Week 07 / Period 01 / Game state

게임은 상태를
기억해야 합니다

게임이 기억하는 점수와 남은 시간을 플레이어에게 보여 줍니다. 먼저 Canvas가 무엇인지 살펴보고, 그 안에 글자 하나를 놓는 데서 시작합니다.

설명과 시연 · Canvas 이해 → 화면 배치 → 값 표시

게임 장면 앞의 투명한 판 위에 점수와 시간이 놓여 Canvas와 자식 UI의 관계를 보여 주는 입체 모형
게임 장면 위에 투명한 판을 겹치듯 UI를 배치합니다. Canvas를 준비한 뒤 그 안에 Text를 넣어, 점수와 남은 시간을 보여 줍니다.
이 페이지에서
핵심 질문
코드의 숫자를 화면에 어떻게 보여 줄까요?
학습 방식
이유 이해 → 화면 시연 → 한 가지씩 비교
이전 학습
이동·변수·함수·Inspector 참조
이어 쓸 장면
Week5_MicroLevel + 6주차 애니메이션
오늘의 도구
Canvas · TextMeshPro · Button
도착점
실제 보석 수집 수 · 남은 시간
학습 목표점수와 남은 시간을 화면에 배치하고, 코드의 값이 바뀔 때 글자를 갱신합니다.

5주차에 만든 레벨에 6주차 캐릭터 애니메이션을 적용했습니다. 이제 플레이어가 “점수가 얼마인지, 시간이 얼마나 남았는지” 알 수 있게 화면 위에 정보를 붙입니다.

1교시에는 글자 하나에서 출발해 점수·시간 UI를 만듭니다. 2교시에는 시작·결과 화면과 재시작 UI를 완성하고, 3교시에는 실행 파일로 빌드합니다.

어떤 순서로 완성할까요?

화면에서 보이는 결과를 하나씩 늘립니다. 본문은 설명과 시연 순서이며, 개인 실습에서도 같은 순서로 따라갈 수 있습니다.

  1. 1 · Canvas 이해하기

    빈 Canvas를 확인하고 자식 Text를 넣습니다. Scene 뷰와 Game 뷰를 비교하며 화면 위에 UI를 그린다는 뜻을 이해합니다.

  2. 2 · 배치 원리 익히기

    글자 상자와 글꼴 크기를 구분합니다. Anchor·Pivot으로 위치 기준을 잡은 뒤 Canvas Scaler로 화면 크기에 대응합니다.

  3. 3 · 코드와 Text 연결하기

    ScoreText를 Inspector에 연결하고, 실행하면 99가 0으로 바뀌는지 확인합니다.

  4. 4 · 버튼으로 점수 올리기

    버튼 클릭이 함수를 호출하고, 함수가 숫자와 글자를 바꾸게 합니다.

  5. 5 · 남은 시간 표시하기

    ScoreText를 복제해 TimerText를 연결합니다. 점수와 시간이 모두 작동하면 두 Text를 HUD 아래로 묶습니다.

  6. 6 · 실제 보석과 연결하기

    연습 버튼을 끄고 기존 PlayerProgress의 수집 수를 HUD에 표시합니다. 보석 하나를 먹으면 0 / 3이 1 / 3으로 바뀝니다.

  7. 정리 · 비교하고 설명하기

    설정 하나를 바꾼 결과를 설명하고 Week07_HUD Scene을 저장합니다.

처음 읽을 때

번호가 붙은 여섯 단계를 따라가며 화면에서 확인의 결과를 봅니다. 접힌 비교 실험과 오류 안내는 기본 동작을 확인한 뒤 펼칩니다.

점수를 기억하는 것과 보여 주는 것은 다릅니다

전광판을 떠올려 봅니다. 점수 기록이 바뀌어도 전광판의 숫자를 다시 써 주지 않으면 관객은 이전 점수를 보게 됩니다.

게임도 같습니다. score는 계산할 수 있는 정수이고, Score: 0은 그 값을 읽기 좋게 만든 글자입니다. 값을 바꾸는 일과 글자를 갱신하는 일이 모두 필요합니다.

왼쪽은 기록지에 3이 쓰여 있지만 전광판은 아직 2이고, 오른쪽은 전광판도 갱신되어 기록과 표시가 모두 3인 모형
기록과 표시는 별개입니다. 기록지는 코드가 기억하는 값, 전광판은 Text에 표시한 글자에 해당합니다. 기록이 3으로 바뀌어도 전광판을 갱신해야 관객에게 3이 보입니다. 그림을 누르면 크게 볼 수 있습니다.

아래 모형에서 값을 바꾸는 일과 글자를 갱신하는 일을 따로 실행해 봅니다.

코드가 기억하는 값score = 0

계산에 쓰는 정수

플레이어가 읽는 글자Score: 0

Text 컴포넌트에 쓴 문자열

두 값이 모두 0입니다. 먼저 값에 1을 더해 보세요.

이 모형은 두 동작의 차이를 보여 줍니다. Unity에서는 한 번의 버튼 클릭으로 score += 1; 다음에 RefreshScore();를 호출해 두 동작이 이어지게 만들 것입니다.

기억할 것

UI는 코드에 있는 값을 읽을 수 있는 형태로 보여 줍니다. 화면에 “Score: 99”를 써 놓았다고 해서 코드의 점수가 99가 되지는 않습니다.

작업 전, 5·6주차가 이어진 공통 장면을 엽니다

5주차의 지형·보석·가시·목표에 6주차의 다섯 애니메이션이 적용된 상태에서 UI를 더합니다.

7주차 공통 실습 패키지 내려받기 · 씬 구성과 적용 안내

기존 5·6주차 프로젝트를 백업한 뒤 Assets → Import Package → Custom Package로 가져옵니다. 이 자료는 기존 프로젝트용이며, UI 애셋과 코드도 포함합니다. 직접 고친 기존 스크립트가 있다면 Import 목록에서 해당 파일을 제외하고 필요한 변경만 병합합니다.

패키지의 세 씬
씬 경로 · Assets/Week7 아래용도
Scenes/Week07_Start.unityUI 없는 공통 출발점. 5주차 수집·가시 복귀·목표 규칙과 6주차 Idle / Move / Jump / Fall / Dash가 연결되어 있습니다.
Reference/Week07_HUD_Complete.unity1교시 완성 참고. 실습 중 막히면 자신의 씬과 비교합니다.
Reference/Week07_Complete.unity2교시 완성 참고. 시작부터 결과·재시작까지 확인할 수 있습니다.
  1. 출발 씬을 Week07_HUD로 별도 저장하기

    Assets/Week7/Scenes/Week07_Start.unity를 단독으로 엽니다. Play를 끈 상태에서 File → Save As로 Assets/Week7/Week07_HUD.unity를 저장합니다. 원본 Assets/Week5/Week5_MicroLevel.unity와 참고 씬은 그대로 남깁니다.

    화면에서 확인Hierarchy의 Scene 이름은 Week07_HUD입니다. Canvas는 아직 없고 기존 레벨이 보입니다.
  2. 이전 기능이 연결된 상태 확인하기

    A/D 또는 방향키로 이동하고 Space로 점프, Left Shift로 대시합니다. 다섯 동작과 보석 접촉, 가시 복귀를 확인합니다. 보석을 모두 모아 오른쪽 목표 지점에 닿으면 Console에 완료 로그가 나옵니다. 보석·가시·목표 오브젝트는 유지합니다.

    Play를 끄면 위치와 수집물이 복원됩니다. 이후 UI 설정은 편집 상태에서 진행합니다.

    화면에서 확인기존 게임 규칙은 작동하지만 시작·결과 화면은 아직 없습니다.
