해당 내용 출처 :) https://docs.godotengine.org/en/stable/getting_started/first_2d_game/index.html
이전 내용:) https://twd0622.tistory.com/89
저번 포스팅에서는 몹을 만들어 보았다.
이번엔 게임의 메인 화면을 만들고 플레이를 해보도록 하겠다.
5장 메인 화면 만들기
5-1 새로운 씬 생성
메인 화면이 될 새로운 씬을 생성하자.
상단 메뉴 '씬' → 새 씬 → 다른노드에 들어가준다.
루트 노드는 'Node'로 생성해 주고 이름을 'Main'으로 변경해주었다.
- Node: 모든 씬 객체의 기본 클래스
- 2D Node를 사용하지 않은 이유는 해당 노드에서는 게임 로직만 처리할 뿐 2D기능이 필요하지 않기 때문이다.
다음은 이전에 만든 플레이어씬 인스턴스를 연결해 줄거다.
씬 메뉴에 있는 체인모양 버튼을 누른다.
그럼 사진처림 자식 씬 인스턴스화 창이 나오게 되는데 player.tscn을 선택하고 열기를 눌르면 된다.
이제 메인 화면에 필요한 자식 노드들을 생성해 줄거다.
'Timer' 노드 3개와 'Marker2D'를 루트 노드의 자식노드로 만들어준다.
'Timer' 노드의 이름은 각각 'MobTimer', 'ScoreTimer', 'StartTimer'로 변경해주고 'Marker2D'의 이름은 'StartPosition'으로 변경해준다.
- Timer: 카운트 다운 타이머
- Marker2D: 편집을 위한 기본 2D 위치 잡는 노드
'MobTimer'는 몹의 생성시간을 정해주고, 'ScoreTimer'는 점수가 올라가는 시간, 'StartTimer'는 게임이 시작하기 전 잠깐 딜레이를 주기 위한 타이머이다.
이제 각 노드들의 기본 설정을 해줄 건데, 먼저 Timer 노드들의 Wait Time을 변경해준다.
Wait Time은 타이머가 카운트다운할 초를 의미한다.
- MobTimer: 0.5s
- ScoreTimer: 1s
- StartTimer: 2s
StartTimer는 Wait Time 밑에 있는 One Shot을 사용으로 체크해준다.
One Shot은 카운트 다운을 반복하지않고 한번만 한다는 의미이다.
'StartPosition'은 Node2D 영역에 Transform을 열고 position을 (240, 450)으로 변경해준다.
5-2 몹 스폰 경로 설정
몹을 스폰 시키기 위해서는 새로운 자식 노드를 생성해야한다.
'Path2D'노드를 추가해주고 이름을 'MobPath'로 변경해준다.
- Path2D: 따라야 할 PathFollow2D 노드에 대한 Curve2D 경로가 포함되어 있습니다.
'MobPath'은 몹이 화면 가장자리에서 생성되게 해줄것이다.
'MobPath'를 누르면 사진처럼 편집기 상단에 새로운 버튼이 생긴다.
출처: https://docs.godotengine.org/ko/4.x/getting_started/first_2d_game/05.the_main_game_scene.html
위 이미지 처럼 포인트를 지정해 화면 모서리를 경로로 잡아준다.
스마트 스냅과 격자 스냅을 켜면 포인트를 잡기 더 쉬워진다.
이제 경로를 지정해줬으니 'PathFollow2D' 노드를 'MobPath'노드의 자식노드로 추가해 주고 이름을 'MobSpawnLocation'으로 변경해준다.
'MobSpawnLocation'은 자동으로 경로를 따라 회전하기때문에 이를 이용해 경로에 따라 임의의 위치와 방향을 선택할 수 있다.
지금까진지 생성한 노드의 모습이다.
5-3 스크립트 작성
이제 스크립트를 작성할 차례다. 'Main' 노드에서 스크립트를 생성해 주자.
스크립트 상단에 전역 변수 2개를 작성하자.
@export var mob_scene : PackedScene
var score
- '@export var mob_scene : PackedScene'을 통해 인스턴스화 하려는 몹의 씬을 가져올 수 있다.
위 코드를 작성하고 저장을 하면 인스펙터 메뉴에 mob_scene가 생긴것을 볼 수 있다.
불러오기를 통해서 'mob.tscn'을 불러오자.
다음으론 'Main'노드 아래 있는 'Player'씬 인스턴스를 클릭하고 노드 메뉴에서 hit() 시그널을 찾아 더블 클릭 한다.
그러면 사진과 같이 시그널을 메서드에 연결하는 창이 나온다.
플레이어가 hit() 신호를 보내면 게임이 끝나도록 하는 함수를 만들고 싶기 때문에, 받는 메서드에 'game_over'를 적어서 연결을 눌러준다.
생성된 'game_over'함수에 아래 코드를 입력
# Player씬에서 hit() 신호를 보낼 경우
func game_over() -> void:
$ScoreTimer.stop()
$MobTimer.stop()
게임이 끝나면 점수가 오르는 것과 몹 스폰이 중지된다.
그라고 게임이 시작되는 함수도 만들어 준다.
func new_game():
score = 0; # 점수 초기화
$Player.start($StartPosition.position) # 플레이어 위치 설정
$StartTimer.start() # 2초 대기
이제 'Main'노드 아래 있는 Timer 노드들(StartTimer, ScoreTimer, MobTimer)의 'timeout()' 시그널을 연결해 준다.
'StartTimer'가 끝나면 'MobTimer'와 'ScoreTimer'를 시작시키고, 'ScoreTimer'는 끝날때 마다 score를 +1 시켜준다.
func _on_start_timer_timeout() -> void:
$MobTimer.start()
$ScoreTimer.start()
func _on_score_timer_timeout() -> void:
score += 1
'MobTimer'가 끝나면, 몹 인스턴스를 생성해주고, 'Path2D에 따라' 랜덤으로 시작위치를 지정하고 몹의 애니메이션을 선택한다.
'PathFollow2D'노드는 경로를 따라가면서 자동으로 회전하기 때문에, 이를 이용해 몹의 방향과 위치를 저정한다.
아래의 코드를 추가하자.
func _on_mob_timer_timeout() -> void:
var mob = mob_scene.instantiate() # 몹 인스턴스 생성
# 몹 위치
var mob_spawn_location = $MobPath/MobSpawnLocation
mob_spawn_location.progress_ratio = randf() # MobPath에서 랜덤으로 지정
# 몹 방향
var direction = mob_spawn_location.rotation + PI / 2 # 몹의 방향을 경로의 수직으로 설정
direction += randf_range(-PI / 4, PI / 4) # 방향을 랜덤으로 살짝 병경해준다.
# 몹 속도
var velocity = Vector2(randf_range(150.0, 250.0), 0.0) # 몹의 속도 랜덤 지정
# 몹 인스턴스에 설정한 값들 부여
mob.position = mob_spawn_location.position
mob.rotation = direction
mob.liner_velocity = velocity.rotated(direction)
# 메인 화면에 몹 생성
add_child(mob)
- 게임의 재미를 위해서 몹의 속도는 150.0 ~ 250.0 사이로 지정해 주었다.
- GodotEngine은 각도가 필요할 때 각도가 아닌 라디안(radians)을 사용한다.
- PI는 약 3.1415º 이고 2 * PI = TAU 이다.
- 각도로 작업하는게 편하다면 deg_to_rad() 및 rad_to_deg() 함수를 사용해서 두 단위를 변환할 수 있다.
- 몹의 방향은 기본적으로 수직으로 설정해준뒤 일정 범위내에서 약간 변화를 주었다.
5-4 게임 테스트
이제 메인화면이 완성 되었다.
_ready() 함수에 'new_game()' 함수를 호출해 보자.
func _ready() -> void:
new_game()
또한 메인 스크립트를 'Main'씬으로 변경해주어야한다.
FileSystem메뉴에서 main.tscn에 오른쪽 마우스를 클릭해 메인 씬으로 설정을 눌러준다.
이제 실행해보자.
테스트를 할때 3가지를 확인해야 된다.
- 플레이어가 움직이는지
- 몹이 랜덤으로 스폰되는지
- 플레이어가 몹에 닿으면 플레이어가 사라지는지
테스트가 무사히 끝났다면 _ready() 함수에서 'new_game()'을 지우고 pass를 넣어준다.
이제 메인화면 만들기가 모두 끝났다.
메인 스크립트 전체
extends Node
@export var mob_scene : PackedScene
var score
func _ready() -> void:
# new_game()
pass
func _process(delta: float) -> void:
pass
# Player씬에서 hit() 신호를 보낼 경우
func game_over() -> void:
$ScoreTimer.stop()
$MobTimer.stop()
func new_game():
score = 0; # 점수 초기화
$Player.start($StartPosition.position) # 플레이어 위치 설정
$StartTimer.start() # 2초 대기
func _on_start_timer_timeout() -> void:
$MobTimer.start()
$ScoreTimer.start()
func _on_score_timer_timeout() -> void:
score += 1
func _on_mob_timer_timeout() -> void:
var mob = mob_scene.instantiate() # 몹 인스턴스 생성
# 몹 위치
var mob_spawn_location = $MobPath/MobSpawnLocation
mob_spawn_location.progress_ratio = randf() # MobPath에서 랜덤으로 지정
# 몹 방향
var direction = mob_spawn_location.rotation + PI / 2 # 몹의 방향을 경로의 수직으로 설정
direction += randf_range(-PI / 4, PI / 4) # 방향을 랜덤으로 살짝 병경해준다.
# 몹 속도
var velocity = Vector2(randf_range(150.0, 250.0), 0.0) # 몹의 속도 랜덤 지정
# 몹 인스턴스에 설정한 값들 부여
mob.position = mob_spawn_location.position
mob.rotation = direction
mob.linear_velocity = velocity.rotated(direction)
# 메인 화면에 몹 생성
add_child(mob)
게임은 작동 되지만 부족한 점이 있다면, 바로 사용자 인터페이스이다(UI).
다음 포스팅에서는 타이틀 화면을 추가하고 플레이어 점수를 표시해보겠다.
다음 포스팅 :) https://twd0622.tistory.com/91
'Godot Engine > 게임 제작' 카테고리의 다른 글
[GodotEngine] Tutorial 2D Game - 7 [完] (2) | 2025.01.02 |
---|---|
[GodotEngine] Tutorial 2D Game - 6 (1) | 2024.12.27 |
[GodotEngine] Tutorial 2D Game - 4 (0) | 2024.12.19 |
[GodotEngine] Tutorial 2D Game - 3 (2) | 2024.12.16 |
[GodotEngine] Tutorial 2D Game - 2 (0) | 2024.12.11 |