티스토리 뷰

728x90

고도 데모에 있는 2D 리듬 게임을 분석하며 스터디를 해볼까 한다. 위의 그림은 게임의 실행 화면으로 [Go] 버튼을 클릭하면 게임을 시작한다. Cephalopod, Disco Lounge, Werq라는 3개의 트랙이 있는데 그중에서 Cephalopod를 실행하고 있는 화면이다. 곡을 바꾸려면 [Go] 버튼을 누르기 전에 상단의 사각형 박스를 좌측 또는 우측으로 드래그하면 된다. 기본 분석 내용은 아래와 같다.

 

■ 프로젝트 이름 : 2D Rhythm Game

■ 프로젝트 파일 :

godot-2d-rhythm.zip
14.17MB


■ 메인 씬 : RhythmGameDemo.tscn
■ 자동 로드 : Events="*res://RhythmGame/Autoload/Events.gd"
■ 입력 맵 :
☞ touch : BUTTON_LEFT

 

입력 맵에서 확인할 수 있듯이 리듬 게임은 터치로 모든 동작을 수행한다. 데스크톱에서는 마우스 좌측 버튼으로 게임을 진행할 수 있다.

 

숫자가 차례로 나오는데 각 숫자를 터치하는 시점은 리듬에 맞추어 숫자 주위에 있는 흰색 선이 원에 딱 맞게 줄어드는 순간이다. 정확도에 따라 Perfect > Great > Ok 점수를 부여한다. 숫자 간에 경로가 있는 경우는 드래그로 경로를 따라가면 된다.

 

위의 그림은 리듬 게임의 씬 트리를 그래프로 만든 것이다. Patterns 씬 아래에 세 가지 음악 트랙인 Cephalopod, Disco Lounge, Werq 각각이 하나의 씬으로 제작되고 각 씬은 PlacerHitBeat, PlacerRest, PlacerHitRoller 씬의 인스턴스로 구성되고 있음을 확인할 수 있다. 만약 새로운 곡을 추가하고 싶다면 Tracks 폴더에 새로운 곡을 위한 폴더를 추가하고 앞서 언급한 세 가지 씬의 인스턴스를 추가하는 방법으로 게임을 추가할 수 있다.

 

다음은 각 씬 또는 스크립트에서 받으려고 설정한 이벤트와 사용자 시그널을 발생시키는 부분을 정리한 것이다. 이벤트의 흐름을 따라가다 보면 게임의 전체적인 동작을 어렵지 않게 파악할 수 있기 때문이다. ▶표시는 씬에서 설정한 이벤트로 이벤트 이름과 해당 이벤트가 발생했을 때 처리하는 함수를 확인할 수 있다. ▷표시는 스크립트에서 설정한 이벤트로 코루틴에서 yield()로 이벤트를 대기하는 것을 포함한다. →표시는 스크립트에서 시그널을 발생시키는 것으로 시그널 이름과 발생 함수를 확인할 수 있다. 이벤트 및 시그널 처리가 있는 씬만 표시한다.

 

■ 이벤트 목록 :
☞ RhythmGameDemo(RhythmGameDemo.tscn, RhythmGame/HitSpawner.gd, Node2D)

    씬 로드 시 _generate_stacks()를 통해서 Patterns 씬이 가지고 있는 각 트랙을 각각의 스택으로 생성하고 스택에 비트 정보를 담아 둔다. Patterns 씬은 해제.
 ▷ "scored"(Events) --> "_create_score_fx"(self)

    점수 처리 시 Miss, Great, Ok, Perfect 표시
 ▷ "track_selected"(./Synchronizer>Events) --> "_load_track"(self)

    트랙을 로드하고 플레이 
 ▷ "timeout"(./Synchronizer>get_tree().create_timer(time_delay)) --> play_audio(yield)

    트랙을 로드하고 플레이 전에 오디오 서버 자료로 대기 시간 계산 잠시 멈춤
 → Events."beat_incremented" from(./Synchronizer>_process)

    트랙의 비트 간격을 계산해서 비트 발생
 ▷ "beat_incremented"(./HitSpawner>Events) --> "_spawn_beat"(self)

    비트에 따라 스택에서 항목을 하나 꺼내서 씬 인스턴스를 생성하고 동작시킨다. 원 터치 또는 드래그 항목의 시작. 
 → Events."track_finished" from(./HitSpawner>_spawn_beat)

    스택이 비어 더 이상 동작시킬 것이 없을 때 트랙 종료 통보

 ▷ "track_selected"(./HitSpawner>Events) --> "_select_stack"(self)

    트랙을 변경한 경우 지정한 이름의 스택을 현재 스택으로 선택

 