여기까지 확인

이번 교시는 기존 수집 값을 화면에 보여 주는 단계입니다. 지형이나 수집물을 새로 만들 필요가 없습니다.

1 · 점수 글자를 놓을 자리가 필요합니다

5·6주차에는 게임 속 캐릭터와 레벨을 만들었습니다. 화면에 계속 읽혀야 하는 점수는 어디에 놓으면 좋을까요?

바닥이나 보석은 게임 속 공간에 있습니다. 카메라가 다른 곳을 바라보면 화면 밖으로 사라질 수 있지요. 그런데 점수까지 함께 사라지면 플레이어가 현재 상황을 알기 어렵습니다. 점수는 게임 장면을 보면서도 계속 읽을 수 있는 자리가 필요합니다.

이처럼 플레이어에게 정보를 보여 주거나 조작할 수 있게 하는 화면 요소를 UI(User Interface)라고 합니다. 이번에는 글자와 버튼을 GameObject로 만드는 Unity의 uGUI를 사용합니다. uGUI 요소가 들어갈 부모가 Canvas입니다.

Canvas는 UI가 놓일 영역과 그려질 방식을 정합니다. 화면 위에 투명한 판을 한 겹 놓고 그 위에 점수를 쓴다고 생각해 보세요. 판만 준비하면 아직 읽을 내용은 없습니다. Canvas를 만든 다음, 그 안에 글자를 그리는 Text를 넣어야 합니다.

뒤쪽 게임 장면 앞에 투명한 판이 놓이고, 그 판의 왼쪽 위에는 점수 0 / 3, 오른쪽 위에는 시간 30이 붙어 있는 분리 모형
게임 화면 위에 UI를 겹칩니다. 투명판은 Canvas의 역할을, 그 위의 두 표시는 자식 UI를 비유합니다. 실제 게임 공간에 유리판을 만드는 것은 아닙니다. 그림은 표시까지 추가한 모습이며, 빈 Canvas 자체는 배경이나 글자를 그리지 않습니다.
  1. 우선 Canvas만 만들기

    Play를 끈 상태에서 Hierarchy의 빈 곳을 우클릭하고 UI → Canvas를 선택합니다. 새 Canvas는 Player나 Main Camera 안에 넣지 않고 Hierarchy의 최상위에 둡니다.

    Canvas를 선택하면 Inspector에 Rect Transform, Canvas, Canvas Scaler, Graphic Raycaster가 보입니다. 지금은 Canvas 컴포넌트의 Render Mode만 봅니다. 기본값인 Screen Space - Overlay를 유지합니다. 다른 항목은 필요한 순간에 하나씩 다룹니다.

    화면에서 확인Hierarchy에 Canvas가 생깁니다. 아직 그 아래에는 점수 Text가 없습니다.
  2. Game 뷰에서 달라진 점 찾아보기

    Game 탭을 눌러 봅니다. 게임 장면만 보이고 새 글자나 흰 사각형은 보이지 않습니다. Canvas는 자식 UI를 그릴 준비를 하는 것이므로, Canvas만 만들었을 때 화면이 그대로인 것은 정상입니다.

    화면에서 확인Game 뷰는 이전 장면과 같습니다. Canvas 생성과 글자 생성이 서로 다른 작업임을 확인합니다.
  3. Scene 뷰에서 Canvas의 영역 찾아보기

    Hierarchy에서 Canvas를 선택한 뒤 Scene 뷰 위에 마우스를 놓고 F를 눌러 선택한 대상을 화면에 맞춥니다. 사각형 윤곽이 보이면 그것이 UI를 배치할 Canvas 영역입니다. 윤곽이 안 보이면 Scene 뷰의 Gizmos 표시도 확인합니다.

    이 사각형은 Game 뷰에서 보일 배경 그림이 아닙니다. 편집할 때 UI의 범위를 알려 주는 표시입니다. 월드의 Player보다 훨씬 크게 보여도 Player나 Canvas의 Scale을 줄이지 않습니다.

    화면에서 확인Scene 뷰에서는 Canvas의 편집 영역을 보고, Game 뷰에서는 플레이어가 볼 결과를 봅니다.
함께 생긴 EventSystem은 무엇인가요?

EventSystem은 버튼처럼 입력을 받는 UI에 클릭·선택 사건을 전달하는 오브젝트입니다. 글자를 화면에 놓는 지금 단계에서는 설정할 일이 없습니다. 뒤의 버튼 단계에서 다시 확인합니다.

여기서 기억할 것

Canvas는 UI가 들어갈 부모이고, 글자 자체는 Text가 그립니다. 다음에는 이 빈 Canvas에 Text 하나만 넣어 봅니다.

1-1 · Canvas 안에 글자 하나를 넣습니다

지금은 Canvas라는 빈 부모만 있습니다. 그 자식으로 Text를 만들면 Game 뷰에 첫 UI가 나타납니다.

6주차에 Player 아래 Visual을 넣었듯이, 이번에는 Canvas 아래 Text를 넣습니다. Hierarchy에서 들여쓰기된 자식 관계를 확인하면 어느 Canvas가 이 글자를 그리는지 알 수 있습니다.

CanvasUI가 놓일 영역과 그리는 방식
ScoreText그 영역 안에 표시할 글자
지금 만들 구조입니다. ScoreText가 Canvas보다 한 단계 안쪽에 놓입니다.
  1. Canvas를 선택한 상태에서 Text 만들기

    Hierarchy의 Canvas를 우클릭하고 UI → Text - TextMeshPro를 선택합니다. 3D Object 메뉴에도 글자 오브젝트가 있지만, 지금은 UI 메뉴에서 만듭니다. 새 오브젝트의 이름을 ScoreText로 바꿉니다.

    처음 TextMeshPro를 사용하면 TMP Importer 창이 열릴 수 있습니다. Import TMP Essentials를 눌러 기본 글꼴과 재료를 가져온 뒤 창을 닫습니다.

    화면에서 확인Canvas 왼쪽의 펼침 화살표를 열면 ScoreText가 자식으로 보입니다. Game 뷰에는 기본 글자인 New Text가 나타납니다.
  2. 표시할 문장만 바꾸기

    ScoreText를 선택합니다. Inspector의 TextMeshPro - Text (UI) 컴포넌트에서 Text Input을 찾아 Score: 99라고 입력합니다. 위치나 크기는 그대로 두고 Game 뷰를 봅니다.

    이 99는 현재 화면에 적어 둔 글자입니다. 아직 점수를 계산하는 코드와 연결하지 않았습니다. 잠시 뒤 코드로 0을 표시해 두 값의 차이를 확인할 것입니다.

    화면에서 확인Play를 누르지 않아도 New Text가 Score: 99로 바뀝니다.
  3. 읽을 수 있는 글자 크기와 색 정하기

    같은 Text 컴포넌트에서 Auto Size를 끄고 Font Size를 36으로 설정합니다. Auto Size를 끄면 글자 상자 크기가 바뀌어도 글꼴 크기는 지정한 값으로 유지됩니다. Vertex Color 또는 Color에서 배경과 구별되는 색을 고르고 Alpha가 0이 아닌지 확인합니다.

    화면에서 확인Game 뷰에서 Score: 99를 읽을 수 있습니다. 화면 모서리에 놓는 작업은 다음 단계에서 합니다.
여기서 기억할 것

