본문 바로가기

Programming/Godot

고도 엔진 횡스크롤 게임 예제 (Godot Platformer) -1

반응형

 

따라해야할 양이 많다보니 포스팅이 친절하지 못한점 양해바랍니다.^^;;

그럼 시작하겠습니다.


1. 프로젝트 세팅

  - assets 다운로드(예제 소스 링크)

  - platformer폴더 생성 후 assets폴더를 복사해 넣습니다. 
  - assets 폴더에 이미지 등 소스 추가
  - src 폴더 추가

2. Player 생성

  - kinematicbody2D 추가

  - Player로 변경
  - CollisionShape2D추가 ->Rectangle shape 설정
  - Player 이미지를 끌어오면 자동으로 sprite node가 추가되며 이름은 player로 설정됨
  - magnetic을 클릭하여 snap 활성화
  - pixel snap을 활성화하여 맨 바닥이 0에 오도록 설정
  - Collisionshape2D의 크기 조정(캐릭터 이미지보다 약간 작도록 설정, 단 바닥은 0에 닿도록 함) 
  - Player Scene을 src/Actors폴더 안에 저장
  - Player node에 script 추가(Empty template로 설정하여 추가)
  - File menu에서 script 추가하여 Actor.gd(palyer와 enemy가 공유할 base script)를 추가 (player.gd는 현 단계에서는 필요 없음.

------Actor.gd script 1단계-------

extends KinematicBody2D
class_name Actor

const FLOOR_NORMAL: = Vector2.UP #플로어에 있는지에 관한 정보

export var speed: = Vector2(300.0, 1000.0)
export var gravity: = 3000.0

var velocity: = Vector2.ZERO

func _physics_process(delta:float) ->void: #매 프레임마다 수행
	velocity.y += gravity * delta
	#나중에 추가: velocity.y = max(velocity.y, speed.y) //if(velocity.y >speed.y){velocity.y=speed.y}
	velocity = move_and_slide(velocity) #Player 스크립트로 이동

3. Level 생성(2D Node: LevelTemplate) 및 저장

  - Tilemap 추가, Tileset 추가(미리 저장된 것 불러오기, 새로 생성하여 사용 가능)

스텝 사이즈 80으로 설정
Collision지정으로 충돌/정지할 수 있도록 인식영역 설정
셀 사이즈를 타일셋에서 지정한 이미지 사이즈와 동일하게 80으로 설정


  - wall, floor 생성. 적당히 생성 후 Player를 불러와서 실행해본다.

4. 레이서 설정(Editor > Project Settings > 2D Physics)

- 설정의 2d physics 로 가서 Level 설정 추가(어떤 레벨과 반응하는지..충돌 등, 씬 안에있는 instance의 세팅을 변경할 경우 해당 instance만 변경되므로, 해당 씬을 수정해야함)

- Layer Names - 2D Physics
- Layer1: player
- Layer2: enemies
- Layer3: coins
- Layer4: world
-Tilemap: Layer-world, Mask-none
-Player: 

 

예를 들면 TileMap같은 경우에는 Collision > Layer 메뉴의 ... 버튼에서 어떤 레이어인지 직접 지정해줄 수 있다. (마스크는 제외해줘야 함)

 

Player는 우선 4번 (world)를 마스크해서 감지하도록 해야함.

 

5. Project Setting - Input Map

-move_left (A, Left, Devide 0(Stick Left)
-move_right(D, Right, Device 0(Stick Right)
-jump(W, Up, Device 0(PS Cross, xBox A nintendo B)


--------Player.gd script----------
- 1단계 : 키 인식 확인용

extends Actor

func _physics_process(delta: float) -> void:
	var direction: = Vector2(
		Input.get_action_strength("move_right") -
		Input.get_action_strength("move_left"),
		1.0
	)
	velocity = speed * direction
	velocity = move_and_slide(velocity)

 

- 최종 -

extends Actor

export var stomp_impulse: = 1000

func _on_EnemyDetector_area_entered(area: Area2D) ->void:
	_velocity = calculate_stomp_velocity(_velocity, stomp_impulse) #enemy를 밟을때 충격으로 튀어오르는 기능

func _on_EnemyDetector_body_entered(body: PhysicsBody2D) -> void: #enemy와 부딛혔을 때 죽는 기능
	queue_free()

#1단계
func _physics_process(delta: float)->void:
	var is_jump_interrupted: = Input.is_action_just_released("jump") and velocity.y <0.0 #현재 점프상태가 끝나면 jump도 짧게 끝남. 키 누르는 시간에 비례하도록)
	direction: = get_direction()
	velocity = calculate_move_velocity(velocity, direction, speed, is_jump_interrupted) 
	velocity = move_and_slide(velocity, FLOOR_NORMAL) #현재 player가 frame 밖에 있으므로 안쪽으로 옮겨준 뒤 실행가능


#1단계
func get_direction() -> Vector2:
	return Vector2(
		Input.get_action_strength("move_right")-Input.get_action_strength("move_left"), -1.0 if Input.is_action_just_pressed("jump") and is_on_floor() else 1.0) #벽에서 점프한 경우에만 y 방향을 -1로 설정하고, 그 외에는 1로 설정함

#1단계
func calculate_move_velocity(
      linear_velocity: Vector2,
      direction: Vector2,
      speed: Vector2
      is_jump_interrupted: bool
      ) ->Vector2:
	var new_velocity: = linear_velocity
	new_velocity.x = speed.x * direction.x
	new_velocity.y += gravity * get_physics_process_delta_time() //delta값을 호출해줌
	if direction.y == -1.0:
		new_velocity.y = speed.y * direction.y
	if is_jump_interrupted:
		new_velocity.y = 0.0
	return new_velocity

func calculate_stomp_velocity(linear_velocity: vector2, impulse: float) -> Vector2:
	var out: = linear_velocity
	out.y = -impulse
	return out

  - player와 enemy속도 차이를  위해 player 600 설정
  -  Jump 함수 추가(방향함수를 추가하여 가독성 향상)
  - Jump 함수에 FLOOR_NORMAL 상수를 추가로 전달하여야 JUMP가 가능함
  - new_velocity 변수 일괄 변경: 선택-> Ctrl+R(대체)->out
  - Ctrl+Shift+F: 파일 전체(전체프로젝트)에서 찾기: velocity찾기->whole word체크(전체일치)->replace->_velocity 로 변경(private:해당 script/class에서만 쓸 수 있음)

6. Enemy

  - Player.tscn -> duplicate: 이름 변경 to Enemy, sprite 이름 enemy
  - enemy.png를 sprite texture에 놓는다.
  - Collisionshape2D  사이즈 변경

  - extend script - actor.gd - enemy.gd생성

Actor.gd를 상속하고, 이름은 Enemy.gd로 하여 생성

- layer를 enemy layer로, mask를 player/world로


<enemy.gd 스크립트>

extends "res://src/Actors/Actor.gd"

func _ready() ->void:
	set_physices_process(false) #enemy가 밖에 있을 때 작동하지 않도록 하는 기능
	_velocity.x = -speed.x 

func _on_StompDetector_body_entered(body:
		PhysicsBody2D) ->void:
	if body.global_positioy.y < get_node("StompDetector").global_position.y: 
		return
	get_node("CollisionShape2D").disabled = true # 죽이고도 내가 또 죽는 오류를 방지
	queue_free()	# 적 삭제

func _physics_process(delta: float) ->void: #1단계
	_velocity.y +=gravity * delta
	if is_on_wall(): #1단계
		_velocity.x *= -1.0 #1단계
		_velocity.y = move_and_slice(_velocity, FLOOR_NORMAL).y #1단계

  - Frame 밖에 있을 때는 작동하지 않도록 하는 기능 ( 노드 추가: VisibilityEnabler2D )

  - physics Process Par: on,  process parent: on

- "physics Process Par: on" 옵션은 화면 밖으로 나가는 것에대해서만 Trigger역할을 합니다. 즉, 이미 오른쪽 밖에서 안으로 이동하는 녀석에 대해서는 작동하지 않습니다. 계속 왼쪽 (화면 안쪽)으로 이동합니다. 따라서 추가 함수가 필요합니다.

func _ready() ->void:
	set_physices_process(false) #enemy가 이미 밖에 있을 때도 작동하지 않도록 하는 기능
	_velocity.x = -speed.x


  - Frame Trigger box 사이즈/위치 조절(-50, -50, 100, 60) 


  - Enemy에 Area2D 노드 추가(StompDetector로 이름 변경)


  - Layer 체크 삭제
  - Monitorable: off


  - CollisionShape2D 노드 추가: rectangleshape/사이즈 조정


  - StompDetector위치는 위로 올리고, 아래에서부터 부딛힐 때도 죽으면 안되니까..

  - 내부의 CollisionShape2D는 아래로 내려서 위치를 조정한다. 

  -  박스 색깔을 조절한다.



  - signal연결: body_entered(PhysicsBody2d)


7. 적을 밟아 제거하면 튀어오르는 모션 추가

이제 적당히 움직임이 구현되었습니다. 거기에 더해 Player 동작에 적을 밟았을 때 튀어오르는 모션을 추가해보겠습니다.

  - player에 Area2D 노드 추가(EnemyDetector로 이름 변경)


  - Layer: 없음, Mask: enemy
  - Monitorable: off


  - collisionshape2D - rectangleshape추가
  - 사이즈 조절

살짝 크게, 아래쪽으로 살짝 더 내려오게 박스 설정

  - visibility tab에서 색깔 변경(파란색으로)

  - 적 제거후 튀어오르는 애니메이션 구현 코드


8. 적과 충돌시 Player 죽는 기능 구현

  - signal 연결: body_entered(PhysicsBody2D body) 적에 닿으면 죽는 기능 구현

  _on_EnemyDetector_body_entered(body: PhysicsBody2D) 함수 추가

 

9. 적당한 시점에 Level TileMap을 수정해준다.

 

10. 카메라

  - Player 노드에 Camera2D 추가
  - current: on으로 설정해야 카메라 뷰로 보임


  - w키로 프레임 위치 상향 조정

  - 카메라 이동 제한
  - player - camera2D -limit의 값 수정(left: 0, top: 0)
  - Drag Margin: 카메라 이동 없이 player가 움직일 수 있는 공간, left 0, right 0


  - Drag Margin H/V Enabled
  - Smoothing - Enabled:on, Speed:2로 카메라가 따라오는 속도를 낮춤.. 7정도로 유지


  - player-right click - Editable Children - Limit Right:3600으로 해당 레벨에서만 리밋 설정 가능


11. 배경 이미지 설정

  - backgraound 이미지를 Texture Rectangle로 화면 안쪽으로 끌고 들어옵니다. (TextureRect노드안에 설정) 

  - Layout: Full Rect정
  - 맨 위로 올리면 제일 뒤쪽에 보이도록 할 수 있다.


  - CanvasLayer 추가후 background 를 자식으로 추가하면, 일일이 배경을 복사해서 추가하지 않아도, 게임 진행하면서 일관되게 배경을 추력해줍니다.
  - layer에 -100으로 설정



  - visible Collision Shapes 체크를 끔으로써 게임에서 Collision shape들이 안보이게 하여 마무리 해 줍니다.

반응형