지난 글에서는 탑에서 떨어지면 1층부터 다시 시작하게 만들었습니다. 그런데 지금 몇 층에 있는지는 Console 을 열어야만 보입니다. 게임을 하는 사람은 Console 을 볼 수 없습니다. 완성본처럼 화면 위쪽 가운데에 탑 몇 층인지 글자를 띄우고, 위층에 오르거나 떨어지면 글자도 바로 바뀌게 만들겠습니다.
UI 란
게임 화면 위에 겹쳐 그리는 글자나 버튼을 UI 라고 부릅니다. 맵 안에 놓인 발판과 달리, 카메라가 움직여도 화면의 같은 자리에 있습니다.
그럼 UI 는 서버와 클라이언트 중 어디에 있을까요? UI 는 내 화면에 그리는 것이라 클라이언트에만 있습니다. 그래서 글자를 바꾸는 코드도 클라이언트에서 돌아야 합니다.

다행히 몇 층인지는 클라이언트도 이미 알고 있습니다. 6화에서 floor 를 Sync 로 바꿔 두었기 때문에, 서버가 floor 를 바꾸면 내 클라이언트에도 같은 값이 옵니다. 이번에는 그 값을 읽어서 글자로 보여 주기만 하면 됩니다.
글자의 자리도 정해야 합니다. 왼쪽 위에는 PC 에서 채팅 버튼이 들어가는 자리가 있어서, 여기에 두면 글자가 버튼에 가려질 수 있습니다. 그래서 위쪽 가운데에 둡니다.
UI 편집기, 앵커와 PosY
먼저 Maker 에서 글자 하나를 화면에 올립니다.
- 위쪽 도구 막대에서 UI 버튼을 누르면 가운데 화면이 UI 편집 화면으로 바뀝니다.
- 아래쪽 UI Groups 에서 DefaultGroup 을 고릅니다. ToastGroup 은 잠깐 떴다 사라지는 알림, PopupGroup 은 열고 닫는 팝업 창을 담고, DefaultGroup 은 게임 내내 켜져 있는 그룹입니다. 층 표시는 늘 보여야 하니 DefaultGroup 입니다.
- 왼쪽 도구 줄에서 T 모양의 텍스트를 누르면 캔버스에 글자가 생기고 Hierarchy 에 UIText 가 생깁니다. 이름을 FloorText 로 바꿉니다.
이제 이 글자를 위쪽 가운데로 옮깁니다. Property 의 UITransformComponent 에서 네모 그림을 누르면 Anchor Presets 가 열립니다. 앵커는 화면의 어디를 기준으로 위치를 잴지 정하는 점입니다. 위쪽 가운데를 고릅니다.
그런데 글자는 아직 화면 한가운데에 있고, PosY 가 -540 으로 바뀌어 있습니다. 기준점만 위로 옮기고 글자는 제자리에 두려고 Maker 가 숫자를 바꿔 준 것입니다. UI 화면의 높이가 1080 이니 위쪽 끝에서 540 내려온 곳이 한가운데입니다.
PosY 를 -70 으로 바꿉니다. 위에서 20 만큼 띄우고, 글자 상자 높이 100 의 절반인 50 을 더한 값입니다. 글자가 위쪽 가운데로 올라갑니다. 나머지는 보기 좋게 다듬습니다.
- 글자 칸에 탑 1층 을 넣습니다.
- 가로 크기 300, 글자 크기 48, 글자 색 흰색으로 바꿉니다.
- 밝은 하늘에서도 잘 보이게 UseOutLine 을 켜고 외곽선을 검정으로 합니다.
저장하고 실행하면 화면 위쪽 가운데에 탑 1층 이 나타납니다. 하지만 위층에 올라가도 글자는 계속 탑 1층 입니다. 글자를 바꾸는 코드가 아직 없기 때문입니다.
Logic, ClientOnly
지금까지 만든 코드는 모두 컴포넌트였습니다. FloorBuilder 는 맵에, FloorExit 는 출구마다, TowerClimber 는 플레이어마다 붙였습니다. 컴포넌트는 이렇게 물체에 붙이는 부품입니다.
그런데 내 화면의 층 글자는 하나뿐이라 물체마다 둘 필요가 없습니다. 이럴 때 쓰는 것이 Logic 입니다. Logic 은 어디에도 붙이지 않고, 월드에 딱 하나 있으며, 게임이 끝날 때까지 살아 있습니다.