Canvas 아래에 Text를 만들고, Text의 내용을 바꾸면 화면의 글자가 바뀝니다. 지금은 글자가 보이는 것까지 확인하면 됩니다.

1-2 · 같은 UI를 두 창에서 다르게 봅니다

글자가 생겼으니, Canvas가 게임 장면과 어떤 관계인지 실제 화면에서 확인합니다.

Scene 뷰는 오브젝트를 선택하고 배치하는 편집 공간입니다. Game 뷰는 게임이 그린 결과를 보여 줍니다. Scene 뷰에서 Canvas가 크게 보이더라도, Game 뷰에서 점수가 읽기 좋은 크기로 보이면 그 결과를 기준으로 판단합니다.

아까 확인한 Screen Space - Overlay에서 Screen Space는 화면을 기준으로 놓는다는 뜻이고, Overlay는 게임 장면 위에 겹쳐 그린다는 뜻입니다. 이 방식은 UI를 그릴 카메라를 별도로 지정하지 않습니다.

  1. 카메라가 움직여도 글자가 남는지 보기

    Play를 누르고 Player를 움직입니다. 6주차의 추적 카메라가 따라가면 바닥이나 보석은 화면 안에서 위치가 달라집니다. ScoreText는 Canvas에서 정한 화면 위치에 남습니다. 관찰을 마치면 Play를 끕니다.

    추적 카메라가 없는 시작 장면이라면 이 비교는 6주차 카메라 시연에서 확인하고, 아래의 부모·자식 표시 확인을 진행합니다.

    화면에서 확인카메라가 바라보는 월드가 바뀌어도 점수 글자는 화면의 같은 위치에 남습니다.
  2. 부모 Canvas의 표시를 껐다가 켜기

    Play가 꺼진 상태에서 Hierarchy의 Canvas를 선택합니다. Inspector 맨 위 이름 옆의 GameObject 활성화 체크를 끄면 Game 뷰에서 ScoreText가 사라집니다. 체크를 다시 켜면 돌아옵니다. 확인 후 반드시 켜 둡니다.

    부모를 비활성화해도 ScoreText가 삭제되지는 않습니다. Canvas 아래의 UI 전체가 함께 비활성화되었다가 다시 표시되는 것입니다.

    화면에서 확인Canvas를 껐을 때 글자가 사라지고, 다시 켰을 때 같은 글자가 보입니다.
다른 Render Mode는 언제 쓰나요?

Screen Space - Camera는 지정한 카메라를 통해 UI를 그릴 때 사용합니다. World Space는 게임 속 표지판이나 캐릭터 머리 위 정보처럼 UI를 월드의 위치에 놓고 싶을 때 사용합니다. 이번 점수 표시는 화면에 계속 남겨 둘 것이므로 Overlay를 유지합니다.

여기서 기억할 것

이번 Canvas는 화면을 기준으로 UI를 그립니다. 배치를 편집할 때는 Scene 뷰, 실제로 읽히는 결과를 볼 때는 Game 뷰를 사용합니다.

2 · 글자가 들어갈 사각형부터 조절합니다

글자를 옮기기 전에, UI의 위치와 크기를 어느 컴포넌트에서 바꾸는지 구분합니다.

Player의 위치는 Transform에서 바꾸었습니다. UI에서는 Rect Transform을 사용합니다. Rect는 사각형이라는 뜻입니다. 글자도 사각형 영역 안에 들어가기 때문에, 위치뿐 아니라 Width와 Height가 필요합니다.

ScoreText를 선택하고 Rect Tool 단축키 T를 누르면 Scene 뷰에서 글자가 들어가는 상자의 테두리를 볼 수 있습니다. 상자의 크기와 글꼴 크기는 서로 다릅니다. Width는 한 줄에 글자를 담을 공간이고, Font Size는 글자 자체의 크기입니다.

  1. 글자 상자에 충분한 공간 주기

    ScoreText의 Rect Transform에서 Width를 320, Height를 72로 설정합니다. Text 컴포넌트의 Font Size 36은 유지합니다. 기본 Word Wrapping 상태에서는 상자가 좁으면 글자가 다음 줄로 넘어갈 수 있습니다. 지금은 Score: 99를 한 줄에 담을 공간을 확보하는 것입니다.

    화면에서 확인상자는 넓어지지만 글꼴 크기는 36으로 유지됩니다. Score: 99가 한 줄로 들어갑니다.
  2. 상자 안에서 글자를 정렬하기

    이번에는 Text 컴포넌트의 Alignment를 왼쪽·위쪽으로 선택합니다. Game 뷰에서 글자가 상자 안의 어느 쪽에 붙는지 봅니다. Alignment는 글자 상자 안의 정렬이며, 상자 자체를 화면 왼쪽 위로 옮기는 설정은 아닙니다.

    화면에서 확인글자는 자기 사각형 안의 왼쪽 위에 놓입니다. 상자의 화면 위치는 아직 그대로입니다.
여기서 기억할 것

Rect Transform은 글자 상자의 위치·크기, Text 컴포넌트는 내용·글꼴 크기·정렬을 담당합니다. 이제 이 상자 전체를 화면 모서리로 옮깁니다.

2-1 · 위치를 어느 지점에서 잴까요?

“오른쪽으로 40”이라고 말하려면 어디에서부터 40인지 먼저 정해야 합니다.

Anchor는 부모 사각형에서 위치를 잴 기준점입니다. 지금 ScoreText의 부모는 Canvas이므로, 화면의 가운데나 모서리를 기준으로 삼을 수 있습니다. 왼쪽 아래는 (0, 0), 가운데는 (0.5, 0.5), 왼쪽 위는 (0, 1)입니다. 이 숫자는 픽셀 거리가 아니라 부모 영역 안의 비율입니다.

Pivot은 내 사각형의 기준점입니다. 점수 상자의 왼쪽 위를 부모의 왼쪽 위에 맞추려면, Anchor와 Pivot을 모두 왼쪽 위로 정하면 됩니다. 그다음 Pos X·Y로 모서리에서 얼마나 띄울지 정합니다.

Anchor는 부모에서의 기준점, Pivot은 글자 상자에서의 기준점입니다. 크기를 단순화한 설명 그림입니다.
  1. 부모의 왼쪽 위를 기준으로 정하기

    ScoreText의 Rect Transform에서 Anchors를 펼칩니다. Min의 X를 0, Y를 1로 입력하고, Max도 (0, 1)로 맞춥니다. Min과 Max가 같으면 두 Anchor가 한 점에 모여 고정된 크기의 상자를 배치하게 됩니다.

    Anchor를 바꾸어도 Unity가 기존 화면 위치를 유지하도록 Pos 값을 보정할 수 있습니다. 글자가 바로 모서리로 이동하지 않아도 정상입니다.

    화면에서 확인Anchors의 Min과 Max가 모두 (0, 1)입니다. 부모 안의 기준점이 왼쪽 위로 정해졌습니다.
  2. 글자 상자의 왼쪽 위를 그 기준점에 맞추기

    Pivot도 (0, 1)로 설정합니다. 그다음 Pos X와 Pos Y를 둘 다 0으로 입력합니다. Anchor·Pivot을 바꾼 뒤에 Pos를 입력해야 자동 보정된 위치값이 남지 않습니다.

    화면에서 확인글자 상자의 왼쪽 위가 화면의 왼쪽 위에 맞닿습니다. 아직 여백은 없습니다.
  3. 모서리에서 조금 띄우기

    Pos X만 40으로 바꾸어 오른쪽으로 이동하는지 봅니다. 이어서 Pos Y를 -40으로 바꾸어 아래로 이동하는지 봅니다. UI의 Y 방향도 위가 양수이므로, 위쪽 모서리에서 아래로 내려오려면 음수를 사용합니다.

    화면에서 확인Score: 99가 화면의 왼쪽 위에서 오른쪽과 아래로 조금 떨어져 놓입니다.
  4. 화면 비율이 바뀌어도 기준점이 유지되는지 보기

    Game 뷰 위쪽의 비율 메뉴를 16:9와 4:3으로 번갈아 바꿉니다. 점수가 화면 중앙이 아니라 왼쪽 위를 기준으로 놓이는지 봅니다.

    화면에서 확인화면의 가로·세로 비율이 달라져도 점수는 왼쪽 위에 남습니다.
