화면 UI
씬에서 플레이어를 위한 UI를 만드는 방법을 알아보세요. 예를 들어 게임 관련 정보를 표시하는 데 유용합니다.
씬을 위한 UI를 만들 수 있으며, 3D 월드 공간이 아니라 화면의 고정된 2D 공간에 표시됩니다.
UI 요소는 플레이어가 씬의 LAND 구역 안에 서 있을 때만 보입니다. 인접한 씬이 자체 UI를 표시할 수도 있기 때문입니다. UI의 일부는 월드 공간에서 특정 이벤트가 발생할 때 열리도록 트리거할 수도 있습니다. 예를 들어 플레이어가 특정 지점을 클릭하는 경우입니다.
중첩된 구조를 정의하여 UI를 만듭니다 UiEntity 객체를 JSX로 정의합니다. UI에 사용되는 문법은 다음과 매우 유사합니다 React (웹 UI를 만들기 위한 매우 인기 있는 JavaScript 기반 라이브러리입니다).
📔 참고: UI 문법은 다음이 있는 파일에서만 정의할 수 있습니다 .tsx 확장자. .tsx 파일은 다음이 지원하는 모든 것을 지원하며 .ts 파일은 UI 문법도 지원합니다. 다음을 만드는 것을 권장합니다 ui.tsx 파일을 만들고 그곳에 UI를 정의하세요. UI 렌더 메서드를 다음에서 호출하는 것을 잊지 마세요 index.ts 와 함께 ReactEcsRenderer.setUiRenderer(yourUiMethodName), 아래 예제를 참조하세요.
정적인 요소로만 이루어진 간단한 UI는 HTML과 매우 비슷해 보일 수 있지만, 상태 변화에 반응하는 동적 요소를 추가하면 훨씬 더 강력한 작업을 할 수 있습니다.
기본 Decentraland 익스플로러 UI에는 채팅 위젯, 지도, 기타 요소가 포함됩니다. 이러한 UI 요소는 항상 최상위 레이어에 표시되며, 씬별 UI보다 위에 있습니다. 따라서 씬에 이들과 같은 화면 공간을 차지하는 UI 요소가 있으면 가려집니다.
참조 UX 가이드라인 UI의 모양과 느낌을 설계하는 방법에 대한 팁은
플레이어가 아래의 UI 닫기 버튼을 화면 오른쪽 아래에서 클릭하면 모든 UI 요소가 숨겨집니다.
UI 렌더링하기
씬에 UI를 표시하려면 ReactEcsRenderer.setUiRenderer() 함수를 사용하고, 다음에 설명된 유효한 엔티티 구조를 전달하세요 .tsx 파일.
각 엔티티는 HTML과 유사한 노드로 정의되며, 각 컴포넌트에 대한 속성을 가집니다.
ui.tsx 파일:
import { UiEntity, ReactEcs } from '@dcl/sdk/react-ecs'
import { Color4 } from '@dcl/sdk/math'
export const uiMenu = () => (
<UiEntity
uiTransform={{
width: 700,
height: 400,
margin: { top: '35px', left: '500px' },
}}
uiBackground={{ color: Color4.Red() }}
/>
)index.ts 파일:
엔티티 구조를 정의하고 렌더링하는 작업을 하나의 명령으로도 할 수 있습니다 .tsx 파일.
ui.tsx 파일:
index.ts 파일:
📔 참고: 모든 UI 요소는 같은 구조 안에 중첩되어야 하며, 구조의 루트에는 단 하나의 부모가 있어야 합니다. 다음을 한 번만 호출할 수 있습니다 ReactEcsRenderer.setUiRenderer() 씬에서.
UI 엔티티
UI의 각 요소는 별도의 UiEntity로 정의되어야 하며, 이미지, 텍스트, 배경, 보이지 않는 정렬 박스 등 어떤 것이든 가능합니다. 씬의 3D 공간과 마찬가지로 각 UiEntity 은 위치, 색상 등을 부여하는 자체 컴포넌트를 가집니다.
React와 유사한 문법을 사용하면 각 컴포넌트를 UiEntity내의 속성으로 지정할 수 있어 코드가 더 짧고 읽기 쉬워집니다.
에서 사용되는 컴포넌트는 UiEntity 일반 엔티티에서 사용되는 것과 다릅니다. UI 컴포넌트를 일반 엔티티에 적용할 수도 없고, 일반 컴포넌트를 UI 엔티티에 적용할 수도 없습니다.
다음 컴포넌트를 UiEntity:
uiTransformuiBackgrounduiTextonMouseDownonMouseUponMouseEnteronMouseLeaveuiInputBinding— 요소가 눌려 있는 동안 입력 동작을 유지하여 사용자 지정 컨트롤을 만듭니다
HTML 태그처럼, 컴포넌트를 자기 자신만 닫는 형태로 정의하거나 하나를 다른 것 안에 중첩할 수 있습니다.
경고: 전체 화면 레이아웃 래퍼에는 포인터 핸들러를 두지 마세요. 다음 중 하나라도 추가하면 onMouse 핸들러나 pointerFilter: 'block'를 UiEntity 크기로 100% 만들면 화면의 모든 클릭을 가로채게 되어, 다른 모든 UI와 3D 월드 전체를 클릭할 수 없게 됩니다. 대신 필요한 패널이나 버튼에 핸들러를 두세요. 다음을 참조하세요 100% 포인터 차단 // 부모 엔티티.
ui.tsx 파일:
index.ts 파일:
UI 모듈의 정의에는 부모 레벨 엔티티가 하나만 있을 수 있습니다. 원하는 만큼 다른 엔티티를 정의할 수 있지만, 모두 최상단에 단 하나의 부모를 가진 구조 안에 들어가야 합니다.
화면 가상 스케일
UI의 가상 너비와 높이를 설정하세요. 이렇게 하면 실제 픽셀 화면 크기와 상관없이 다양한 화면 크기에서 UI가 동일하게 보입니다.
가상 너비를 1920, 가상 높이를 1080으로 설정하면 UI는 화면 크기에 맞게 스케일됩니다. 화면이 1920x1080이면 UI는 가상 크기와 같은 크기로 표시됩니다. 화면이 더 크거나 작으면 픽셀 값은 모두 가상 크기에 맞도록 스케일됩니다. 예를 들어 화면이 3840x2160이라면 너비가 100픽셀로 정의된 항목은 실제 200픽셀로 표시됩니다.
픽셀 값에 곱해지는 UI 스케일 팩터의 실제 계산식은 Math.min(realWidth / virtualWidth, realHeight / virtualHeight).
기본 가상 크기
가상 크기는 선택 사항이지만 두 차원은 함께 사용해야 합니다: 둘 다 전달하거나 둘 다 전달하지 않아야 합니다. 하나도 전달하지 않으면 플랫폼 기본값이 적용됩니다:
모바일
1600x720
데스크톱 및 웹
1920x1080
즉, 옵션을 전혀 전달하지 않아도 UI의 픽셀 값은 항상 기준 해상도에 대해 스케일됩니다. 이는 씬이 다음만 사용하는 경우에도 적용됩니다 addUiRenderer() 를 호출하지 않고 setUiRenderer().
세 가지 특별한 경우:
스케일링 제외하기: 유효하지 않은 크기 —
0이하인 값이라면 무엇이든 사용하세요 — 를 전달하면 가상 화면이 완전히 비활성화됩니다. 그러면 픽셀 값은 스케일링 없이 원시 캔버스 픽셀로 사용됩니다. 이것이 가상 화면을 끄는 문서화된 방법이므로 아무 것도 기록되지 않습니다.불완전한 크기: 두 차원 중 하나만 전달하는 것도 유효하지 않으며, 위의 경우와 마찬가지로 가상 화면이 비활성화됩니다. 플랫폼 기본값이 적용되지 않습니다. 위의 제외 사례와 달리 이것은 보고됩니다. 절반만 전달된 크기는 의도적인 선택이 아니라 실수이므로 SDK가 다음을 기록합니다
불완전한 가상 화면 크기(…): 두 차원이 모두 필요하므로 가상 화면이 비활성화되고 UI 스케일링이 적용되지 않습니다.각 크기당 한 번. 두 차원을 모두 전달하거나, 둘 다 전달하지 마세요.주 렌더러에서는 꼭 다시 확인할 가치가 있습니다. 거기서 불완전한 크기도 씬 전체의 전환이기 때문입니다.
setUiRenderer()라고 언급하는 어느 한 차원이든addUiRenderer()보다 우선합니다. 따라서 위의 예시는 씬 전체에 대해 가상 화면을 비활성화하고, 추가 렌더러에 전달된 유효한 크기를 무시합니다.모바일의 16:9 크기: 휴대폰 화면은 16:9보다 훨씬 넓기 때문에 16:9 가상 캔버스는 UI에 레터박스를 넣게 됩니다. 16:9 크기(
1920x1080,1280x720, …)를 전달하고 씬이 모바일에서 실행되면, 이는1600x720로 재정의되며, 콘솔에 한 번 메시지가 기록됩니다. 모바일에서 16:9가 아닌 크기와 데스크톱 또는 웹에서의 모든 유효한 크기는 그대로 적용됩니다.
— 로드 시 UI가 잠깐 다시 스케일되는 것이 보일 수 있고, 씬이 시작된 지 잠시 후 콘솔에 16:9 재정의 메시지가 나타납니다. 첫 프레임부터 하나의 안정적인 기준 해상도를 원한다면 명시적으로 16:9가 아닌 가상 크기를 전달하세요.
세 가지 변경 사항이 기존 씬에 영향을 줍니다. 이들 중 어느 것도 선택 사항이 아니므로, 전혀 수정하지 않은 씬도 SDK 버전을 업데이트하면 보기 달라집니다:
가상 화면이 이제 기본적으로 적용됩니다. 옵션을 전달하지 않던 씬은 예전에는 픽셀 값을 원시 캔버스 픽셀로 배치했습니다. 이제는
1920x1080(1600x720에 맞춰 스케일됩니다(모바일에서는). 이전 동작으로 되돌리려면 가상 화면을 명시적으로 다음으로 비활성화하세요setUiRenderer(ui, { virtualWidth: 0, virtualHeight: 0 }).screenInset기본값은'device'. UI가 이제 기기 안전 영역 안에 배치되므로 모바일에서는 안쪽으로 이동하며, 루트 레벨의100%는 더 이상 전체 화면이 아닙니다 — 전체 화면 배경, 딤머 및 오버레이는 노치와 홈 인디케이터에서 멈춥니다. 이전 동작으로 되돌리려면setUiRenderer(ui, { screenInset: 'none' })을 전달하세요. 다음을 참조하세요 화면 인셋 영역.devicePixelRatio는 UI 레이아웃에 관여하지 않습니다. 픽셀 크기 UI는 이제 고밀도(레티나 및 모바일) 화면에서 이전보다 최대 2~3배 더 크게 표시됩니다. 이것에는 제외 옵션이 없습니다 — 수동으로 조정했던 크기를 다시 확인하고, 씬이 자체적으로 계산하던 스케일 팩터는 제거하세요.
화면 인셋 영역
화면은 완전히 사용할 수 있는 상태가 아닙니다: 휴대폰은 노치, 상태 표시줄, 홈 인디케이터 및 둥근 모서리를 위해 공간을 예약하고, 모든 익스플로러는 캔버스 일부 위에 자체 HUD(미니맵, 채팅, …)를 그립니다. 렌더러 옵션의 선택적 screenInset 속성은 UI가 배치될 화면 영역을 선택합니다:
'device' (기본값)
노치, 상태 표시줄 및 둥근 모서리를 제외한 기기 안전 영역입니다. 다음에서 읽습니다 UiCanvasInformation.screenInsetArea.
'interactable'
익스플로러가 자체 HUD(미니맵, 채팅, …)가 없는 것으로 보고하는 영역입니다. 다음에서 읽습니다 UiCanvasInformation.interactableArea. 무엇을 포함하는지는 각 익스플로러에 따라 다릅니다 — 대상 플랫폼에서 확인하세요.
'none'
전체 화면이며, 0,0 가 왼쪽 위 모서리에 있습니다.
데스크톱에서는 기기 인셋이 0이므로 'device' 그곳에서는 UI를 전체 화면에 배치합니다 — 다음과 같은 결과입니다 'none'. 영역은 매 틱마다 다시 읽히므로, 회전하거나 시스템 바가 나타났다 사라질 때처럼 인셋이 변경되면 UI도 이를 따라갑니다.
📔 UI를 왼쪽 위에 고정하지 마세요. 그 모서리와 왼쪽 전체 가장자리는 익스플로러가 미니맵, 채팅, 그리고 모바일에서는 가상 조이스틱을 그리는 곳입니다. 그곳에 배치된 씬 UI는 렌더되지만, 클라이언트의 자체 UI가 그 위에 그려지므로 가려지고 버튼을 클릭할 수 없는 경우가 많습니다. UI는 오른쪽에 고정하거나 중앙에 두거나, 또는 screenInset: 'interactable' 을 사용하여 익스플로러가 자체 HUD를 피해서 배치하도록 하세요. 다음을 참조하세요 UI를 절대 왼쪽 위에 고정하지 마세요.
📔 'interactable' 는 데스크톱에서 무의미한 동작이 아닙니다. 기기 인셋과 달리 interactable 영역은 적용되지 데스크톱 클라이언트에서는 0입니다. 자체 UI를 위해 화면의 왼쪽 약 25%를 예약하므로 screenInset: 'interactable' 그곳에서 남은 75%에 UI를 배치합니다. 그것이 이 옵션의 목적이지만, 데스크톱 레이아웃도 바뀐다는 뜻이기도 합니다 — 다음으로 분기하세요 isMobile() 휴대폰에서만 적용하려면.
클라이언트 지원: 'interactable' 영역을 보고하는 익스플로러가 필요합니다. 데스크톱에서는 지원되며, 모바일에서는 클라이언트 버전 1.12.1 부터 지원됩니다 — 더 오래된 모바일 클라이언트에서는 값이 0으로 보고되고, UI는 전체 화면을 덮는 것으로 되돌아갑니다. 같은 1.12.1 릴리스에서는 또한 'device' 영역을 Android와 iOS 사이에서 정규화하므로, 어느 인셋이든 의존하는 레이아웃의 하한선으로 취급하세요. 'none' 어디서나 동일하게 동작합니다.
📔 참고: UI를 ScreenInsetArea 또는 InteractableArea 컴포넌트로 감싸는 동시에 일치하는 screenInset 값을 렌더러에 남겨 두지 마세요 — 인셋이 두 번 적용되어 UI가 여백의 두 배만큼 안쪽으로 밀립니다. 대신 다음에 의존하거나 screenInset, 또는 이를 'none' 으로 설정하고 래퍼를 직접 배치하세요.
실제 기기에서의 세 가지 영역
아래 캡처는 같은 휴대폰에서 같은 씬을, 오직 screenInset 값만 바꿔 세 번 렌더링한 결과입니다. 자홍색 사각형은 UI 자체이고, 윤곽선은 익스플로러가 보고하는 영역입니다 — 청록색은 전체 캔버스, 호박색은 screenInsetArea, 녹색은 interactableArea.

screenInset: 'none' — UI가 전체 캔버스를 덮습니다. 모서리는 노치 아래와 클라이언트 자체 컨트롤 뒤에 있습니다.
screenInset: 'device' (기본값) — UI가 기기 안전 영역 안으로 들어가 노치와 둥근 모서리에서 벗어납니다. 여전히 클라이언트의 컨트롤과 겹치는데, 이는 이 영역의 일부가 아닙니다.
screenInset: 'interactable' — UI가 클라이언트가 씬 UI를 위해 지정한 사각형 안에 배치됩니다. 캡처된 모바일 클라이언트 1.12.1에서는 왼쪽 열(프로필, 채팅, 조이스틱)이 제외되며, 오른쪽 아래의 액션 버튼은 설계상 해당 영역 위에 그려집니다.각 렌더러는 자체 screenInset를 존중하므로, 메인 UI와 addUiRenderer() 로 추가된 어떤 UI도 서로 다른 영역을 동시에 사용할 수 있습니다. 가상 크기와 달리 이 값은 렌더러 간에 공유되지 않습니다.
여러 UI 모듈
씬에 각자 UI를 정의하는 여러 시스템이나 모듈이 있다면, 각 UI 모듈을 ReactEcsRenderer.addUiRenderer()로 렌더링할 수 있습니다. 이는 여러 UI 컴포넌트를 가진 복잡한 씬을 작업할 때 특히 유용하며, 스마트 아이템를 정의할 때도 유용합니다. 이는 씬의 나머지 코드와 무관하게 사용 가능해야 합니다.
해당 ReactEcsRenderer.addUiRenderer() 함수는 UI의 소유자로 엔티티를 제공해야 합니다. 이는 어떤 엔티티든 될 수 있으며, UI의 소유자로 사용하기 위해서만 만든 더미 엔티티도 가능합니다.
이 코드 조각은 씬의 다른 UI 코드와 독립적으로 존재할 수 있습니다. 씬의 나머지 부분에는 ReactEcsRenderer.setUiRenderer()가 포함될 수도 있고 전혀 없을 수도 있지만, UI는 여전히 렌더링됩니다.
하나의 addUiRenderer() 호출도 다음과 마찬가지로 가상 너비와 높이를 포함할 수 있습니다 setUiRenderer(). 그러나 씬에 가상 너비와 높이를 정의하는 setUiRenderer() 호출도 있다면, addUiRenderer() 호출의 가상 너비와 높이는 무시됩니다.
가상 크기는 씬 전체의 단일 값이며, 다음과 같이 해결됩니다: setUiRenderer() 의 크기가 우선하고, 그렇지 않으면 처음으로 addUiRenderer() 를 호출해 하나를 제공한 쪽이 우선하며, 어떤 렌더러도 제공하지 않았다면 플랫폼 기본값 이 적용됩니다. 오직 screenInset 만 포함하는 옵션은 제공된 크기로 간주되지 않습니다.
해당 screenInset 는 반대로 동작합니다 — 렌더러별이므로 각 UI 모듈은 서로 다른 화면 영역에 배치될 수 있습니다:
그 UI는 다음으로 제거할 수 있습니다 ReactEcsRenderer.removeUiRenderer(dummyEntity) , 또한 UI를 소유한 엔티티가 파괴되면 UI도 함께 제거됩니다. 동일한 엔티티에 대해 ReactEcsRenderer.addUiRenderer() 가 다른 UiRenderer로 다시 호출되면 이전 것은 정리되고 새 것이 이를 대체하며, 스태킹 순서에서의 위치는 유지됩니다. 다시 호출하는 것은 이미 렌더링된 UI 모듈의 옵션을 변경하는 방법이기도 합니다. 예를 들어 zIndex.
UI 모듈 간 스태킹 순서
기본적으로 UI 모듈은 처음 나타나는 순서대로 쌓이며, 나중에 나타난 것이 앞에 옵니다. 같은 프레임에 처음 나타난 모듈 — 일반적으로 씬의 첫 프레임 전에 등록된 모든 것 — 은 setUiRenderer() 의 메인 UI와 함께 뒤쪽에 그려지고, 그 뒤에 추가된 순서대로 다른 모듈이 이어집니다. 이를 명시적으로 제어하려면 렌더러 옵션에 zIndex 를 전달하세요: 더 높은 zIndex 를 가진 모듈이 낮은 값을 가진 모듈보다 앞에 렌더링됩니다. 나타난 순서와 무관합니다. 값을 0 에 둔 모듈은 setUiRenderer()기본 순서를 유지합니다. 이 옵션은
이것은 다음과는 다른 설정입니다 zIndex 의 UiEntity로, 오직 UI 모듈 내부의 형제 요소만 정렬합니다. 렌더러 옵션은 전체 모듈끼리의 순서를 정하고, 각 모듈 내부의 요소는 자체 zIndex.
하나의 setUiRenderer 문을 공유하기
각 UI 모듈마다 ReactEcsRenderer.addUiRenderer() 를 호출하는 대신, 다음을 호출할 수 있습니다 ReactEcsRenderer.setUiRenderer() 서로 다른 파일에 있을 수 있는 UI 모듈 배열과 함께 한 번.
아래는 더 완전한 예시입니다:
ui.tsx 파일:
index.ts 파일:
마지막 업데이트