Heads up display

The final piece our game needs is a User Interface (UI) to display things like score, a "game over" message, and a restart button.

Create a new scene, and add a CanvasLayer node named HUD. "HUD" stands for "heads-up display", an informational display that appears as an overlay on top of the game view.

CanvasLayer 노드는 게임 위 레이어에 우리의 UI 요소들을 그릴수 있게 해주고, 이로써 보여지는 정보가 플레이어나 몹과 같은 게임 요소들에 의해 가려지지 않게 만듭니다.

HUD는 다음 정보를 표시해야 합니다:

  • ScoreTimer에 의해 변경되는 점수(Score).

  • "Game Over"나 "Get Ready!"와 같은 메시지

  • 게임을 시작하기 위한 "Start" 버튼.

UI 요소의 기초가 되는 노드는 Control입니다. UI를 만들기 위해, 우리는 두 가지 타입의 Control 노드: LabelButton을 사용할 것입니다.

다음 노드들을 HUD 노드의 자식으로 추가하세요:

  • ScoreLabel로 이름지은 Label.

  • Message로 이름지은 Label.

  • StartButton으로 이름지은 Button.

  • MessageTimer로 이름지은 Timer.

ScoreLabel을 클릭하고 인스펙터(Inspecter)의 Text 필드에 숫자를 입력하세요. Control 노드의 기본 폰트는 작아서 크기 조정이 잘 되지 않습니다. 게임 에셋에는 "Xolonium-Regular.ttf"라는 폰트 파일이 있습니다. 이 폰트를 사용하려면, 각 Control 노드마다 다음과 같이 하세요:

  1. Under Theme overrides > Fonts click on the empty box and select "New DynamicFont"

../../_images/custom_font1.png
  1. Click on the "DynamicFont" you added, and under Font > FontData, choose "Load" and select the "Xolonium-Regular.ttf" file.

../../_images/custom_font2.png

Set the "Size" property under Settings, 64 works well.

../../_images/custom_font3.png

Once you've done this on the ScoreLabel, you can click the down arrow next to the Font property and choose "Copy", then "Paste" it in the same place on the other two Control nodes.

참고

Anchors and Margins: Control nodes have a position and size, but they also have anchors and margins. Anchors define the origin - the reference point for the edges of the node. Margins update automatically when you move or resize a control node. They represent the distance from the control node's edges to its anchor.

아래에 보이는 대로 노드를 배열하세요. "레이아웃(Layout)" 버튼을 클릭해서 Control 노드의 레이아웃을 설정하세요:

../../_images/ui_anchor.png

여러분은 노드를 드래그해서 수동으로 놓을 수도 있지만 더 정확하게 하고 싶다면 다음 설정을 사용하세요:

ScoreLabel

  • 레이아웃(Layout) : "위쪽 넓게(Top Wide)"

  • Text : 0

  • Align : "Center"

메시지

  • 레이아웃(Layout) : "수평선 중앙 넓게(HCenter Wide)"

  • Text : Dodge the Creeps!

  • Align : "Center"

  • Autowrap : "On"

StartButton

  • Text : Start

  • 레이아웃(Layout) : "아래쪽 중앙(Center Bottom)"

  • Margin :

    • Top: -200

    • Bottom: -100

MessageTimer에서 Wait Time2로 설정하고 One Shot 속성을 "사용(On)"으로 설정합니다.

이제 HUD에 이 스크립트를 추가하세요:

extends CanvasLayer

signal start_game

start_game 시그널은 Main 노드에게 버튼이 눌려졌음을 알립니다.

func show_message(text):
    $Message.text = text
    $Message.show()
    $MessageTimer.start()

이 함수는 "Get Ready"와 같은 메시지를 일시적으로 표시하고 싶을 때 호출됩니다.

func show_game_over():
    show_message("Game Over")
    # Wait until the MessageTimer has counted down.
    yield($MessageTimer, "timeout")

    $Message.text = "Dodge the\nCreeps!"
    $Message.show()
    # Make a one-shot timer and wait for it to finish.
    yield(get_tree().create_timer(1), "timeout")
    $StartButton.show()

