이번에는 만들어준 직교투영 카메라를 활용하여 버튼을 만들고 기능까지 붙여주자. 지금은 레이케스팅을 할 때, 원근 투영된 뷰포트에 대한 값을 찾아주고 있는데
이를 직교투영된 방식으로 해준다고하면 충돌체크는 OBB를 통해 해주면 될 것이고 방향을 잘 맞춰서 레이케스팅을 해주면 충돌이 이루어지면서 클릭이 될 것이다.
Unity에서는 UI를 생성하면 기본적으로 EventSystem이라는 오브젝트가 자동으로 생성된다. 이 EventSystem을 통해
피격 판정이 이루어졌는지 체크한다.
우리는 클릭한 스크린 좌표를 가져와서 클릭한 좌표에 이 버튼이 포함되는지 확인해주는 방식으로 구현해보자.
우선 버튼클래스를 만들어주고 버튼을 눌렀을 때 콜백함수가 호출되도록 만들어주자. 이때 콜백함수는 Std의 Function 변수를 사용해주자.
이 변수를 사용할 때 주의해야할 점이 있는데 만약 연결되는 콜백함수가 static이나 전역이면 문제가 없지만
람다나 멤버함수, 람다캡쳐를 통해 전달해주게 된다면 문제가 생길 수 있다는 것이다.
이때 람다캡처는 외부에 있던 함수를 복사해서 넣어줄 수 있는 것인데 이때 포인터가 들어가면 포인터가 외부에서 해제된 경우 없어진 주소를 참조하는 것이기 때문에 문제가 생길 수 있다.
스마트 포인터를 쓴다고 하면 참조가 계속되어서 릴리즈가 안되는 문제가 생길 수 있다.
Button.h
#pragma once
#include "Component.h"
class Button : public Component
{
using Super = Component;
public:
Button();
virtual ~Button();
bool Picked(POINT screenPos);
void Create(Vec2 screenPos, Vec2 size, shared_ptr<class Material> material);
void AddOnClickedEvent(std::function<void(void)> func);
void InvokeOnClicked();
private:
std::function<void(void)> _onClicked;
RECT _rect;
};
Button.cpp
#include "pch.h"
#include "Button.h"
#include "MeshRenderer.h"
#include "Material.h"
Button::Button() : Super(ComponentType::Button)
{
}
Button::~Button()
{
}
bool Button::Picked(POINT screenPos)
{
//해당영역에 들어가 있는지 확인해줌
return ::PtInRect(&_rect, screenPos);
}
void Button::Create(Vec2 screenPos, Vec2 size, shared_ptr<class Material> material)
{
auto go = _gameObject.lock();
float height = GRAPHICS->GetViewport().GetHeight();
float width = GRAPHICS->GetViewport().GetWidth();
//실제 배치할 좌표구하기
float x = screenPos.x - width / 2;
float y = height / 2 - screenPos.y;
Vec3 position = Vec3(x, y, 0);
go->GetOrAddTransform()->SetPosition(position);
go->GetOrAddTransform()->SetScale(Vec3(size.x, size.y, 1));
go->SetLayerIndex(Layer_UI);
if (go->GetMeshRenderer() == nullptr)
go->AddComponent(make_shared<MeshRenderer>());
go->GetMeshRenderer()->SetMaterial(material);
auto mesh = RESOURCES->Get<Mesh>(L"Quad");
go->GetMeshRenderer()->SetMesh(mesh);
go->GetMeshRenderer()->SetPass(0);
//Picking
//사각형 상하좌우 실제 영역구해주기
_rect.left = screenPos.x - size.x / 2;
_rect.right = screenPos.x + size.x / 2;
_rect.top = screenPos.y - size.y / 2;
_rect.bottom = screenPos.y + size.y / 2;
}
void Button::AddOnClickedEvent(std::function<void(void)> func)
{
_onClicked = func;
}
void Button::InvokeOnClicked()
{
if (_onClicked)
_onClicked();
}
UI를 피킹하는 코드를 Scene클래스에 추가해주자.
Scene.cpp
void Scene::PickUI()
{
if (INPUT->GetButtonDown(KEY_TYPE::LBUTTON) == false)
return;
if (GetUICamera() == nullptr)
return;
//누른 좌표가져오기
POINT screenPt = INPUT->GetMousePos();
shared_ptr<Camera> camera = GetUICamera()->GetCamera();
const auto gameObjects = GetObjects();
for (auto& gameObject : gameObjects)
{
if (gameObject->GetButton() == nullptr)
continue;
if (gameObject->GetButton()->Picked(screenPt))
gameObject->GetButton()->InvokeOnClicked();
}
}
이렇게 해주고 실제로 테스트해보기 위해 메인코드를 만들어주자. 아까 위에서 봤던 문제에서 핵심은 람다캡처에서
메모리 문제가 생길 수 있기 때문에 주의해서 사용하거나 전역이나 static함수를 사용해주자.
람다에서 캡처는 fuctor에서 주고자하는 값을 멤버변수로 들고있는 것이라고 보면 된다.
ButtonDemo.cpp
#include "pch.h"
#include "ButtonDemo.h"
#include "RawBuffer.h"
#include "TextureBuffer.h"
#include "Material.h"
#include "GeometryHelper.h"
#include "Camera.h"
#include "GameObject.h"
#include "CameraScript.h"
#include "MeshRenderer.h"
#include "Mesh.h"
#include "Material.h"
#include "Model.h"
#include "ModelRenderer.h"
#include "ModelAnimator.h"
#include "Mesh.h"
#include "Transform.h"
#include "VertexBuffer.h"
#include "IndexBuffer.h"
#include "Light.h"
#include "Graphics.h"
#include "SphereCollider.h"
#include "Scene.h"
#include "AABBBoxCollider.h"
#include "OBBBoxCollider.h"
#include "Terrain.h"
#include "Camera.h"
#include "Button.h"
void ButtonDemo::Init()
{
_shader = make_shared<Shader>(L"23. RenderDemo.fx");
// Camera
{
auto camera = make_shared<GameObject>();
camera->GetOrAddTransform()->SetPosition(Vec3{ 0.f, 0.f, -5.f });
camera->AddComponent(make_shared<Camera>());
camera->AddComponent(make_shared<CameraScript>());
camera->GetCamera()->SetCullingMaskLayerOnOff(Layer_UI, true);
CUR_SCENE->Add(camera);
}
//UI_Camera
{
auto camera = make_shared<GameObject>();
camera->GetOrAddTransform()->SetPosition(Vec3{ 0.f, 0.f, -5.f });
camera->AddComponent(make_shared<Camera>());
camera->GetCamera()->SetProjectionType(ProjectionType::Orthographic);
camera->GetCamera()->SetNear(1.f);
camera->GetCamera()->SetFar(100.f);
//UI만 그려주게
camera->GetCamera()->SetCullingMaskAll();
camera->GetCamera()->SetCullingMaskLayerOnOff(Layer_UI, false);
CUR_SCENE->Add(camera);
}
// Light
{
auto light = make_shared<GameObject>();
light->AddComponent(make_shared<Light>());
LightDesc lightDesc;
lightDesc.ambient = Vec4(0.4f);
lightDesc.diffuse = Vec4(1.f);
lightDesc.specular = Vec4(0.1f);
lightDesc.direction = Vec3(1.f, 0.f, 1.f);
light->GetLight()->SetLightDesc(lightDesc);
CUR_SCENE->Add(light);
}
// Material
{
shared_ptr<Material> material = make_shared<Material>();
material->SetShader(_shader);
auto texture = RESOURCES->Load<Texture>(L"Veigar", L"..\\Resources\\Textures\\veigar.jpg");
material->SetDiffuseMap(texture);
MaterialDesc& desc = material->GetMaterialDesc();
desc.ambient = Vec4(1.f);
desc.diffuse = Vec4(1.f);
desc.specular = Vec4(1.f);
RESOURCES->Add(L"Veigar", material);
}
// Mesh - 버튼 역활
{
auto obj = make_shared<GameObject>();
obj->AddComponent(make_shared<Button>());
obj->GetButton()->Create(Vec2(100, 100), Vec2(100, 100), (RESOURCES->Get<Material>(L"Veigar")));
obj->GetButton()->AddOnClickedEvent([obj]() { CUR_SCENE->Remove(obj); });
CUR_SCENE->Add(obj);
}
// Mesh
{
auto obj = make_shared<GameObject>();
obj->GetOrAddTransform()->SetLocalPosition(Vec3(0.f));
obj->GetOrAddTransform()->SetScale(Vec3(2.f));
obj->AddComponent(make_shared<MeshRenderer>());
{
obj->GetMeshRenderer()->SetMaterial(RESOURCES->Get<Material>(L"Veigar"));
}
{
auto mesh = RESOURCES->Get<Mesh>(L"Sphere");
obj->GetMeshRenderer()->SetMesh(mesh);
obj->GetMeshRenderer()->SetPass(0);
}
CUR_SCENE->Add(obj);
}
}
void ButtonDemo::Update()
{
}
void ButtonDemo::Render()
{
}
이렇게 람다 캡처를 사용해서 콜백함수를 만들어준 다음 실행시켜보면 버튼이 잘 없어지는 것을 볼 수 있다.
'게임공부 > Directx11' 카테고리의 다른 글
[Directx11][C++][3D]36. 빌보드2 (1) | 2024.10.13 |
---|---|
[Directx11][C++][3D]35. 빌보드1 (1) | 2024.10.12 |
[Directx11][C++][3D]33. 직교투영 (0) | 2024.10.10 |
[Directx11][C++][3D]32. 수학3(Triangle) (4) | 2024.10.09 |
[Directx11][C++][3D]31. 수학2(Intersection & RayCasting) (1) | 2024.10.09 |