아무리 가상환경이 좋아졌다지만 각각의 OS를 사용하는 것만 못한것 같아서 둘 다 깔아놓고 씁니다.
물론 메인은 리눅스로, 회사일이나 은행일 볼때는 윈도우로(사실 모바일로 다 하지만) 로그인합니다.
서버용 리눅스는 잘 모르겠으나 데스크탑으로는 만자로도 개인적으로 만족하고 있습니다. 물론 심각한 개발을 한다거나 하진 않습니다. Godot엔진으로 간단한 게임 만들고, Visual Studio Code로 웹사이트 끄적거리고, IntelliJ로 아주 초보적인 JAVA 프로그래밍, LiteIDE로 아주 초보적인 Golang 스터디 정도 하고 있네요. 물론 인터넷, 토렌트, 음악은 예전부터 유명한 몇몇 프로그램이 있어서 잘 사용하고 있습니다.
개인적으로는 바탕화면에 프로그램 실행파일들이 널려있으면 지저분해져서 뭔가 정리하고 싶은 마음이 항상 들었는데,
가능은 하지만 귀찮아서 굳이 꺼내놓지 않게끔 하는 이 리눅스의 환경이 저한테는 깔끔하니 이뻐서 더 손이 가게 하네요.
항상 처음 설치할 때 한글 설정으로 버벅거린 경험과, C#을 하겠답시고 이것저것 깔아보다가 실패한 경험이 있지만, 주제넘치는 시도는 당분간 잠재우고, 일반적인 개발과 일반적인 인터넷, 일반적인 멀티미디어 활용으로도 아직은 할일이 넘치는 것 같습니다. 당분간 계속 만자로를 쓸 것 같습니다.
만자로 깔면서 매번 다른 블로그를 찾아가며 시행착오 겪었던 일들을 정리하는 시간을 갖도록 할 예정입니다.
tool #플러그인 사용
extends Button
export(String, FILE) var next_scene_path:="" #string형태로 scene을 받아들이고, 파일 다이얼로그를 보여줌
func _on_button_up():
get_tree().change_scene(next_scene_path)
func _get_configuration.warning() -> String:
return "next_screne_path must be set for the button to work" if next_scene_path=="" else ""
- Merge from scene - mainscreen.tscn에서 Menu 선택으로 메뉴/버튼 불러오기 ○ Next scene에 mainscreen을 선택 ○ Play버튼 텍스트를 Play Again으로 변경
- EndScreen선택 후 resource에서 title검색으로 title.tscn을 가져와서 화면으로 drag & drop ○ Title text변경: Congratulations, you finished the game ○ Layout: center top ○ 아래쪽으로 위치이동
- Label 노드 추가 ○Layout: center top ○Text수정: 텍스트 오른쪽의 expand button
your final score is %s
you died times %s
- 헌번 더 layout 조정, 텍스트 얼라인 center조정
- Title, Label, Menu를 동시 선택 후 layout - center로 중앙 정렬 후 위치 조정 ○ 화면 사이즈가 변하더라도 위치변화로 이미지가 겹치지 않는다.
extends Control
onready var scene_tree: = get_tree() #_ready함수 전에 실행됨
onready var pause_overlay: ColorRect= get_node("PauseOverlay") #ColorRect type임을 명시해줌. 혼선의 여지가 없을 경우는 선언 안하더라도 상관 없음.
var paused: = false setget set_paused
func _unhandled_input(event: InputEvent) -> void:
if event.is_action_pressed("pause"):
self.paused = !paused #(== "paused = not paused") #Self는 gdscript에서 기본이지만, setget함수를 위해서...
scene_tree.set_input_as_handled() #pause event가 끝날때까지 propagate하지 않고 기다린다.
func set_paused(value: bool) ->void:
paused = value
scene_tree.paused = value
- Project Settings - Input Map - pause생성 - Key 추가(+버튼) - Esc로 설정 - UserInterface.tscn을 Level01에 추가 ○ Canvaslayer추가(UserInterface로 변경) ○ 상위 노드로 위치를 바꾸고 Layer를 +100정도로 앞쪽으로 한다. ○ 그 후 Canvaslayer에 UserInterface.tscn을 하위 자식으로 추가
- UserInterface노드 선택 후 Node-Pause-Process로 선택(Inherit(default)/Stop/Process 중) ○ 기본으로 되어있는 Inherit는 상위노드를 상속하고 상위노드는 Stop으로 되어있다. ○ Stop은 완전 정지로 아무것도 Process할 수 없는 상태임
- 추가로, UserInterface 씬에서 Label을 hide하고, 각 level에서 인스턴스된 UserInterface 노드에서는 Label을 show해야만 Label이 이중으로 나타나는 현상이 사라진다.
tool
extends Area2D
onready var anim_player: animationPlayer = $AnimationPlayer
export var next_scene: PackedScene
func _on_body_entered(body: PhysicsBody2D) -> void:
teleport()
func _get_configuration_warning() -> String:
return "The next scene property can't be empty" if not next_scene else ""
func teleport() ->void:
anim_player.play("fade_in")
yield(anim_player, "animation_finished") #anim_player가 끝나서 animation_finished 시그널을 보낼 때까지 기다림
get_tree().change_scene_to(next_scene)
- nextscene을 export로 추가하고, 없으면 에러메세지 popup띄우는 기능 추가
- teleport함수 추가 - body_entered(PhysicsBody2D body) 시그널 연결 : _on_body_entered - Level을 우선 복제하여 테스트
#참고로...터치패드에서 대량으로 tilemap을 지울 때에는 우클릭에 해당하는 두손가락 클릭을 하는데,,,한번하면 생성, 두번하면 삭제 #한손가락은 패드도 눌러야하고, 검지손가락은 터치도 해야함...두번씩....
- 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는 현 단계에서는 필요 없음.
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 사이즈 변경
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들이 안보이게 하여 마무리 해 줍니다.
흔히 슈퍼마리오 게임으로 익숙하게 알고있는 횡 스크롤 형태의 게임을 Platformer 라고 하는 것 같습니다. Godot 엔진을 이용한 Platformer 게임만들기를 시작하려고 하는데요, youtube강좌 https://www.youtube.com/watch?v=Mc13Z2gboEk 를 따라해보았습니다. 총 3편에 걸쳐 진행하며 Godot 엔진에 익숙해질 시간을 가져보겠습니다.
아래는 예전에 핸드폰에 넣으려고 만들었던 게임의 스크린 샷인데, 지금은 소스가 안남아있네요..ㅠㅠ