퐁
튜토리얼에서 간단한 게임인 퐁을 만들어 볼 겁니다. 많은 데모가 엔진에 포함되어 있습니다만, 2D 게임의 기본적인 기능성을 소개해줄 겁니다.
자 시작해보죠, 고도엔진을 켜서 프로젝트를 시작해주세요.
에셋
몇몇 에셋이 튜토리얼을 위해 포함되어 있습니다 :
여러분의 프로젝트 폴더에 내용물을 압축 해제하세요.
씬 설정
오랜 전통에 따라, 게임은 640x400 픽셀 해상도를 지닙니다. 해상도는 Project Setting (프로젝트 설정을 보세요.)의 Scene/Project 세팅 메뉴 하위에서 설정할 수 있습니다. 기본 배경 색은 검은색입니다 :
프로젝트 루트를 위해서 Node2D를 생성하세요. Node2D는 2D 엔진의 기본 타입입니다. 이후, 왼쪽 혹은 오른쪽 패들에 몇몇 스프라이트(스프라이트 노드)를 더하세요, 구분하는 부분과 공을 추가합니다. 각 노드에 이름을 줄 수 있습니다. Inspector에서 각 스프라이트의 텍스쳐를 설정합니다.
노드 포지션을 설정하기:
- "왼쪽" 노드: (67, 183)
- "오른쪽" 노드: (577, 187)
- "구분하는 부분" 노드: (320, 200)
- "공" 노드: (320, 188)

씬을 "pong.tscn"으로 저장하고 프로젝트 속성에서 메인 씬으로 설정합니다.
입력 행동 설정
비디오 게임은 다양한 입력 방법을 사용해서 플레이 가능합니다: 키보드, 조이패드, 마우스, 터치스크린(멀티터치)... 고도엔진은 이 모두를 사용할 수 있습니다. 그러나, 여러분이 별도로 관리하는 하드웨어 작업 대신에 "Input Actions"로 입력을 정의하는 것이 흥미로울 것입니다. 이 방법으로는, 어떤 입력 방법도 사용할 수 있습니다: 여러분이 직접 정의한 게임 행동이 여러분이 설정한 버튼에 연결되어 있게만 하면 됩니다.
이 게임은 퐁입니다. 유일한 입력은 패드가 위, 아래로만 움직이면 되겠죠.
프로젝트 설정 대화 상자를 열어주세요(Scene/Project 설정), 이번에는 "Input Map" 탭으로 가볼 시간입니다.
이 탭에서, 4개의 행동을 추가해주세요:
left_move_up
, left_move_down
, right_move_up
, right_move_down
원하는 키에 할당하면 됩니다. A/Z(왼쪽 플레이어) 그리고 위/아래(오른쪽 플레이어)를 대부분의 경우에 사용합니다.