☞ HitBeat(RhythmGame/Hits/HitBeat.tscn, RhythmGame/Hits/HitBeat.gd, Node2D)
 ▶ "input_event"(Area2D) --> "_on_Area2D_input_event"(self)

    터치인 경우 반지름으로 점수 계산 및 처리, 항목은 종료 처리
 → Events."scored" from(_on_Area2D_input_event)
    터치인 경우 반지름으로 점수 계산 및 처리 통보

 → Events."scored" from(_process)

    터치 없이 반지름이 줄어든 경우로 0점 처리 통보 및 항목은 종료 처리

 

☞ HitRoller(RhythmGame/Hits/HitRoller/HitRoller.tscn, RhythmGame/Hits/HitRoller/Roller.gd, Path2D)
 ▶ "timeout"(GrowingLine2D/GrowthTimer) --> "_on_GrowthTimer_timeout"(GrowingLine2D)

    타이머 시그널에 따라 선을 그려간다. 선을 모두 그렸으면 타이머 동료
 ▶ "movement_finished"(RollerFollow) --> "destroy"(self)

    종료 애니메이션 플레이
 ▶ "movement_finished"(RollerFollow) --> "submit_score"(RollerFollow/Roller)

 → Events."scored" from(RollerFollow/Roller>submit_score)
    점수 처리 통보

 ▶ "mouse_entered"(RollerFollow/Roller) --> "_on_mouse_entered"(RollerFollow/Roller)

    경로 안에 있음 플래그 켜기
 ▶ "mouse_exited"(RollerFollow/Roller) --> "_on_mouse_exited"(RollerFollow/Roller)

    경로 안에 있음 플래그 끄기
 ▶ "timeout"(RollerFollow/Roller/ScoreTimer) --> "_on_ScoreTimer_timeout"(RollerFollow/Roller)

    경로 안에서 드래그 중이면 점수 증가
 ▷ "timeout"(./RollerFollow>get_tree().create_timer(delay)) --> start_movement(yield)

    경로를 따라 이동시 지정 대기 시간만큼 잠시 멈춤
 ▷ "tween_all_completed"(./RollerFollow>_tween) --> start_movement(yield)

    경로를 따른 모든 이동 감지
 → self."movement_finished" from(./RollerFollow>start_movement)

    경로를 따른 모든 이동 통보

☞ UITrackPlaying(RhythmGame/UI/TrackPlaying/UITrackPlaying.tscn, RhythmGame/UI/TrackPlaying/UITrackPlaying.gd, Control)
 ▷ "track_selected"(Events) --> "_fade_in"(self)

    트랙 개시에 따른 게임 플레이 씬 보이기

☞ UIMetronome(RhythmGame/UI/TrackPlaying/UIMetronome.tscn, RhythmGame/UI/TrackPlaying/UIMetronome.gd, Sprite)
 ▷ "beat_incremented"(Events) --> "_pulse"(self)

    비트를 스피커 움직임으로 표시

☞ UIScore(RhythmGame/UI/TrackPlaying/UIScore.tscn, RhythmGame/UI/TrackPlaying/UIScore.gd, )
 ▷ "scored"(Events) --> "_add_score"(self)

    점수가 도달하면 총합을 더하고 표시

