문제 상황
Nez 엔진에서 카메라를 이동시킨 후, UI 요소의 클릭 판정이 정상적으로 작동하지 않는 문제가 발생할 수 있습니다. 예를 들어, 화면의 특정 위치에 버튼이 있을 때, 버튼의 시각적 위치는 올바르게 보이지만, 실제 클릭 이벤트가 발생하는 위치가 카메라 이동 후 왜곡되는 현상을 겪을 수 있습니다.
아래 코드는 이 문제를 보여주는 간단한 예시입니다. 버튼은 생성되지만, 카메라 위치가 변경되면 판정 영역이 정상적으로 업데이트되지 않습니다.
public class ProblematicScene : Scene
{
public override void Initialize()
{
base.Initialize();
// UI 엔티티 생성
var uiEntity = CreateEntity("uiElement");
var canvasComponent = uiEntity.AddComponent<UICanvas>();
// 버튼 추가 및 크기 설정
canvasComponent.Stage.AddElement(new Button(ButtonStyle.Create(Color.White, Color.White, Color.Yellow))).SetSize(100, 100);
// 카메라 위치 이동
Camera.Position = new Vector2(50, 50);
}
}
해결 방안
이 문제를 해결하려면, UI 렌더링을 화면 공간(Screen Space)에서 처리해야 합니다. Nez의 IFinalRenderDelegate 인터페이스를 구현하여 최종 렌더링 단계에서 UI를 별도로 렌더링하는 것이 핵심입니다. 이를 통해 카메라의 월드 공간(World Space) 위치 변화에 영향을 받지 않고 UI가 항상 화면의 고정된 위치에 렌더링됩니다.
아래 코드는 IFinalRenderDelegate를 구현하여 문제를 해결한 예시입니다.
public class FixedScene : Scene, IFinalRenderDelegate
{
// UI 렌더링을 위한 고정 렌더 레이어
private const int UI_RENDER_LAYER = 999;
private ScreenSpaceRenderer _uiRenderer;
public FixedScene()
{
// ScreenSpaceRenderer 인스턴스 생성 및 최종 렌더링 델리게이트 설정
_uiRenderer = new ScreenSpaceRenderer(100, UI_RENDER_LAYER);
FinalRenderDelegate = this;
}
public override void Initialize()
{
base.Initialize();
// UI 엔티티 생성
var uiEntity = CreateEntity("uiElement");
var canvasComponent = uiEntity.AddComponent<UICanvas>();
// 버튼 추가 및 크기 설정
canvasComponent.Stage.AddElement(new Button(ButtonStyle.Create(Color.White, Color.White, Color.Yellow))).SetSize(100, 100);
// UI 캔버스의 렌더 레이어 설정
canvasComponent.RenderLayer = UI_RENDER_LAYER;
// 카메라 위치 이동
Camera.Position = new Vector2(50, 50);
}
#region IFinalRenderDelegate impls
private Scene _scene;
public void HandleFinalRender(RenderTarget2D finalRenderTarget, Color letterboxColor, RenderTarget2D source,
Rectangle finalRenderDestinationRect, SamplerState samplerState)
{
Nez.Core.GraphicsDevice.SetRenderTarget(null);
Nez.Core.GraphicsDevice.Clear(letterboxColor);
Graphics.Instance.Batcher.Begin(BlendState.Opaque, samplerState, DepthStencilState.None, RasterizerState.CullNone, null);
Graphics.Instance.Batcher.Draw(source, finalRenderDestinationRect, Color.White);
Graphics.Instance.Batcher.End();
_uiRenderer.Render(_scene);
}
public void OnAddedToScene(Scene scene)
{
_scene = scene;
}
public void OnSceneBackBufferSizeChanged(int newWidth, int newHeight)
{
_uiRenderer.OnSceneBackBufferSizeChanged(newWidth, newHeight);
}
#endregion
}