여기서 기억할 것

기준점을 정한 뒤 여백을 정합니다. Anchor·Pivot은 “어느 점을 맞출까”, Pos X·Y는 “그 점에서 얼마나 떨어질까”에 답합니다.

2-2 · 화면 크기가 달라질 때 UI 크기도 맞춥니다

모서리에 놓는 방법은 익혔습니다. 이제 큰 화면과 작은 화면에서 글자 크기를 어떻게 유지할지 생각해 봅니다.

같은 크기의 글자를 큰 종이와 작은 종이에 올리면 작은 종이에서 더 큰 비중을 차지합니다. UI도 화면 해상도가 달라질 때 비슷한 문제가 생깁니다. 기본 Constant Pixel Size는 UI 크기를 픽셀 기준으로 유지하므로, 작은 게임 화면에서는 같은 글자가 상대적으로 커집니다.

Canvas Scaler는 Canvas 안의 UI 전체를 화면 크기에 맞춰 얼마나 확대·축소할지 정합니다. Scale With Screen Size를 선택하면 기준 화면보다 작을 때 UI도 작아지고, 클 때 UI도 커집니다. 각 Text의 Font Size를 따로 바꾸는 작업을 Canvas 전체의 배율로 처리하는 것입니다.

  1. ScoreText에서 부모 Canvas로 선택 바꾸기

    Hierarchy에서 Canvas를 선택하고 Inspector의 Canvas Scaler 컴포넌트를 찾습니다. UI Scale Mode를 Constant Pixel Size에서 Scale With Screen Size로 바꿉니다.

    화면에서 확인Canvas Scaler 아래에 Reference Resolution과 Screen Match Mode 항목이 보입니다.
  2. 배치할 때의 기준 화면 크기 정하기

    Reference Resolution의 X를 1920, Y를 1080으로 입력합니다. 이는 “1920 × 1080에서 배치한 UI를 기준으로 크기를 조절하겠다”는 뜻입니다. Game 뷰나 실행 파일의 해상도를 강제로 1920 × 1080으로 바꾸는 항목은 아닙니다.

    예를 들어 같은 16:9 비율에서 게임 해상도가 960 × 540이 되면 가로·세로가 절반이므로 UI도 절반 배율로 그려집니다. ScoreText의 Font Size 설정값 36은 그대로이며, Canvas의 배율이 적용됩니다.

    화면에서 확인Reference Resolution이 1920 × 1080입니다. ScoreText를 다시 선택해도 Font Size는 36입니다.
  3. 가로·세로 비율이 다를 때의 기준 정하기

    Canvas를 다시 선택합니다. Screen Match Mode는 Match Width Or Height로 두고 Match를 0.5로 설정합니다. 0은 가로 크기, 1은 세로 크기를 기준으로 배율을 정하며, 0.5는 두 기준을 함께 반영합니다.

    화면이 기준과 같은 16:9라면 가로·세로의 변화 비율도 같습니다. 4:3처럼 다른 비율이 되면 두 변화 비율이 달라지므로 어느 쪽을 반영할지 정해야 합니다.

    화면에서 확인Scale With Screen Size, 1920 × 1080, Match 0.5가 한 컴포넌트에 설정되어 있습니다.
  4. Game 뷰에서 최종 배치 확인하기

    16:9와 4:3을 다시 비교합니다. 왼쪽 위에 점수가 남고, 글자 전체가 읽히는지 봅니다. Anchor가 기준 위치를 유지하고, Canvas Scaler가 크기를 조절한다는 두 역할을 나누어 설명해 봅니다.

    화면에서 확인두 화면 비율에서 Score: 99가 잘리지 않고 왼쪽 위에 보입니다.
설정값을 다시 확인할 때

표를 좌우로 밀어 설정값을 확인하세요.

설명을 따라 배치한 뒤 확인할 값
대상항목설정
CanvasRender ModeScreen Space - Overlay
Canvas ScalerUI Scale ModeScale With Screen Size
Canvas ScalerReference Resolution / Match1920 × 1080 / 0.5
ScoreTextAnchor Min·Max / Pivot모두 (0, 1)
ScoreTextPos / Width·Height(40, -40) / 320 × 72
ScoreTextFont Size / Alignment36 / 왼쪽·위쪽
다음 단계로 넘어가기 전

Canvas 아래 ScoreText 하나가 있고, Game 뷰 왼쪽 위에 Score: 99가 보이면 됩니다. 글자를 놓는 방법을 확인했으니, 이제 코드가 그 내용을 바꾸게 합니다.

3 · 코드의 값을 ScoreText에 연결합니다

배치를 마쳤으니, Inspector에 써 둔 글자를 코드가 바꾸게 합니다. 처음에는 실행 시 99를 0으로 바꾸는 것만 확인합니다.

스크립트는 이름이 같은 Text를 자동으로 찾아 주지 않습니다. scoreText라는 참조 칸을 만들고 Hierarchy의 ScoreText를 그 칸에 연결합니다. 이 참조를 통해 scoreText.text에 새 문자열을 쓸 수 있습니다.

int score는 점수를 기억하고, TMP_Text scoreText는 글자를 보여 줄 컴포넌트를 가리킵니다. [SerializeField]는 private 필드도 Inspector에서 연결할 수 있게 합니다. Start()에서 점수를 0으로 정한 뒤 RefreshScore()를 호출합니다.

작성 위치공통 패키지의 Assets/Week7/Scripts/HudPractice.cs를 엽니다. 새 파일을 중복 생성하지 않고, 이 파일의 전체 내용을 아래 점수 전용 코드로 바꿉니다. 이후 버튼·타이머 단계에서 기능을 차례로 더합니다. 파일 이름과 클래스 이름은 모두 HudPractice입니다.
HudPractice.cs · 점수만 연결한 단계
using TMPro;
using UnityEngine;

public class HudPractice : MonoBehaviour
{
    [SerializeField] private TMP_Text scoreText;
    private int score;

    private void Start()
    {
        score = 0;
        RefreshScore();
    }

    private void RefreshScore()
    {
        scoreText.text = $"Score: {score}";
    }
}