스크립트
extends Node2D func _ready(): pass
우선 우리는 몇몇 유용한 변수를 저장할 수 있도록 우리의 스크립트를 위한 몇몇 멤버를 정의해야 합니다. 이러한 값은 화면의 치수, 패드 및 공의 초기 방향입니다.
extends Node2D # Member variables var screen_size var pad_size var direction = Vector2(1.0, 0.0) func _ready(): pass
이미 알고 있듯이, _ready() 함수는 첫번째로 호출되는 함수입니다(여기서는 필요 없는 _enter_tree() 다음입니다). 이 함수 안에서는, 두 가지를 할 수 있습니다. 먼저 프로세싱을 허락할 수 있습니다: set_process(true) 함수를 하는 목적입니다. 두 번째로는 두 멤버 변수를 초기화 할 수 있습니다.
extends Node2D # Member variables var screen_size var pad_size var direction = Vector2(1.0, 0.0) func _ready(): screen_size = get_viewport_rect().size pad_size = get_node("left").get_texture().get_size() set_process(true)
우리는 패드 노드 중 하나를 가져와서 pad_size를 초기화 했고, 그것의 텍스쳐 사이즈를 얻었습니다. screen_size는 게임 화면과 일치하는 Rect를 반환하고 그 크기를 저장하는 get_viewport_rect()를 사용해서 초기화했습니다.
자 이제, 우리의 공을 움직이게 하기 위해 우리의 스크립트에 다른 멤버들을 추가할 필요가 있습니다.
extends Node2D # Member variables var screen_size var pad_size var direction = Vector2(1.0, 0.0) # Constant for ball speed (in pixels/second) const INITIAL_BALL_SPEED = 80 # Speed of the ball (also in pixels/second) var ball_speed = INITIAL_BALL_SPEED # Constant for pads speed const PAD_SPEED = 150 func _ready(): screen_size = get_viewport_rect().size pad_size = get_node("left").get_texture().get_size() set_process(true)
결국엔 _process() 함수입니다. 아래의 모든 코드는 이 함수 안에 포함되어 있습니다.
우리는 계산을 위해 몇몇 변수들을 초기화해야 합니다. 먼저 공의 위치이고(노드에서), 두 번째는 각 패드를 위한 사각형입니다(Rect2). 이 사각형들은 패드와 공의 충돌 판정을 위해 사용됩니다. 기본적으로 스프라이트는 텍스쳐를 중심으로 배치하므로, pad_size/2의 작은 조정을 추가해야 합니다.
func _process(delta): var ball_pos = get_node("ball").get_pos() var left_rect = Rect2( get_node("left").get_pos() - pad_size*0.5, pad_size ) var right_rect = Rect2( get_node("right").get_pos() - pad_size*0.5, pad_size )
이제, 공의 작은 움직임을 _process() 함수에서 추가해봅시다. 공의 위치는 ball_pos 변수에 있기 때문에, 합산은 간단합니다 :
# Integrate new ball position ball_pos += direction * ball_speed * delta
이 코드 라인은 _process() 함수의 각 반복 마다 호출됩니다. 이는 각각의 새로운 프레임에서 공의 위치가 업데이트됨을 의미합니다.
이제 공은 새로운 위치를 가지는 데, 우리는 이제 창의 경계나 패드에 공이 충돌하는 지를 알아야 합니다. 먼저 바닥과 천장입니다 :
# Flip when touching roof or floor if ((ball_pos.y < 0 and direction.y < 0) or (ball_pos.y > screen_size.y and direction.y > 0)): direction.y = -direction.y
두 번째로, 패드입니다: 만약 하나의 패드가 건드려진다면, 우리는 공의 X축 방향을 반대로 바꾸어야 합니다. 그리고 랜덤한 Y방향을 randf() 함수를 이용해서 주어야 합니다. 우리는 스피드를 조금 높여야 할 필요도 있습니다.
# Flip, change direction and increase speed when touching pads if ((left_rect.has_point(ball_pos) and direction.x < 0) or (right_rect.has_point(ball_pos) and direction.x > 0)): direction.x = -direction.x direction.y = randf()*2.0 - 1 direction = direction.normalized() ball_speed *= 1.1
마지막으로, 만약 공이 화면 바깥으로 나간다면 게임이 종료됩니다. 이것은 공의 X 위치가 0보다 작거나 화면의 크기보다 클 때입니다. 그렇다면 게임을 다시 시작하죠 :
# Check gameover if (ball_pos.x < 0 or ball_pos.x > screen_size.x): ball_pos = screen_size*0.5 ball_speed = INITIAL_BALL_SPEED direction = Vector2(-1, 0)
이 모든것이 완료되면, 노드는 공의 새 위치를 다음과 같이 계산합니다 :
get_node("ball").set_pos(ball_pos)
다음으로, 우리는 패드를 움직이는 것을 허용합니다. 우리는 유저의 입력에 관해서만 이들의 위치를 움직여야 합니다. 이것은 입력 클래스를 이용해 가능합니다 :
# Move left pad var left_pos = get_node("left").get_pos() if (left_pos.y > 0 and Input.is_action_pressed("left_move_up")): left_pos.y += -PAD_SPEED * delta if (left_pos.y < screen_size.y and Input.is_action_pressed("left_move_down")): left_pos.y += PAD_SPEED * delta get_node("left").set_pos(left_pos) # Move right pad var right_pos = get_node("right").get_pos() if (right_pos.y > 0 and Input.is_action_pressed("right_move_up")): right_pos.y += -PAD_SPEED * delta if (right_pos.y < screen_size.y and Input.is_action_pressed("right_move_down")): right_pos.y += PAD_SPEED * delta get_node("right").set_pos(right_pos)
우리는 위에 있는 설정 섹션에서 사전 정의 된 4개의 행동을 사용합니다. 플레이어가 대표 키를 누르면, 그에 일치하는 행동이 촉발됩니다. 키를 누를 때 마다, 원하는 방향 쪽으로 새 위치를 간단하게 계산하고 노드에 적용합니다.
됐습니다!! 몇 개의 줄로 간단한 퐁을 만들어버렸어요.
'레퍼런스 > 고도엔진' 카테고리의 다른 글
고도엔진 튜토리얼 #8 화면을 뿌리다(Splash screen). (0) | 2018.02.01 |
---|---|
고도엔진 튜토리얼 #7 GUI 튜토리얼(GUI tutorial) (0) | 2018.02.01 |
고도엔진 튜토리얼 #5 스크립팅 - 이어서(Scripting (continued)) (0) | 2018.01.30 |
고도엔진 튜토리얼 #4 스크립팅(Scripting) (0) | 2018.01.30 |
고도엔진 튜토리얼 #3 인스턴스화 - 이어서(Instnacing (continued)) (0) | 2018.01.30 |