일반적으로 UI를 구성할 때는 게임의 3D 씬과는 별도의 2D 공간에서 버튼과 같은 인터페이스 요소를 렌더링하고, 이 위에 클릭 이벤트를 처리할 수 있어야 한다. Unity에서 EventSystem과 InputModule이 이런 역할을 해주는데, 여기서는 직접 구현해본다.
핵심 구현 요소는 다음과 같다:
버튼 기능은 UI이므로 Component를 상속하는 방식으로 설계한다. 기본 구조는 다음과 같다.
class Button : public Component
{
public:
Button();
virtual ~Button();
bool Picked(POINT screenPos); // 마우스 좌표가 버튼 안에 있는지 판별
void Create(Vec2 screenPos, Vec2 size, shared_ptr<Material> material); // 버튼 생성
void AddOnClickedEvent(std::function<void(void)> func); // 클릭 이벤트 등록
void InvokeOnClicked(); // 클릭 이벤트 발생 시 호출
private:
std::function<void(void)> _onClicked; // 등록된 콜백
RECT _rect; // 버튼의 실제 클릭 가능한 영역
};
Create: 버튼 생성 시 화면 좌표를 월드 좌표로 변환해서 버튼 위치 지정 + 크기 설정 + 렌더러 구성_rect: 화면 좌표 기준으로 실제 마우스 클릭 시 피킹 여부를 판단하는 영역Picked: WinAPI의 PtInRect()를 사용해 클릭 여부 판별AddOnClickedEvent: 클릭 시 호출될 사용자 정의 콜백 함수 등록InvokeOnClicked: 클릭된 경우 콜백 호출버튼이 클릭되었는지를 판단하고 해당 버튼의 콜백을 실행시킬 위치가 필요하다. 이 로직은 Scene::PickUI()로 분리한다.
void Scene::PickUI()
{
if (INPUT->GetButtonDown(KEY_TYPE::LBUTTON) == false)
return;
if (GetUICamera() == nullptr)
return;
POINT screenPt = INPUT->GetMousePos();
for (auto& gameObject : GetObjects())
{
if (gameObject->GetButton() == nullptr)
continue;
if (gameObject->GetButton()->Picked(screenPt))
gameObject->GetButton()->InvokeOnClicked();
}
}
⚠ Scene::Update() 내부에서 매 프레임마다 호출해주는 방식으로 항상 체크되게 한다.
버튼이 클릭되었을 때 수행할 작업은 다음처럼 람다 함수를 AddOnClickedEvent()에 등록하면 된다.
obj->GetButton()->AddOnClickedEvent([obj]() {
CUR_SCENE->Remove(obj); // 버튼 클릭 시 자기 자신 제거
});
⚠ 람다 사용 시 주의사항
UI는 3D 씬과는 별도로 항상 고정된 크기로 보여야 하므로, Orthographic 카메라를 별도로 세팅한다.
auto camera = make_shared<GameObject>();
camera->AddComponent(make_shared<Camera>());
camera->GetCamera()->SetProjectionType(ProjectionType::Orthographic);
camera->GetCamera()->SetCullingMaskAll(); // 모든 걸 안 보이게 한 뒤
camera->GetCamera()->SetCullingMaskLayerOnOff(Layer_UI, false); // UI만 보이게
버튼을 추가하고 클릭했을 때 해당 오브젝트가 사라지는 예제를 구현한다.
auto obj = make_shared<GameObject>();
obj->AddComponent(make_shared<Button>());
obj->GetButton()->Create(Vec2(100, 100), Vec2(100, 100), RESOURCES->Get<Material>(L"UnityLogo"));
obj->GetButton()->AddOnClickedEvent([obj]() {
CUR_SCENE->Remove(obj);
});
CUR_SCENE->Add(obj);
✅ 클릭 시 등록된 콜백이 호출되어 오브젝트가 삭제됨을 확인 가능.