$"Score: {score}"는 중괄호 안의 값을 문자열에 넣는 표현입니다. score가 0이면 Score: 0이 됩니다. RefreshScore()는 우리가 만든 함수이며, 이름을 정했다고 자동으로 실행되는 것은 아닙니다. 지금은 Start()가 점수를 0으로 정한 다음 이 함수를 호출해 화면에 씁니다.

  1. 스크립트를 Scene 오브젝트에 붙이기

    코드를 저장하고 Unity로 돌아와 컴파일이 끝날 때까지 기다립니다. Hierarchy 빈 곳에서 Create Empty로 HudController를 만듭니다. Project의 HudPractice 스크립트를 HudController의 Inspector에 드래그해 붙입니다.

    화면에서 확인HudController에 Hud Practice 컴포넌트가 있고 Score Text 칸이 보입니다.
  2. Score Text 참조 연결하기

    Hierarchy의 ScoreText를 HudController의 Score Text 칸으로 드래그합니다. Project의 스크립트 파일이나 Canvas를 넣는 칸이 아닙니다. Scene을 저장합니다.

    화면에서 확인Score Text가 None이 아니라 ScoreText (TextMeshProUGUI)를 가리킵니다.
  3. 99가 0으로 바뀌는지 실행하기

    편집 상태의 Text Input이 Score: 99인지 확인한 뒤 Play를 누릅니다. Play를 끄면 편집 상태의 글자로 돌아갈 수 있습니다. 다시 Play를 눌러도 코드의 시작 값은 0입니다.

    화면에서 확인실행하면 Score: 0이 보이고 Console에 빨간 오류가 없습니다.
기억할 것

파일을 만드는 것, 오브젝트에 붙이는 것, 참조를 연결하는 것은 세 단계입니다. 코드의 scoreText와 Hierarchy의 ScoreText는 Inspector 연결을 통해 만납니다.

4 · 버튼 한 번으로 점수와 글자를 바꿉니다

글자가 코드에서 바뀌는 것을 확인했습니다. 이번에는 실행 중에 점수를 바꿀 사건을 만듭니다.

수집물 접촉을 기다리지 않고도 UI를 확인할 수 있도록 점수 확인용 버튼을 만듭니다. 먼저 클릭할 때 실행할 AddScore() 함수를 추가합니다. 이 함수는 점수에 1을 더한 뒤, 앞에서 만든 RefreshScore()를 호출합니다.

추가할 위치HudPractice.cs의 Start를 닫는 중괄호 다음, private void RefreshScore() 바로 위에 아래 함수를 넣습니다. 두 함수와 마찬가지로 HudPractice 클래스 안에 두며, Start 함수 안에 넣지 않습니다.
HudPractice.cs · AddScore 함수 추가
public void AddScore()
{
    score += 1;
    RefreshScore();
}

Button의 On Click()은 클릭했을 때 실행할 함수를 연결하는 곳입니다. 위처럼 public void로 선언하고 매개변수를 받지 않는 함수는 이 목록에서 선택할 수 있습니다. 함수 작성과 버튼 연결을 차례로 진행합니다.

  1. AddScore를 추가하고 컴파일 확인하기

    Play를 끄고 위 함수를 지정한 위치에 추가합니다. 파일을 저장한 뒤 Unity로 돌아와 컴파일이 끝날 때까지 기다립니다. 기존 Start와 RefreshScore는 그대로 둡니다.

    화면에서 확인Console에 빨간 오류가 없습니다. 함수를 추가한 것만으로 점수가 오르지는 않습니다.
  2. 점수 확인용 버튼 배치하기

    Play를 끄고 Canvas를 우클릭해 UI → Button - TextMeshPro를 만듭니다. 이름을 AddScoreButton으로 바꾸고 자식 Text의 내용을 +1 Score로 둡니다. 버튼 Rect Transform의 Anchor Min·Max·Pivot을 모두 (0.5, 0), Pos를 (0, 40), Width 240, Height 72로 설정합니다.

    화면에서 확인Game 뷰의 아래쪽 중앙에 +1 Score 버튼이 보입니다.
  3. Button의 On Click에 함수 연결하기

    AddScoreButton의 Button 컴포넌트에서 On Click()의 +를 한 번 누릅니다. Object 칸에 Hierarchy의 HudController를 넣고 함수 목록에서 HudPractice → AddScore()를 선택합니다. 같은 함수를 두 줄에 연결하지 않습니다.

    화면에서 확인On Click 목록에 HudController와 HudPractice.AddScore가 한 줄로 연결되어 있습니다.
  4. EventSystem 확인하고 클릭하기

    Hierarchy의 EventSystem은 UI 입력을 전달합니다. 없으면 UI → Event System으로 하나를 만듭니다. 4주차의 Input System을 사용하므로 Input System UI Input Module을 사용합니다. Standalone Input Module이 보이면 Replace with InputSystemUIInputModule로 교체합니다. Actions Asset과 Point·Left Click이 채워져 있는지 봅니다. 비어 있다면 이 컴포넌트의 우측 위 메뉴에서 Reset으로 기본 UI 입력 연결을 복원합니다.

    Play를 누르고 Game 뷰에서 버튼을 세 번 클릭합니다. 이후 Game 뷰의 빈 곳을 클릭하고 이동 키도 확인합니다.

    화면에서 확인Score: 0 → 1 → 2 → 3으로 바뀝니다. 클릭 한 번에 정확히 1이 오르고 캐릭터 이동도 작동합니다.
기억할 것

Button은 연결된 함수를 호출하고, 함수는 값과 화면을 갱신합니다. 이번 버튼은 수집 사건을 대신하는 확인 도구입니다. 2교시에는 실제 수집물이 점수 증가를 요청하게 만듭니다.

5 · 같은 방법으로 남은 시간을 표시합니다

점수는 클릭할 때 바뀌고, 시간은 실행 중 계속 바뀝니다. 값이 바뀌는 시점은 달라도 Text로 보여 주는 방법은 같습니다.

남은 시간은 소수 부분이 필요하므로 float timeRemaining에 저장합니다. Start()에서 시작 시간으로 설정하고, 매 프레임 호출되는 Update()에서 Time.deltaTime을 뺍니다. deltaTime은 이전 프레임부터 흐른 시간(초)입니다. 프레임마다 1을 빼면 1초가 아니라 프레임 수만큼 줄어듭니다.

예를 들어 한 프레임에 0.02초가 지났다면 30에서 0.02를 뺍니다. Mathf.Max(0f, ...)는 계산 결과가 음수가 될 때 0을 선택합니다. :0.0은 화면에 소수점 한 자리까지 쓰는 형식이며, 내부의 남은 시간을 한 자리로 바꾸지는 않습니다.

시간 감소 → 글자 갱신 · 완성 코드에서 읽을 부분
private void Update()
{
    timeRemaining = Mathf.Max(0f, timeRemaining - Time.deltaTime);
    RefreshTime();
}

private void RefreshTime()
{
    timerText.text = $"Time: {timeRemaining:0.0}";
}
  1. ScoreText를 복제해 TimerText 배치하기

    Play를 끄고 Canvas 아래 ScoreText를 선택해 복제한 뒤 이름을 TimerText로 바꿉니다. 복제한 Text도 Canvas의 자식으로 둡니다. Anchor Min·Max·Pivot을 모두 (1, 1)로 바꾸고, 마지막에 Pos를 (-40, -40)으로 입력합니다. Width 320, Height 72는 유지하고 글자 정렬은 오른쪽·위쪽으로 설정합니다. Text Input은 Time: 30.0으로 둡니다.

    화면에서 확인Canvas 바로 아래 ScoreText와 TimerText가 나란히 있습니다. Game 뷰에는 왼쪽 위 점수와 오른쪽 위 시간이 보입니다.
  2. 기존 스크립트를 시간까지 포함한 코드로 바꾸기

    아래 완성 코드를 펼쳐 기존 HudPractice.cs의 전체 내용을 교체합니다. 같은 이름의 두 번째 스크립트를 만들거나, 기존 코드 끝에 이어 붙이지 않습니다. 저장 후 Unity의 컴파일을 기다립니다.

    HudController의 새 Timer Text 칸에 TimerText를 드래그합니다. Score Text 참조와 버튼의 AddScore 연결이 유지되는지 확인하고, Round Duration을 30으로 둡니다.

    화면에서 확인Hud Practice 컴포넌트의 두 Text 참조가 채워져 있고 Round Duration이 30입니다.
  3. 점수와 시간이 함께 바뀌는지 실행하기

    Play를 누릅니다. 시간은 30초에서 줄어들기 시작하고, 버튼을 누를 때마다 점수는 1씩 올라갑니다. 빠른 종료 확인은 Play를 끈 뒤 Round Duration을 5로 바꾸고 다시 실행합니다. 확인 후 Play를 끄고 30으로 복원합니다.

    화면에서 확인시간은 0.0 아래로 내려가지 않습니다. 0.0이 되어도 캐릭터는 움직이고 점수 버튼은 계속 작동합니다.