이 함수는 플레이어가 패배했을 때 호출됩니다. 이 함수는 2초동안 "Game Over"를 보여주고, 타이틀 화면으로 돌아와서, 잠깐 일시정지한 후 "Start" 버튼을 보여줍니다.

참고

잠시 일시 정지를 해야 한다면 타이머 노드를 사용하는 대신 SceneTree의 create_timer() 함수를 사용하는 것이 좋습니다. 이 함수는 "Start" 버튼을 표시하기 전에 잠시 기다려야 하는 위의 코드와 같이 지연을 추가하는 데 매우 유용할 수 있습니다.

func update_score(score):
    $ScoreLabel.text = str(score)

이 함수는 점수가 변경될 때마다 Main에 의해서 호출됩니다.

MessageTimertimeout() 시그널과 StartButtonpressed() 시그널을 연결하고 다음 코드를 새 함수에 추가하세요:

func _on_StartButton_pressed():
    $StartButton.hide()
    emit_signal("start_game")

func _on_MessageTimer_timeout():
    $Message.hide()

메인에 HUD를 연결하기

이제 HUD 씬 만들기가 끝났으니 저장하고 다시 Main으로 돌아가세요. Player 씬에서 했듯이 HUD 씬을 Main에 인스턴스화하고, 트리의 맨 아래에 배치하세요. 모든 트리는 다음처럼 보여야 하므로, 놓친 것이 없는지 확인해보세요:

../../_images/completed_main_scene.png

이제 HUD 기능을 Main 스크립트에 연결할 것입니다. 여기에는 Main 씬에 추가적인 것들이 요구됩니다:

Node 탭에서 "시그널을 메서드에 연결(Connect a Signal to a Method)" 창의 "받는 메서드(Receiver Method)"에 "new_game"을 입력해 HUD의 start_game 신호를 Main 노드의 new_game() 함수에 연결하세요. 이제 스크립트의 func new_game() 옆에 녹색 연결 아이콘이 나타나는지 확인하세요.

new_game()에서, 점수 화면을 업데이트하고 "Get Ready" 메시지를 보이게 합니다:

$HUD.update_score(score)
$HUD.show_message("Get Ready")

game_over()에서는, 해당 HUD 함수를 호출해야 합니다:

$HUD.show_game_over()

마지막으로, _on_ScoreTimer_timeout()을 추가해서 점수가 바뀌면 화면과 일치하도록 합니다:

$HUD.update_score(score)

이제 실행할 준비가 끝났습니다! "프로젝트 실행" 버튼을 누르세요. 메인 씬을 선택하라고 물어보면 Main.tscn을 골라줍시다.

오래된 적들 제거하기

"Game Over"까지 플레이한 다음 바로 새 게임을 시작하면 이전 게임의 크립이 화면에 계속 표시될 수 있습니다. 새 게임이 시작될 때 모두 사라지면 더 좋을 것입니다. 우리는 모든 몹에게 스스로 제거하도록 지시할 방법이 필요합니다. "그룹(group)" 기능으로 이 작업을 수행할 수 있습니다.

Mob 씬에서 루트 노드를 선택하고 인스펙터 옆에 있는 "노드(Node)" 탭을 클릭하세요(노드의 시그널을 찾을 수 있는 동일한 위치). "시그널(Signals)" 옆에 있는 "그룹(Groups)"을 클릭하고 새 그룹 이름을 입력하고 "추가(Add)"를 클릭하세요.

../../_images/group_tab.png

Now all mobs will be in the "mobs" group. We can then add the following line to the new_game() function in Main:

get_tree().call_group("mobs", "queue_free")

call_group() 함수는 그룹의 모든 노드에서 지정된 함수를 호출합니다. 이 경우 모든 몹에게 자신을 삭제하도록 지시합니다.

The game's mostly done at this point. In the next and last part, we'll polish it a bit by adding a background, looping music, and some keyboard shortcuts.