- MyDesk 를 오른쪽 클릭하고 Create Scripts 에서 Create Logic 을 고릅니다. 이름은 TowerHud 로 합니다. Workspace 에서 컴포넌트는 빨간 아이콘, Logic 은 초록 아이콘입니다.
- TowerHud 를 열고 Property 옆 + 로 새 Property 를 만듭니다. 타입 목록에서 Entity 를 고르고 이름을 floorText 로 바꿉니다. FloorText 라는 글자 물체를 통째로 담으려는 것입니다. 어떤 글자를 쓸지는 Maker 에서 미리 정해 두니 Sync 는 None 입니다.
- 값을 보면 nil 입니다. nil 은 그릇은 있는데 아직 아무것도 담기지 않았다는 뜻입니다. 옆의 동그라미 버튼으로 Reference 창을 열고 FloorText 를 고르면 그릇에 화면의 글자가 담깁니다.

다음은 글자를 바꿀 함수입니다. Method 옆 + 의 Event Method 에서 OnUpdate 를 고릅니다. 7화에서 쓴, 화면이 그려질 때마다 불리는 함수입니다. 그런데 위를 보면 server only 입니다. UI 는 클라이언트에만 있으니 서버에서는 이 글자를 바꿀 수 없습니다. 점 세 개 메뉴의 Execution Space Setting 에서 ClientOnly 로 바꿉니다.
LocalPlayer, floor + 1
이제 OnUpdate 안을 채웁니다.
- _UserService 의 LocalPlayer 로 내 플레이어를 찾습니다. 지금 이 화면을 보고 있는 나의 플레이어입니다.
- 내 플레이어에 붙은 TowerClimber 를 GetComponent 로 꺼냅니다. 위의 ---@type 줄은 이 값이 TowerClimber 라고 편집기에 알려 주는 표시이고, 실행에는 영향이 없습니다.
- floorText 에 담긴 글자 물체에서 TextComponent 의 Text 를 바꿉니다. 점 두 개(..)는 글자를 이어 붙이는 기호라서, 탑과 층 수와 층을 이어 탑 몇 층이라는 글자를 만듭니다.
@Logic
script TowerHud extends Logic
property Entity floorText = "5e1d9b4c-7048-4b56-ae66-2485c7b9fe1e"
@ExecSpace("ClientOnly")
method void OnUpdate(number delta)
local player = _UserService.LocalPlayer
---@type TowerClimber
local climber = player:GetComponent("script.TowerClimber")
self.floorText.TextComponent.Text = "탑 " .. (climber.floor + 1) .. "층"
end
end맨 위 floorText 옆의 긴 글자는 직접 적는 값이 아닙니다. Reference 창에서 FloorText 를 고르면 Maker 가 그 엔티티를 가리키는 고유 번호를 대신 적어 둔 것이라, 내 월드에서는 다른 값이 들어갑니다.
floor 에 1 을 더하는 이유는 floor 가 0 부터 시작하기 때문입니다. 첫 층에 있을 때 floor 는 0 이라, 1 을 더해야 우리가 읽는 1층, 2층이 됩니다.

저장하고 실행하면 처음에는 탑 1층, 출구에 닿아 올라가면 탑 2층, 한 층 더 오르면 탑 3층이 되고, 떨어지면 다시 탑 1층으로 돌아옵니다. 층을 바꾸는 것은 서버이고, 서버가 floor 를 바꾸면 Sync 로 그 값이 내 클라이언트에 오며, 클라이언트의 OnUpdate 가 새 값으로 글자를 바꿉니다.

정리
- UI 편집기에서 DefaultGroup 에 글자를 올리고, 앵커와 PosY 로 위쪽 가운데에 두었습니다.
- 화면에 하나뿐인 것은 컴포넌트 대신 Logic 으로 만들었습니다.
- UI 는 클라이언트에만 있어서 OnUpdate 를 ClientOnly 로 바꿨습니다.
- 내 플레이어의 floor 에 1 을 더해 글자로 보여 주었습니다.
다음 글에서는 떨어졌을 때 몇 층까지 올랐는지, 그 사람 화면에만 알려 줍니다.