완성 코드 · HudPractice.cs 전체 내용 교체

점수 단계의 Start를 확장하고 TimerText·시작 시간·Update·RefreshTime을 추가했습니다. 기존 AddScore와 RefreshScore는 유지합니다.

HudPractice.cs · 점수와 시간
using TMPro;
using UnityEngine;

public class HudPractice : MonoBehaviour
{
    [SerializeField] private TMP_Text scoreText;
    [SerializeField] private TMP_Text timerText;
    [SerializeField] private float roundDuration = 30f;

    private int score;
    private float timeRemaining;

    private void Start()
    {
        score = 0;
        timeRemaining = roundDuration;
        RefreshScore();
        RefreshTime();
    }

    private void Update()
    {
        timeRemaining = Mathf.Max(0f, timeRemaining - Time.deltaTime);
        RefreshTime();
    }

    public void AddScore()
    {
        score += 1;
        RefreshScore();
    }

    private void RefreshScore()
    {
        scoreText.text = $"Score: {score}";
    }

    private void RefreshTime()
    {
        timerText.text = $"Time: {timeRemaining:0.0}";
    }
}

완성 HudPractice.cs 내려받기 · 이미 만든 파일이 있다면 그 파일의 내용을 교체합니다.

0.0이 보이면 게임이 끝난 것일까요?

지금 코드는 시간을 줄이고 글자를 보여 주는 역할만 합니다. 시간이 끝났다는 판정과 이동·점수 입력을 멈추는 규칙은 아직 없습니다. 표시를 만드는 일과 게임의 진행을 제어하는 일이 다르다는 것을 다음 교시에서 이어갑니다.

기억할 것

Update에서 지난 시간을 빼고, 바뀐 값을 TimerText에 씁니다. 점수는 클릭 때, 타이머는 매 프레임 갱신한다는 차이를 설명할 수 있어야 합니다.

5-1 · 작동하는 두 Text를 한 부모 아래 묶습니다

점수는 버튼으로 바뀌고 시간은 계속 줄어듭니다. 두 표시가 작동하는 것을 확인했으니, 이제 Hierarchy를 정리합니다.

점수와 시간처럼 플레이 중 계속 읽는 정보를 HUD라고 합니다. 다음 교시에는 게임 진행에 따라 두 표시를 함께 켜고 끌 것입니다. 이를 위해 Canvas와 두 Text 사이에 HUD라는 부모를 하나 만듭니다.

점수 0 / 3과 시간 30 표시를 담은 쟁반을 옮기기 전과 후. 쟁반 안의 두 표시는 같은 간격과 배치를 유지한다
부모 하나로 두 자식을 함께 다룹니다. 쟁반은 HUD, 두 표시는 ScoreText와 TimerText입니다. 왼쪽과 오른쪽은 같은 묶음을 옮기기 전후입니다. 자식을 한 부모 아래 두면 함께 이동시키거나 켜고 끌 수 있습니다.
  1. Canvas 안에 빈 HUD 부모 만들기

    Play를 끄고 Canvas를 우클릭해 UI → Panel을 만든 뒤 이름을 HUD로 바꿉니다. Panel에는 UI를 담을 Rect Transform과 배경을 그리는 Image 컴포넌트가 있습니다.

    이번에는 부모 역할만 필요하므로 HUD의 Image 컴포넌트 메뉴에서 Remove Component를 선택합니다. Rect Transform은 유지합니다.

    화면에서 확인Canvas 아래에 HUD가 생깁니다. Panel을 만들 때 나타났던 배경 사각형은 사라지고 두 Text는 아직 Canvas 바로 아래에 있습니다.
  2. HUD를 Canvas 전체에 맞추기

    HUD의 Rect Transform에서 Anchor Min은 (0, 0), Max는 (1, 1)인지 확인합니다. Left·Right·Top·Bottom은 모두 0으로 둡니다. Panel을 처음 만들었을 때의 기본 배치입니다.

    앞에서는 Anchor 두 점을 같은 모서리에 모았습니다. 이번에는 두 점을 부모의 반대쪽 모서리로 벌려 부모 전체를 채웁니다. 이런 배치를 Stretch라고 합니다. HUD가 Canvas와 같은 영역을 차지해야 두 Text도 기존 화면 모서리를 기준으로 놓일 수 있습니다.

    화면에서 확인Scene 뷰에서 HUD를 선택하면 사각형 영역이 Canvas 전체와 겹칩니다. Game 뷰에는 배경이 추가되지 않습니다.
  3. 기존 두 Text를 옮기고 다시 실행하기

    Hierarchy의 ScoreText와 TimerText를 HUD 위로 드래그해 자식으로 넣습니다. 기존 오브젝트를 옮기므로 HudController에 연결한 Text 참조는 유지됩니다.

    ScoreText의 Anchor Min·Max·Pivot은 (0, 1), Pos는 (40, -40)인지 확인합니다. TimerText는 각각 (1, 1), (-40, -40)인지 확인합니다. AddScoreButton은 Canvas 바로 아래에 둡니다.

    Play를 눌러 버튼과 타이머를 다시 확인하고, Game 뷰를 16:9와 4:3으로 바꿔 양쪽 모서리 배치를 봅니다. 확인 후 Play를 끄고 Scene을 저장합니다.

    화면에서 확인Canvas 안의 HUD를 펼치면 ScoreText와 TimerText가 보입니다. 점수·시간의 위치와 동작은 묶기 전과 같습니다.
기억할 것

두 Text를 먼저 완성하고, 함께 다룰 수 있도록 부모 아래 묶었습니다. 다음 교시에는 이 HUD 그룹을 그대로 이어 씁니다.

5-2 · 작동하는 HUD에 배경과 아이콘을 붙입니다

점수와 시간을 쓰는 코드는 그대로 두고, 각 숫자를 읽기 쉽도록 그림을 더합니다.