☞ UITrackFinished(RhythmGame/UI/TrackFinished/UITrackFinished.tscn, RhythmGame/UI/TrackFinished/UITrackFinished.gd, Control)
 ▶ "pressed"(ButtonBack) --> "_on_ButtonBack_pressed"(self)

    게임 첫 화면으로 이동
 ▷ "track_finished"(Events) --> "_fade_in"(self)

    트랙 종료에 따른 보이기 애니메이션 수행

☞ UITrackSelector(RhythmGame/UI/TrackSelector/UITrackSelector.tscn, RhythmGame/UI/TrackSelector/SelectArea.gd, Control)
 ▶ "timeout"(TrackCarousel/TrackTiles/AlignTimer) --> "_on_AlignTimer_timeout"(TrackCarousel/TrackTiles)

    트랙을 드래그로 선택할 때 타이머로 사각형을 중앙에 맞추어 주는 애니메이션 수행
 ▶ "dragged"(TrackCarousel/DragDetector) --> "_on_DragDetector_dragged"(TrackCarousel/TrackTiles)

    드래그 신호가 오면 전달된 방향과 강도로 스크롤 수행
 ▶ "input_event"(TrackCarousel/DragDetector) --> "_on_input_event"(TrackCarousel/DragDetector)
 → self."dragged" from(TrackCarousel/DragDetector>_on_input_event)

    터치 상태의 움직임을 감지하여 드래그 시그널 발생(움직인 정도 전달)

 ▶ "area_entered"(TrackCarousel/TrackSelector) --> "_on_area_entered"(TrackCarousel/TrackSelector)

 → self."track_selected" from(TrackCarousel/TrackSelector>_on_area_entered)

    트랙이 바뀌면 트랙이 선택되었다는 시그널 통보

 ▶ "track_selected"(TrackCarousel/TrackSelector) --> "update_track_info"(self)

    기존 트랙을 끝내고 새로운 트랙을 로드하여 플레이
 ▷ "animation_finished"(_animation_player) --> update_track_info(yield)

    기존 트랙을 끝낼 때 애니메이션 종료 대기
 ▶ "track_selected"(TrackCarousel/TrackSelector) --> "_on_track_selected"(TrackCarousel/TrackTiles)

    트랙이 바뀌면 상단 트랙 정보 상자 정렬을 위해 현재 트랙 정보 설정
 ▶ "pressed"(GoButton) --> "_on_GoButton_pressed"(self)

 → Events."track_selected" from(_on_GoButton_pressed)

    트랙 시작 시그널을 통보하고, 현재 오브젝트는 종료 처리

 

리듬 게임의 기본 화면 구조이다. 배경 이미지가 트리의 맨 위에 있고 게임 진행을 수행하는 Synchronizer와 HitSpawner는 중간에 있고 게임의 시작과 종료, 그리고 게임 점수를 표시하는 UI 노드는 맨 아래에 있다. 결과적으로 노드들의 레이어 구조는 노드 트리의 상단으로 갈수록 화면 아래에 배치되고 트리의 하단으로 갈수록 위에 표시된다. 노드 우측에 있는 가시성 토글 아이콘을 클릭하면 보이기 및 숨기기를 간편하게 처리할 수 있는데 이를 통해 노드들이 어떻게 쌓아지는지 직접 확인할 수 있다. UI 노드를 보면 처음에는 트랙을 선택하는 UI만 보이고 나머지는 숨기도록 설정했다가, 게임이 시작되면 트랙을 선택하는 UI는 종료시키고 UITrackPlaying 노드를 보이고 게임이 끝나면 UITrackFinished 노드도 보이는 방식으로 수행한다.

게임이 단순해 보이지만 다양한 기술적 요소를 담고 있다. Area2D, AnimationPlayer, Sprite, ColorRect, Line2D, PathFollow2D, Timer, CollisionShape2D, Tween, TextureButton, CanvasLayer와 TextureRect, Position2D, AudioStreamPlayer, AudioServer, Particles2D 클래스를 비롯하여 다양한 요소들이 있는데 다음 글부터 하나씩 다루어 보고자 한다.

 

 

728x90
댓글
글 보관함
최근에 올라온 글
최근에 달린 댓글
«   2024/05   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31