| 일 | 월 | 화 | 수 | 목 | 금 | 토 |
|---|---|---|---|---|---|---|
| 1 | 2 | 3 | 4 | 5 | ||
| 6 | 7 | 8 | 9 | 10 | 11 | 12 |
| 13 | 14 | 15 | 16 | 17 | 18 | 19 |
| 20 | 21 | 22 | 23 | 24 | 25 | 26 |
| 27 | 28 | 29 | 30 |
- 3D Animation
- 이동방식
- 유니티 애니메이션
- 소울라이크
- 오블완
- typeof
- sendcustomnetworkevent
- blendtree
- VolumetricFog
- navmashagent
- VRChat
- 티스토리챌린지
- unity animation
- 유니티
- mixamo
- pothon
- unity
- HDRP
- udon#
- u#
- Root Motion
- 유니티 네비매쉬
- 3인칭 이동
- Animation
- 브이알챗
- navmash
- 3rd Person
- unity mixamo
- c#
- unityai
- Today
- Total
개발 새발 블로그
[Unity] UI ToolKit Resize,Dragable 본문
Unity 6 · UIElements · UI Toolkit · Digital Twin 개발 중 마주친 문제들과 해결법을 정리합니다.
1. UIElements RightPanel 드래그 리사이즈
문제
flex-direction: row으로
Main(좌측 3D 뷰)과 RightPanel(우측 데이터 패널)을 나란히 배치했는데,
RightPanel 폭이 width: 40%로 고정되어 사용자가 조절할 수 없었습니다.
해결 전략
Main과 RightPanel 사이에 5px 너비의 드래그 핸들 VisualElement를 삽입하고,
C#에서 Pointer 이벤트로 폭을 실시간 조정합니다.
핵심은 width: % →
width: px 고정값으로 전환해 드래그가 즉시 반영되도록 하는 것입니다.
UXML — 핸들 삽입
<!-- Main 닫는 태그와 RightPanel 사이 -->
<ui:VisualElement name="ResizeHandle" class="resize-handle" />
<ui:VisualElement name="RightPanel"
style="min-width: 300px; background-color: rgb(30, 31, 32); flex-shrink: 0;">
USS — 핸들 스타일
.resize-handle {
width: 5px;
background-color: rgb(50, 51, 52);
flex-shrink: 0;
cursor: col-resize;
transition-property: background-color;
transition-duration: 0.1s;
}
.resize-handle:hover,
.resize-handle--dragging {
background-color: rgb(0, 120, 215);
}
C# — 드래그 이벤트
// 초기 width: % → px 고정 (GeometryChangedEvent 이후)
private void OnRightPanelGeometryReady(GeometryChangedEvent evt)
{
if (_rightPanel.resolvedStyle.width > 0)
{
_rightPanel.style.width = _rightPanel.resolvedStyle.width;
_rightPanel.style.flexGrow = 0;
_rightPanel.UnregisterCallback<GeometryChangedEvent>(OnRightPanelGeometryReady);
}
}
private void OnResizePointerDown(PointerDownEvent evt)
{
_isDragging = true;
_dragStartX = evt.position.x;
_dragStartWidth = _rightPanel.resolvedStyle.width;
_resizeHandle.AddToClassList("resize-handle--dragging");
_resizeHandle.CapturePointer(evt.pointerId);
evt.StopPropagation();
}
private void OnResizePointerMove(PointerMoveEvent evt)
{
if (!_isDragging) return;
// 핸들을 왼쪽으로 드래그 → RightPanel 넓어짐
float delta = _dragStartX - evt.position.x;
float newWidth = Mathf.Max(300f, _dragStartWidth + delta);
_rightPanel.style.width = newWidth;
evt.StopPropagation();
}
private void OnResizePointerUp(PointerUpEvent evt)
{
_isDragging = false;
_resizeHandle.RemoveFromClassList("resize-handle--dragging");
_resizeHandle.ReleasePointer(evt.pointerId);
evt.StopPropagation();
}
포인트: CapturePointer를 사용해야 드래그 중 마우스가 핸들 밖으로 나가도 이벤트가 끊기지 않습니다.
2. Camera Frustum Shift — RightPanel 폭에 따라 카메라 중심 이동
문제
RightPanel이 화면 우측 40%를 차지하면 카메라가 화면 정중앙을 바라보기 때문에
3D 뷰의 피사체가 실제 보이는 영역 중심이 아닌 오른쪽으로 치우쳐 보입니다.
기존에는 camera.rect.x를 고정값으로 조정했지만,
RightPanel 폭이 유동적으로 바뀌면 함께 대응해야 합니다.
왜 Projection Matrix인가
| 방법 | 렌더 잘림 | 정석 여부 |
|---|---|---|
| Viewport Rect X 조정 | 좌측 잘림 발생 | 편법 |
| Projection Matrix Frustum Offset | 없음 | 정석 ✓ |
| Physical Camera Lens Shift | 없음 | 내부적으로 동일 방식 |
VR의 비대칭 frustum, 와이드 멀티모니터 렌더링 등에서 표준으로 사용하는 방식입니다.
Projection Matrix의 proj[0,2] 값이
수평 frustum 비대칭 오프셋을 담당합니다.
수식
// 카메라가 바라봐야 할 영역의 중심 (픽셀)
visibleWidth = Screen.width - rightPanelWidth
visibleCenter = visibleWidth / 2
// NDC (-1 ~ 1) 기준 오프셋
offsetNDC = (screenCenter - visibleCenter) / screenCenter
// Projection Matrix에 적용
proj[0,2] = offsetNDC
구현 — CameraFrustumShift.cs
[RequireComponent(typeof(Camera))]
public class CameraFrustumShift : MonoBehaviour
{
public static float RightPanelWidth { get; set; }
private Camera _cam;
void Awake() => _cam = GetComponent<Camera>();
void LateUpdate() => ApplyFrustumShift(RightPanelWidth);
private void ApplyFrustumShift(float rightPanelWidthPx)
{
float visibleCenter = (Screen.width - rightPanelWidthPx) * 0.5f;
float screenCenter = Screen.width * 0.5f;
float offsetNDC = (screenCenter - visibleCenter) / screenCenter;
Matrix4x4 proj = Matrix4x4.Perspective(
_cam.fieldOfView, _cam.aspect,
_cam.nearClipPlane, _cam.farClipPlane);
proj[0, 2] = offsetNDC;
_cam.projectionMatrix = proj;
}
}
MainUIController의 리사이즈 핸들러에서
CameraFrustumShift.RightPanelWidth = newWidth;만 호출하면
LateUpdate에서 자동으로 매트릭스를 갱신합니다.
3. UIDocument + 3D 오브젝트 포인터 이벤트 공존
문제
3D 오브젝트에 IPointerClickHandler를 붙이고
카메라에 PhysicsRaycaster를 추가해도
클릭/호버 이벤트가 전혀 발생하지 않는 문제가 생깁니다.
원인은 UI Toolkit의 PanelRaycaster가 EventSystem의 모든 포인터 이벤트를 먼저 가로채기 때문입니다. PhysicsRaycaster까지 이벤트가 내려오지 못합니다.
해결책 — Physics.Raycast 직접 사용
EventSystem을 우회하고, 중앙 매니저 스크립트의 Update()에서
Physics.Raycast로 직접 처리합니다.
// PolePart.cs — 순수 메서드만 (IPointer 인터페이스 제거)
public class PolePart : MonoBehaviour
{
public void OnEnter() { /* 호버 진입 처리 */ }
public void OnExit() { /* 호버 나감 처리 */ }
public void OnClick() { /* 클릭 처리 */ }
}
// WorldCanvasManager.cs — 중앙 Raycast 루프
public class WorldCanvasManager : MonoBehaviour
{
[SerializeField] private LayerMask targetLayer;
private Camera _cam;
private PolePart _hovered;
void Start() => _cam = Camera.main;
void Update()
{
Ray ray = _cam.ScreenPointToRay(Input.mousePosition);
if (Physics.Raycast(ray, out RaycastHit hit, Mathf.Infinity, targetLayer))
{
var part = hit.collider.GetComponent<PolePart>();
if (part != null && part != _hovered) // Enter
{
_hovered?.OnExit();
_hovered = part;
_hovered.OnEnter();
}
if (part != null && Input.GetMouseButtonDown(0)) // Click
part.OnClick();
}
else if (_hovered != null) // Exit
{
_hovered.OnExit();
_hovered = null;
}
}
}
주의:Physics.Raycast의 세 번째 인자는float maxDistance입니다.
Physics.Raycast(ray, out hit, targetLayer)처럼 쓰면 LayerMask 값(예: 256)이 거리값으로 해석되어 항상 true를 반환합니다.
반드시Physics.Raycast(ray, out hit, Mathf.Infinity, targetLayer)로 작성하세요.
씬 세팅
IPointerClickHandler,PhysicsRaycaster— 제거- 각 3D 파트 →
Collider+PolePart.cs - 씬에 빈 GameObject →
WorldCanvasManager.cs - Inspector에서
targetLayer에 3D 파트 레이어 지정
정리
| 주제 | 핵심 포인트 |
|---|---|
| RightPanel 리사이즈 | 핸들 삽입 + CapturePointer + width px 고정 |
| Camera Frustum Shift | proj[0,2] = offsetNDC — Viewport Rect 잘림 없음 |
| UIDocument + 3D 이벤트 | PanelRaycaster 우회 → Physics.Raycast 직접 처리 |
Unity 6 | UI Toolkit | Digital Twin 개발 노트
'Unity 한테 정복당하기' 카테고리의 다른 글
| Unity Spooky Challenge 후기 (0) | 2025.10.10 |
|---|---|
| Unity 6.2 AI 로 Sound를 제작해보자 (0) | 2025.10.08 |
| [Unity] Rig Error Copied Avatar Rig Configuration mis-match (4) | 2025.07.20 |
| Unity 멀티 Photon PUN (0) | 2025.03.23 |
| UI (0) | 2025.03.17 |