공통 실습 패키지를 가져왔다면 UI 애셋도 이미 있습니다. 그림만 따로 필요할 때는 픽셀 UI 패키지를 Import합니다. Assets/Week7/UI/Sprites에서 PNG를 펼치면 같은 이름의 Sprite가 있습니다. 아래 Source Image 칸에는 이 Sprite를 넣습니다. 글자는 이미지에 들어 있지 않으므로 기존 ScoreText·TimerText를 그대로 사용합니다.

  1. 두 숫자 뒤에 크림색 배경 놓기

    Play를 끄고 HUD 아래에 UI → Image 두 개를 만듭니다. 이름은 ScoreBackground, TimerBackground입니다. 두 Image의 Source Image에 HUD_Backplate, Image Type에 Sliced, Color에 흰색을 지정합니다. 크기는 각각 360 × 80입니다.

    왼쪽 배경의 Anchor Min·Max·Pivot은 모두 (0, 1), Pos는 (40, -40)입니다. 오른쪽은 모두 (1, 1), Pos는 (-40, -40)입니다. 두 배경을 Hierarchy에서 Text보다 앞 순서로 옮기면 글자 뒤에 그려집니다.

    화면에서 확인두 모서리에 크림색 배경이 보입니다. HUD 부모의 Image는 제거한 상태이며, 작은 배경 Image 두 개만 추가했습니다.
  2. 보석·시계 아이콘과 글자 자리 나누기

    HUD 아래에 ScoreIcon, TimerIcon Image를 더합니다. 각각 Icon_Gem, Icon_Timer Sprite를 넣고 Image Type은 Simple, Preserve Aspect는 켭니다. 크기는 40 × 48입니다. 아이콘과 배경의 Raycast Target은 모두 끕니다.

    아이콘과 Text의 배치는 아래 표를 따릅니다. 각 행의 Anchor Min·Max·Pivot은 같은 값입니다. Text 색은 #293A32, Font Size는 36, 정렬은 가로 왼쪽·세로 가운데로 맞춥니다.

    화면에서 확인그림은 왼쪽, 글자는 그 옆에 놓입니다. 배경 → 아이콘 → Text 순서로 그려지며 서로 겹치지 않습니다.
오브젝트Anchor · PivotPos X / YWidth / Height
ScoreIcon(0, 1)64 / -5640 / 48
ScoreText(0, 1)124 / -52260 / 56
TimerIcon(1, 1)-336 / -5640 / 48
TimerText(1, 1)-56 / -52260 / 56

AddScoreButton에도 Button_Normal Sprite를 넣고 Image Type을 Sliced, Color를 흰색으로 둡니다. 자식 Text는 Font Size 36, 크림색 #F4EEDC로 바꿉니다. 기존 Button의 On Click과 HudController 참조는 그대로 둡니다. 버튼의 상태별 Sprite 연결은 2교시에서 이어서 다룹니다.

적용 뒤 확인

Play에서 점수 버튼과 타이머가 계속 작동하고, 16:9·4:3에서 배경·아이콘·글자가 함께 모서리에 남는지 확인합니다. 두 Text의 Inspector 참조가 유지된 상태로 Scene을 저장합니다.

애셋 7종 미리보기와 전체 적용 안내

Unity 패키지 내려받기 · PNG·설정 ZIP 내려받기 · 적용 안내 내려받기

글자가 없는 투명 배경 PNG 7종입니다. 패키지를 Import하면 Assets/Week7/UI/Sprites에 들어갑니다. PNG를 펼친 뒤 같은 이름의 Sprite를 Source Image에 연결합니다. 점수·시간·버튼 글자는 기존 TextMeshPro를 사용합니다.

얇은 테두리의 픽셀 UI 세트: 크림색 HUD, 제목 띠가 있는 작은 창, 보석과 시계 아이콘, 초록색 버튼 3상태
같은 애셋을 Unity UI에 적용한 미리보기입니다. 그림의 숫자와 글자는 별도의 TextMeshPro이며 PNG 원본에는 들어 있지 않습니다.

HUD 아래 숫자별 배경 Image 두 개에 HUD_Backplate를 넣고 Image Type을 Sliced로 둡니다. 각각 Icon_Gem, Icon_Timer Image를 더해 점수·시간 옆에 놓습니다. 숫자는 크림색 배경에서 잘 읽히도록 짙은 녹색 #293A32로 바꿉니다. 배경은 Text보다 먼저 그려지도록 앞 순서에 두고, 장식 Image의 Raycast Target은 끕니다. 기존 Text 참조는 유지합니다.

6 · 연습 버튼을 실제 보석 수집으로 바꿉니다

값을 Text에 쓰는 원리를 확인했으니, 이제 5주차에서 이미 기억하던 수집 수를 읽습니다.

지금까지의 HudPractice는 버튼으로 값을 바꾸는 연습이었습니다. 실제 레벨에서는 Collectible → PlayerProgress.AddCollectible()이 수집 수를 바꿉니다. HUD에 별도 점수를 만들면 같은 보석을 서로 다르게 셀 수 있으므로, 기존 값 하나를 읽어 표시합니다.

  1. PlayerProgress에 읽기 함수 두 개 확인하기

    Assets/Scripts/PlayerProgress.cs의 클래스 안에 아래 두 함수가 있는지 확인합니다. 공통 패키지에는 이미 들어 있습니다. 자신의 5주차 파일을 유지했다면 이 두 함수만 클래스 안에 추가하고 기존 수집·복귀 함수는 유지합니다.

    PlayerProgress 클래스 안 · 이미 있다면 중복 추가하지 않기
    public int GetCollectedCount() => collectedCount;
    public int GetRequiredCollectibles() => requiredCollectibles;

    화살표 오른쪽 값을 돌려주는 짧은 함수입니다. 수집 수를 바꾸지 않고 현재 값만 알려 줍니다.

    화면에서 확인Collectible은 기존처럼 PlayerProgress에 수집을 알리고, HUD는 읽기 함수로 그 값을 확인할 수 있습니다.
  2. 연습 오브젝트를 끄고 HUDDisplay 연결하기

    HudController와 AddScoreButton을 비활성화합니다. Hierarchy 최상위에 빈 HUDDisplay를 만들고 Week07HudDisplay.cs를 붙입니다. Progress에는 Player, Score Text에는 Canvas/HUD/ScoreText, Timer Text에는 Canvas/HUD/TimerText를 넣습니다. Round Duration은 30입니다.

    같은 Text에 쓰는 스크립트는 HUDDisplay 하나만 켭니다. 기존 PlayerProgress와 보석·가시·목표 컴포넌트는 이 교시 끝까지 유지합니다.

    화면에서 확인세 참조가 채워지고 연습 점수 버튼은 사라집니다.
  3. 보석 접촉으로 화면 값 확인하기

    Play 후 왼쪽 보석 하나에 닿습니다. 보석이 사라지고 HUD가 0 / 3 → 1 / 3으로 바뀌는지 봅니다. 가시에 닿으면 시작점으로 돌아오되 수집 수는 유지되고 시간은 계속 줄어듭니다.

    화면에서 확인Console의 수집 수와 HUD가 같습니다. 0초가 되어도 아직 게임 종료 화면은 나오지 않습니다.

Week07HudDisplay.cs 내려받기

Week07HudDisplay.cs 전체 코드
Week07HudDisplay.cs
using TMPro;
using UnityEngine;

namespace GameEngineI.Week07
{
    // Period 1: display Week 5's existing collection count; do not own a second score.
    public sealed class Week07HudDisplay : MonoBehaviour
    {
        [SerializeField] PlayerProgress progress;
        [SerializeField] TMP_Text scoreText;
        [SerializeField] TMP_Text timerText;
        [SerializeField, Min(1)] float roundDuration = 30f;
        public float TimeRemaining { get; private set; }

        void Start()
        {
            TimeRemaining = roundDuration;
            RefreshHud();
        }

        void Update()
        {
            TimeRemaining = Mathf.Max(0, TimeRemaining - Time.deltaTime);
            RefreshHud();
        }

        void RefreshHud()
        {
            scoreText.text = $"{progress.GetCollectedCount()} / {progress.GetRequiredCollectibles()}";
            timerText.text = $"{TimeRemaining:0.0}";
        }
    }
}

Update는 경과 시간을 빼고 RefreshHud()를 호출합니다. RefreshHud는 PlayerProgress의 현재 수집 수를 읽어 ScoreText에 씁니다. Mathf.Max(0, …)로 시간 표시가 음수가 되지 않게 합니다.

실제 Week5_MicroLevel에서 첫 보석을 수집한 1교시 완성 화면입니다. 수집 수 1 / 3과 남은 시간이 표시됩니다.
실제 Week5_MicroLevel에서 첫 보석을 수집한 1교시 완성 화면입니다. 수집 수 1 / 3과 남은 시간이 표시됩니다.
여기까지 확인

Play를 끄고 Week07_HUD를 저장합니다. 2교시에서는 이 HUD에 시작·목표 도착·시간 종료·재시작을 연결합니다.

하나를 바꾸고, 달라진 결과를 설명합니다

기본 동작을 확인한 뒤 한 가지를 선택합니다. A·B는 앞의 HudPractice 연습에서 비교합니다. 이미 6단계까지 마쳤다면 Play를 끄고 HUDDisplay를 잠시 끈 뒤 HudController·AddScoreButton을 켭니다. 실험이 끝나면 다시 HUDDisplay만 켜고 저장합니다.

A · 값만 바꾸면 글자도 바뀔까요?

Play를 끄고 AddScore() 안의 RefreshScore(); 한 줄만 // RefreshScore();로 바꿉니다. Start 안의 호출은 유지합니다. 저장하고 실행해 버튼을 세 번 누릅니다.

화면은 Score: 0에 머뭅니다. Play 중 HudController를 선택하고 Inspector 우측 위 메뉴에서 Debug로 전환해 private score 값이 3인지 봅니다. 확인 후 Inspector를 Normal로 되돌리고 Play를 끈 뒤 주석을 제거합니다.

설명할 것: 클릭은 전달되었는데 글자만 그대로인 이유는 무엇인가요?

B · 표시 자릿수를 바꾸면 시간도 달라질까요?

RefreshTime()의 {timeRemaining:0.0}을 {timeRemaining:0}으로 바꾸고 실행합니다. 소수 부분이 사라져 정수로 보이지만, 내부의 시간은 계속 소수 단위로 줄어듭니다. 표시값은 반올림되므로 실제 0보다 먼저 0으로 보일 수도 있습니다.

Play를 끄고 :0.0으로 복원합니다. 소수점 한 자리도 표시를 위한 반올림이므로, 종료 판정은 표시 문자열이 아닌 숫자 값으로 해야 합니다.

설명할 것: 화면에 0이 보인다는 사실만으로 실제 값이 정확히 0이라고 말할 수 있을까요?

C · 기준 모서리만 바꾸면 어떤 일이 생길까요?

Play를 끄고 ScoreText의 Anchor Min·Max를 (0.5, 0.5)로 바꾼 다음 Pos를 (124, -52)으로 다시 입력합니다. Pivot은 (0, 1)을 유지합니다. 16:9와 4:3을 비교하면 글자가 화면 중앙을 기준으로 놓입니다.

실험 후 Anchor Min·Max를 (0, 1)로 돌리고 Pos를 (124, -52)으로 다시 입력합니다.

설명할 것: 같은 Pos 값이어도 Anchor가 다르면 위치가 달라지는 이유는 무엇인가요?

한 문장으로 기록

“나는 ______을 바꾸었다. ______할 것으로 예상했고, 실제로 ______했다. 그 이유는 ______이다.” 결과와 설정을 함께 설명합니다.

막혔을 때는 값·참조·호출을 나누어 확인합니다

증상에 맞는 항목을 펼칩니다. Console의 첫 번째 빨간 오류와 현재 선택한 오브젝트부터 확인합니다.

Game 뷰에 글자가 보이지 않습니다

ScoreText가 활성화되어 있고 Canvas의 자식인지 봅니다. 두 Text를 HUD 그룹으로 묶는 단계까지 마쳤다면 Canvas → HUD → ScoreText 순서입니다. Canvas는 Screen Space - Overlay인지 확인하고, HUD를 사용한다면 부모 전체를 채우는지도 봅니다. Text의 색상·Alpha, Rect Transform의 Width·Height, Font Asset도 확인합니다. TMP Essentials를 가져오지 않았다면 Window → TextMeshPro → Import TMP Essential Resources를 실행합니다.

Hud Practice를 붙일 수 없거나 Score Text 칸이 없습니다

파일 이름과 클래스 이름이 모두 HudPractice인지, Console에 컴파일 오류가 없는지 확인합니다. Inspector에서 Project의 스크립트 파일이 아니라 Hierarchy의 HudController를 선택합니다. 파일을 저장한 뒤 Unity가 컴파일을 마쳐야 새 필드가 나타납니다.

NullReferenceException이 나옵니다

오류를 더블클릭해 scoreText.text인지 timerText.text인지 봅니다. Play를 끄고 해당 Inspector 칸의 None을 올바른 Text 오브젝트로 채웁니다. 이름을 맞추는 것만으로 참조가 연결되지는 않습니다.

버튼이 눌리지 않거나 한 번에 2씩 오릅니다

Play 중인지, EventSystem이 하나 있는지, Input System UI Input Module의 UI Actions가 연결되어 있는지 확인합니다. Canvas의 Graphic Raycaster와 Button의 Interactable도 켜져 있어야 합니다. 클릭되는데 점수가 그대로면 On Click의 대상과 함수, AddScore의 RefreshScore 호출을 봅니다. 2씩 오르면 On Click에 같은 함수가 두 번 연결되어 있는지 확인합니다.

타이머가 줄지 않거나 시작 시간이 다릅니다

TimerText 참조, Hud Practice 컴포넌트의 활성 상태, Console 오류를 확인합니다. 이 공통 출발 씬의 5주차 코드는 게임 시간을 멈추지 않습니다. 기존 실험용 GameManager가 남았다면 현재 씬에서 중복 실행되는지 확인합니다. 시작 시간을 바꿀 때는 Inspector의 Round Duration을 바꿉니다. 이미 저장된 컴포넌트의 값은 코드의 기본값을 고쳐도 자동으로 바뀌지 않습니다.

한글이 네모로 보입니다

기본 TMP Font Asset에 한글 글리프가 없을 수 있습니다. 이번 단계는 Score, Time, +1 Score로 확인합니다. 한글을 사용하려면 해당 글자가 포함된 Font Asset을 준비해 Text 컴포넌트에 연결합니다.

완성한 HUD를 실제 레벨에서 확인합니다

새로 Play한 뒤 수집·복귀·표시 순서를 점검합니다.

1교시 완료 확인
확인할 행동기대 결과
새로 Play하기점수 0 / 3, 시간 30.0에서 시작합니다. 보석 세 개가 있습니다.
보석 하나에 접촉해당 보석만 사라지고 1 / 3으로 바뀝니다.
가시 접촉시작점 복귀, 수집 수 유지, 시간 계속 감소.
HUDDisplay의 Round Duration을 5로 바꾸고 실행0.0에서 멈추며 음수가 되지 않습니다. 종료 판정은 다음 교시에서 추가합니다.
16:9와 4:3 비교양쪽 모서리의 HUD가 잘리거나 겹치지 않습니다.
Play 종료 후 설정 복원Round Duration 30. HudController·AddScoreButton은 꺼지고 HUDDisplay만 활성화. Week07_HUD 저장.
여기까지 확인

보석 → PlayerProgress의 수집 수 변경 → HUDDisplay가 값 읽기 → ScoreText 갱신 순서를 설명할 수 있습니다.

Unity 공식 자료

이번 수업의 화면 배치와 코드 연결을 확인할 때 참고합니다.