<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>밀감감의 일상기록</title>
    <link>https://milgamshumi.tistory.com/</link>
    <description>안녕하세요, 밀감감입니다. 게임,취미, 여행일지, 정보 등 여러가지를 취급합니다.</description>
    <language>ko</language>
    <pubDate>Fri, 17 Jul 2026 23:14:59 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>밀감감</managingEditor>
    <image>
      <title>밀감감의 일상기록</title>
      <url>https://tistory1.daumcdn.net/tistory/7488078/attach/cc67a5e907cd4883a196825303f4935c</url>
      <link>https://milgamshumi.tistory.com</link>
    </image>
    <item>
      <title>29. [고도 엔진] 플레이어 이동시키기: CharacterBody와 move_and_slide()</title>
      <link>https://milgamshumi.tistory.com/29</link>
      <description>&lt;p data-path-to-node=&quot;0&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-path-to-node=&quot;0&quot; data-ke-size=&quot;size16&quot;&gt;이 카테고리는 고도 엔진(Godot Engine) 관련 문법을 기록하고 저장하기 위해 만든 공간입니다.&lt;/p&gt;
&lt;p data-path-to-node=&quot;0&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-path-to-node=&quot;1&quot; data-ke-size=&quot;size16&quot;&gt;이번에는 게임에서 가장 먼저 구현하게 되는 기능이자 가장 중요한 뼈대는 바로 '캐릭터 이동'입니다. 슈퍼 마리오처럼 점프를 하거나, 젤다처럼 8방향으로 걸어 다니게 하려면 어떻게 해야 할까요? 고도 엔진(특히 4버전)에서는 &lt;b data-index-in-node=&quot;120&quot; data-path-to-node=&quot;2&quot;&gt;CharacterBody&lt;/b&gt;(2D/3D) 노드와 move_and_slide()라는 마법의 함수를 사용하여 충돌 처리와 이동을 아주 쉽고 완벽하게 구현할 수 있습니다.&lt;/p&gt;
&lt;h3 data-path-to-node=&quot;5&quot; data-ke-size=&quot;size23&quot;&gt;1. CharacterBody 노드: 내 맘대로 조종하는 물리 몸체&lt;/h3&gt;
&lt;p data-path-to-node=&quot;6&quot; data-ke-size=&quot;size16&quot;&gt;게임 속 물체는 크게 두 가지로 나뉩니다. 공처럼 중력과 충돌에 의해 튕겨 나가는 '순수 물리 객체(RigidBody)'와, 벽에 부딪히면 멈추지만 &lt;b data-index-in-node=&quot;83&quot; data-path-to-node=&quot;6&quot;&gt;코드로 직접 이동을 제어&lt;/b&gt;할 수 있는 '캐릭터 객체(CharacterBody)'입니다.&lt;/p&gt;
&lt;p data-path-to-node=&quot;7&quot; data-ke-size=&quot;size16&quot;&gt;플레이어나 몬스터처럼 정밀한 조작(원할 때 멈추고, 원할 때 점프하는)이 필요한 노드는 반드시 CharacterBody2D (또는 3D)를 사용해야 합니다.&lt;/p&gt;
&lt;h3 data-path-to-node=&quot;9&quot; data-ke-size=&quot;size23&quot;&gt;2. velocity와 move_and_slide(): 환상의 콤보&lt;/h3&gt;
&lt;p data-path-to-node=&quot;10&quot; data-ke-size=&quot;size16&quot;&gt;고도 4의 CharacterBody에는 &lt;b data-index-in-node=&quot;22&quot; data-path-to-node=&quot;10&quot;&gt;velocity&lt;/b&gt;(속도)라는 변수가 내장되어 있습니다. 이동을 구현하는 기본 공식은 다음과 같이 아주 간단합니다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-path-to-node=&quot;11&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;캐릭터가 이동할 방향과 속도를 계산해서 내장 변수인 velocity에 넣는다.&lt;/li&gt;
&lt;li&gt;_physics_process 함수 맨 마지막에 move_and_slide()를 딱 한 번 호출한다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-path-to-node=&quot;12&quot; data-ke-size=&quot;size16&quot;&gt;move_and_slide() 함수는 단순히 캐릭터를 이동시키는 것뿐만 아니라, 가다가 벽이나 바닥에 부딪히면 알아서 미끄러지게(Slide) 해주고, 바닥에 닿아있는지(is_on_floor()) 상태까지 자동으로 계산해 주는 엄청난 기능을 가지고 있습니다.&lt;/p&gt;
&lt;h3 data-path-to-node=&quot;14&quot; data-ke-size=&quot;size23&quot;&gt;3. 실전! 2D 플랫포머 기본 이동 코드&lt;/h3&gt;
&lt;p data-path-to-node=&quot;15&quot; data-ke-size=&quot;size16&quot;&gt;가장 많이 쓰이는 좌우 이동과 점프, 그리고 중력이 적용된 2D 플랫포머 게임의 기본 뼈대 코드입니다. CharacterBody2D 노드에 스크립트를 생성하면 고도 엔진이 기본적으로 제공해 주는 템플릿과 동일한 구조입니다.&lt;/p&gt;
&lt;div data-ved=&quot;0CAAQhtANahgKEwjqttaiucCVAxUAAAAAHQAAAAAQpwM&quot; data-hveid=&quot;0&quot;&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;&lt;span&gt;GDScript&lt;/span&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;pre class=&quot;mipsasm&quot;&gt;&lt;code&gt;extends CharacterBody2D

const SPEED = 300.0          # 좌우 이동 속도
const JUMP_VELOCITY = -400.0 # 점프 힘 (위로 올라가야 하므로 마이너스 값)

# 프로젝트 설정에 정의된 기본 중력값을 가져옵니다.
var gravity = ProjectSettings.get_setting(&quot;physics/2d/default_gravity&quot;)

func _physics_process(delta):
    # 1. 중력 적용: 바닥에 닿아있지 않다면(공중에 있다면) 아래로 떨어집니다.
    if not is_on_floor():
        velocity.y += gravity * delta

    # 2. 점프: 바닥에 닿아있고, 점프 키(ui_accept)를 방금 눌렀을 때
    if Input.is_action_just_pressed(&quot;ui_accept&quot;) and is_on_floor():
        velocity.y = JUMP_VELOCITY

    # 3. 좌우 이동: 입력받은 방향(-1, 0, 1)에 속도를 곱합니다.
    var direction = Input.get_axis(&quot;ui_left&quot;, &quot;ui_right&quot;)
    if direction:
        velocity.x = direction * SPEED
    else:
        # 키보드에서 손을 떼면 즉시 멈추도록 속도를 0으로 만듭니다.
        velocity.x = move_toward(velocity.x, 0, SPEED)

    # 4. 설정된 velocity 값을 바탕으로 캐릭터를 실제로 이동시키고 충돌을 처리합니다.
    move_and_slide()
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;h3 data-path-to-node=&quot;18&quot; data-ke-size=&quot;size23&quot;&gt;4. 이동 후 상태 확인하기: 내가 지금 어딨지?&lt;/h3&gt;
&lt;p data-path-to-node=&quot;19&quot; data-ke-size=&quot;size16&quot;&gt;move_and_slide()가 한 번 실행되고 나면, 캐릭터가 현재 어떤 상태인지 알려주는 유용한 내장 함수들을 사용할 수 있습니다. 이 함수들은 이중 점프(더블 점프)를 구현하거나 벽 타기(Wall Jump) 기능을 만들 때 아주 유용합니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;20&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;is_on_floor(): 캐릭터의 발아래가 바닥과 닿아있는가? (true/false)&lt;/li&gt;
&lt;li&gt;is_on_wall(): 캐릭터의 옆면이 벽과 닿아있는가? (true/false)&lt;/li&gt;
&lt;li&gt;is_on_ceiling(): 캐릭터의 머리가 천장에 닿아있는가? (true/false)&lt;/li&gt;
&lt;/ul&gt;
&lt;div data-ved=&quot;0CAAQhtANahgKEwjqttaiucCVAxUAAAAAHQAAAAAQqAM&quot; data-hveid=&quot;0&quot;&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;&lt;span&gt;GDScript&lt;/span&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;pre class=&quot;autoit&quot;&gt;&lt;code&gt;func check_state():
    if is_on_wall() and not is_on_floor():
        print(&quot;벽에 매달려 있습니다!&quot;)
        # 여기에 벽 타기나 슬라이딩 애니메이션 재생 코드를 넣을 수 있습니다.
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;h3 data-path-to-node=&quot;23&quot; data-ke-size=&quot;size23&quot;&gt;한눈에 보는 플레이어 이동 핵심 요약&lt;/h3&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-path-to-node=&quot;24&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;개념 / 함수&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&lt;b&gt;기능 설명&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&lt;b&gt;실전 게임 개발 활용 예시&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span data-path-to-node=&quot;24,1,0,0&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;24,1,0,0&quot;&gt;CharacterBody2D / 3D&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span data-path-to-node=&quot;24,1,1,0&quot;&gt;코드로 조종하며 물리(충돌) 법칙의 영향도 받는 특수 노드&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span data-path-to-node=&quot;24,1,2,0&quot;&gt;플레이어 캐릭터, 추격하는 몬스터, 움직이는 발판(Moving Platform)&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span data-path-to-node=&quot;24,2,0,0&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;24,2,0,0&quot;&gt;velocity&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span data-path-to-node=&quot;24,2,1,0&quot;&gt;CharacterBody에 내장된 속도 벡터(Vector2 또는 Vector3)&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span data-path-to-node=&quot;24,2,2,0&quot;&gt;velocity.x = 300 처럼 값을 대입하여 현재 이동하려는 힘과 방향 설정&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span data-path-to-node=&quot;24,3,0,0&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;24,3,0,0&quot;&gt;move_and_slide()&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span data-path-to-node=&quot;24,3,1,0&quot;&gt;velocity 값을 기준으로 델타 타임(delta)을 자동 계산하여 노드를 이동시킴&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span data-path-to-node=&quot;24,3,2,0&quot;&gt;충돌 시 멈추거나 미끄러지는(Slide) 처리, 언덕 오르내리기 자동 해결&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span data-path-to-node=&quot;24,4,0,0&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;24,4,0,0&quot;&gt;is_on_floor()&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span data-path-to-node=&quot;24,4,1,0&quot;&gt;move_and_slide() 실행 결과, 바닥에 충돌했는지 판별&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span data-path-to-node=&quot;24,4,2,0&quot;&gt;점프가 가능한 상태인지(공중 부양 중이 아닌지) 체크할 때 필수 사용&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span data-path-to-node=&quot;24,5,0,0&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;24,5,0,0&quot;&gt;Input.get_axis()&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span data-path-to-node=&quot;24,5,1,0&quot;&gt;두 액션(왼쪽, 오른쪽) 사이의 값(-1.0 ~ 1.0)을 반환&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span data-path-to-node=&quot;24,5,2,0&quot;&gt;if문 없이 단 한 줄로 좌우 이동 방향을 구할 때 찰떡궁합&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;</description>
      <category>게임/고도엔진 문법</category>
      <category>gdscript</category>
      <category>godot engine</category>
      <category>고도 엔진</category>
      <author>밀감감</author>
      <guid isPermaLink="true">https://milgamshumi.tistory.com/29</guid>
      <comments>https://milgamshumi.tistory.com/29#entry29comment</comments>
      <pubDate>Fri, 17 Jul 2026 12:00:22 +0900</pubDate>
    </item>
    <item>
      <title>28. [고도 엔진] 두 객체 사이의 거리와 방향 구하기 (distance_to, direction_to)</title>
      <link>https://milgamshumi.tistory.com/28</link>
      <description>&lt;p data-path-to-node=&quot;0&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-path-to-node=&quot;0&quot; data-ke-size=&quot;size16&quot;&gt;이 카테고리는 고도 엔진(Godot Engine) 관련 문법을 기록하고 저장하기 위해 만든 공간입니다.&lt;/p&gt;
&lt;p data-path-to-node=&quot;0&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-path-to-node=&quot;1&quot; data-ke-size=&quot;size16&quot;&gt;이번에는&amp;nbsp;게임을 만들다 보면 &quot;플레이어와 몬스터 사이의 거리가 얼마나 될까?&quot;, &quot;총알이 플레이어가 있는 방향으로 날아가게 하려면 어떻게 해야 할까?&quot; 같은 고민을 아주 자주 하게 됩니다. 지난 시간 벡터의 기초에서 '도착점 - 출발점 = 방향'이라는 공식을 배웠지만, 고도 엔진에서는 이를 훨씬 더 직관적이고 편리하게 해결할 수 있는 내장 함수를 제공합니다. 바로 distance_to()와 direction_to()입니다.&lt;/p&gt;
&lt;h3 data-path-to-node=&quot;4&quot; data-ke-size=&quot;size23&quot;&gt;1. distance_to(): 너와 나의 거리 (거리 구하기)&lt;/h3&gt;
&lt;p data-path-to-node=&quot;5&quot; data-ke-size=&quot;size16&quot;&gt;distance_to() 함수는 두 노드의 위치(주로 global_position) 사이의 실제 직선거리를 계산해 줍니다. 2D 게임에서는 픽셀 단위, 3D 게임에서는 미터 단위의 실수(float) 값을 반환합니다.&lt;/p&gt;
&lt;p data-path-to-node=&quot;6&quot; data-ke-size=&quot;size16&quot;&gt;&quot;적이 사정거리 안에 들어왔는가?&quot;, &quot;아이템을 주울 수 있을 만큼 가까이 전근했는가?&quot; 등을 판별할 때 필수적으로 사용됩니다.&lt;/p&gt;
&lt;div data-ved=&quot;0CAAQhtANahgKEwjqttaiucCVAxUAAAAAHQAAAAAQ8wI&quot; data-hveid=&quot;0&quot;&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;&lt;span&gt;GDScript&lt;/span&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;pre class=&quot;swift&quot;&gt;&lt;code&gt;@onready var player = $Player

func _process(delta):
    # 현재 노드(몬스터)의 위치에서 플레이어 위치까지의 거리를 구합니다.
    var distance = global_position.distance_to(player.global_position)
    
    # 거리가 200픽셀보다 작으면 플레이어를 감지하고 추격을 시작합니다.
    if distance &amp;lt; 200.0:
        print(&quot;플레이어 감지! 추격 시작! (거리: &quot;, distance, &quot;)&quot;)
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;h3 data-path-to-node=&quot;9&quot; data-ke-size=&quot;size23&quot;&gt;2. direction_to(): 그곳을 향해 전진 (방향 구하기)&lt;/h3&gt;
&lt;p data-path-to-node=&quot;10&quot; data-ke-size=&quot;size16&quot;&gt;direction_to() 함수는 내 위치에서 상대방 위치를 가리키는 '길이가 1로 고정된 방향 벡터(정규화된 벡터)'를 곧바로 반환해 줍니다.&lt;/p&gt;
&lt;p data-path-to-node=&quot;11&quot; data-ke-size=&quot;size16&quot;&gt;과거에 배웠던 (상대방 위치 - 내 위치).normalized() 공식을 단 한 줄로 깔끔하게 축약한 함수입니다. 이 함수 덕분에 대각선이나 임의의 각도로 이동할 때도 속도가 일정하게 유지되는 화살표(방향)를 쉽게 얻을 수 있습니다.&lt;/p&gt;
&lt;div data-ved=&quot;0CAAQhtANahgKEwjqttaiucCVAxUAAAAAHQAAAAAQ9AI&quot; data-hveid=&quot;0&quot;&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;&lt;span&gt;GDScript&lt;/span&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;pre class=&quot;autoit&quot;&gt;&lt;code&gt;@onready var player = $Player
const SPEED = 150.0

func _physics_process(delta):
    # 내 위치에서 플레이어 위치를 향하는 방향 벡터(길이 1)를 구합니다.
    var direction = global_position.direction_to(player.global_position)
    
    # 구한 방향 벡터에 이동 속도를 곱해 플레이어 쪽으로 걸어갑니다.
    velocity = direction * SPEED
    move_and_slide()
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;h3 data-path-to-node=&quot;14&quot; data-ke-size=&quot;size23&quot;&gt;3. 실전 활용: 사정거리 검사와 조준(look_at) 결합&lt;/h3&gt;
&lt;p data-path-to-node=&quot;15&quot; data-ke-size=&quot;size16&quot;&gt;두 함수를 함께 사용하면 원거리 공격을 하는 적 AI나 유도 미사일의 기초를 완벽하게 구현할 수 있습니다.&lt;/p&gt;
&lt;div data-ved=&quot;0CAAQhtANahgKEwjqttaiucCVAxUAAAAAHQAAAAAQ9QI&quot; data-hveid=&quot;0&quot;&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;&lt;span&gt;GDScript&lt;/span&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;@onready var player = $Player
const ATTACK_RANGE = 100.0

func _process(delta):
    var distance = global_position.distance_to(player.global_position)
    var direction = global_position.direction_to(player.global_position)
    
    # 1. 언제나 플레이어가 있는 방향을 바라보게 만듭니다.
    look_at(player.global_position)
    
    # 2. 사정거리 안에 들어왔을 때만 공격을 실행합니다.
    if distance &amp;lt;= ATTACK_RANGE:
        execute_attack()
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;h3 data-path-to-node=&quot;18&quot; data-ke-size=&quot;size23&quot;&gt;한눈에 보는 거리와 방향 핵심 요약&lt;/h3&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-path-to-node=&quot;19&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;함수&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&lt;b&gt;반환 값 타입&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&lt;b&gt;기능 설명&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&lt;b&gt;실전 게임 개발 활용 예시&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span data-path-to-node=&quot;19,1,0,0&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;19,1,0,0&quot;&gt;A.distance_to(B)&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span data-path-to-node=&quot;19,1,1,0&quot;&gt;실수 (float)&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span data-path-to-node=&quot;19,1,2,0&quot;&gt;지점 A에서 지점 B까지의 &lt;b data-index-in-node=&quot;15&quot; data-path-to-node=&quot;19,1,2,0&quot;&gt;실제 직선거리&lt;/b&gt;를 구합니다.&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span data-path-to-node=&quot;19,1,3,0&quot;&gt;몬스터의 시야 범위 체크, 타워 디펜스의 사거리 판정, 근접 상호작용 가능 여부&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span data-path-to-node=&quot;19,2,0,0&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;19,2,0,0&quot;&gt;A.direction_to(B)&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span data-path-to-node=&quot;19,2,1,0&quot;&gt;벡터 (Vector2 / Vector3)&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span data-path-to-node=&quot;19,2,2,0&quot;&gt;지점 A에서 지점 B를 향하는 &lt;b data-index-in-node=&quot;17&quot; data-path-to-node=&quot;19,2,2,0&quot;&gt;길이가 1인 방향 벡터&lt;/b&gt;를 구합니다.&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span data-path-to-node=&quot;19,2,3,0&quot;&gt;적을 향해 날아가는 총알의 유도 로직, 플레이어를 쫓아오는 좀비의 이동 방향 설정&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;</description>
      <category>게임/고도엔진 문법</category>
      <category>gdscript</category>
      <category>godot engine</category>
      <category>고도 엔진</category>
      <author>밀감감</author>
      <guid isPermaLink="true">https://milgamshumi.tistory.com/28</guid>
      <comments>https://milgamshumi.tistory.com/28#entry28comment</comments>
      <pubDate>Thu, 16 Jul 2026 12:00:10 +0900</pubDate>
    </item>
    <item>
      <title>27. [고도 엔진] 2D/3D 공간의 이해: 벡터(Vector2, Vector3) 기초</title>
      <link>https://milgamshumi.tistory.com/27</link>
      <description>&lt;p data-path-to-node=&quot;0&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-path-to-node=&quot;0&quot; data-ke-size=&quot;size16&quot;&gt;이 카테고리는 고도 엔진(Godot Engine) 관련 문법을 기록하고 저장하기 위해 만든 공간입니다.&lt;/p&gt;
&lt;p data-path-to-node=&quot;0&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-path-to-node=&quot;1&quot; data-ke-size=&quot;size16&quot;&gt;이번에는&amp;nbsp;학교 수학 시간에 '벡터'라는 단어를 듣고 머리가 아팠던 기억이 있으신가요? 걱정하지 마세요! 게임 개발에서의 벡터는 복잡한 수학 공식이라기보다는, 게임 세상 속에서 노드들이 어디에 있는지(위치), 어디로 가고 있는지(방향), 그리고 얼마나 빨리 가고 있는지(속도)를 나타내는 '화살표'라고 생각하면 아주 쉽습니다.&lt;/p&gt;
&lt;p data-path-to-node=&quot;3&quot; data-ke-size=&quot;size16&quot;&gt;고도 엔진에서 2D 게임을 만들 때는 Vector2, 3D 게임을 만들 때는 Vector3를 사용합니다. 캐릭터를 움직이기 위해 반드시 알아야 하는 벡터의 기초와 핵심 활용법을 정리해 보겠습니다.&lt;/p&gt;
&lt;h3 data-path-to-node=&quot;5&quot; data-ke-size=&quot;size23&quot;&gt;1. 벡터의 기본: 좌표계와 위치 (x, y, z)&lt;/h3&gt;
&lt;p data-path-to-node=&quot;6&quot; data-ke-size=&quot;size16&quot;&gt;벡터는 단순히 숫자들의 묶음입니다. 2D 게임에서는 가로(x)와 세로(y) 두 개의 숫자가 필요하므로 Vector2를 쓰고, 3D 게임에서는 깊이(z)까지 추가되어 Vector3를 씁니다.&lt;/p&gt;
&lt;p data-path-to-node=&quot;6&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-path-to-node=&quot;6&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b data-path-to-node=&quot;7,0&quot; data-index-in-node=&quot;0&quot;&gt;주의 (고도 엔진 2D의 특징): &lt;/b&gt;수학 시간이나 일반적인 그래프에서는 y축이 위로 갈수록 숫자가 커지지만, 고도 엔진의 2D 화면에서는&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b data-path-to-node=&quot;7,0&quot; data-index-in-node=&quot;78&quot;&gt;아래로 내려갈수록 y값이 커집니다 (+y).&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;반대로 위로 올라가려면 y값을 빼주어야 합니다 (-y).&lt;/p&gt;
&lt;div data-ved=&quot;0CAAQhtANahgKEwjqttaiucCVAxUAAAAAHQAAAAAQzAI&quot; data-hveid=&quot;0&quot;&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;&lt;span&gt;GDScript&lt;/span&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;func _ready():
    # 1. 2D 벡터 생성: x좌표 100, y좌표 50인 위치
    var player_pos = Vector2(100, 50)
    print(&quot;X 좌표: &quot;, player_pos.x, &quot;, Y 좌표: &quot;, player_pos.y)
    
    # 2. 엔진에서 자주 쓰는 방향은 미리 상수로 만들어져 있습니다.
    var up_dir = Vector2.UP    # Vector2(0, -1) 과 동일
    var right_dir = Vector2.RIGHT # Vector2(1, 0) 과 동일
    
    # 3. 3D 공간이라면 z축이 추가됩니다.
    var box_pos3d = Vector3(10, 5, -2)
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;h3 data-path-to-node=&quot;10&quot; data-ke-size=&quot;size23&quot;&gt;2. 벡터의 덧셈과 뺄셈: 이동과 목표물 추적&lt;/h3&gt;
&lt;p data-path-to-node=&quot;11&quot; data-ke-size=&quot;size16&quot;&gt;게임에서 벡터끼리 더하거나 빼는 것은 일상입니다. 현재 위치에 속도를 더하면 '이동'이 되고, 목표물의 위치에서 내 위치를 빼면 '목표를 향하는 방향'이 나옵니다. 특히 '도착점 - 출발점 = 방향'이라는 공식은 유도 미사일을 만들거나 적을 쳐다보게 만들 때 무조건 쓰이는 마법의 공식입니다.&lt;/p&gt;
&lt;div data-ved=&quot;0CAAQhtANahgKEwjqttaiucCVAxUAAAAAHQAAAAAQzQI&quot; data-hveid=&quot;0&quot;&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;&lt;span&gt;GDScript&lt;/span&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;pre class=&quot;autoit&quot;&gt;&lt;code&gt;# 1. 벡터의 덧셈 (이동)
func _process(delta):
    var velocity = Vector2(100, 0) # 오른쪽으로 100만큼의 속도
    
    # 내 현재 위치에 속도를 더해 캐릭터를 오른쪽으로 이동시킵니다.
    position += velocity * delta 

# 2. 벡터의 뺄셈 (방향 구하기)
func get_direction_to_enemy(enemy_position: Vector2):
    # 공식: 도착점(적 위치) - 출발점(내 위치)
    var direction_vector = enemy_position - self.global_position
    
    # 이 벡터는 나침반처럼 적이 있는 곳을 가리키는 화살표가 됩니다.
    return direction_vector
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;h3 data-path-to-node=&quot;14&quot; data-ke-size=&quot;size23&quot;&gt;3. 마법의 함수들: normalized, length, distance_to&lt;/h3&gt;
&lt;p data-path-to-node=&quot;15&quot; data-ke-size=&quot;size16&quot;&gt;벡터를 자유자재로 다루기 위해 고도 엔진이 제공하는 강력한 내장 함수 3대장입니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;16&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;16,0,0&quot;&gt;length() (길이):&lt;/b&gt; 화살표의 길이를 구합니다. 게임에서는 주로 '현재 내 캐릭터의 이동 속력'이 얼마인지 확인할 때 씁니다.&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;16,1,0&quot;&gt;normalized() (정규화):&lt;/b&gt; 화살표의 방향은 그대로 유지한 채, &lt;b data-index-in-node=&quot;40&quot; data-path-to-node=&quot;16,1,0&quot;&gt;길이만 1로 압축&lt;/b&gt;합니다. 대각선으로 이동할 때 가로/세로 이동보다 1.4배 빨라지는 버그를 막기 위해 반드시 사용해야 합니다.&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;16,2,0&quot;&gt;distance_to() (거리):&lt;/b&gt; 나와 목표물 사이의 실제 직선거리를 구합니다. &quot;사정거리 안에 적이 들어왔는가?&quot;를 판별할 때 쓰입니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;div data-ved=&quot;0CAAQhtANahgKEwjqttaiucCVAxUAAAAAHQAAAAAQzgI&quot; data-hveid=&quot;0&quot;&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;&lt;span&gt;GDScript&lt;/span&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;pre class=&quot;swift&quot;&gt;&lt;code&gt;func move_and_check(target_pos: Vector2):
    # 1. 입력받은 방향을 정규화하여 대각선 이동 시 속도가 빨라지는 것을 방지합니다.
    var move_dir = Input.get_vector(&quot;ui_left&quot;, &quot;ui_right&quot;, &quot;ui_up&quot;, &quot;ui_down&quot;)
    
    # get_vector()는 이미 정규화되어 있지만, 직접 만들 땐 아래처럼 해야 합니다.
    # var raw_dir = Vector2(1, 1) # 대각선 입력 (길이가 약 1.414)
    # var safe_dir = raw_dir.normalized() # 길이를 1.0으로 고정
    
    # 2. 적과의 거리 계산 (사정거리 확인)
    var distance = self.global_position.distance_to(target_pos)
    
    if distance &amp;lt; 50.0:
        print(&quot;적이 사정거리 50 안에 들어왔습니다! 공격!&quot;)
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;h3 data-path-to-node=&quot;19&quot; data-ke-size=&quot;size23&quot;&gt;한눈에 보는 벡터(Vector) 핵심 요약&lt;/h3&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-path-to-node=&quot;20&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;개념 / 함수&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&lt;b&gt;의미와 기능&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&lt;b&gt;실전 게임 개발 활용 예시&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span data-path-to-node=&quot;20,1,0,0&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;20,1,0,0&quot;&gt;Vector2(x, y)&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span data-path-to-node=&quot;20,1,1,0&quot;&gt;2D 공간의 x(가로), y(세로) 좌표 또는 화살표&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span data-path-to-node=&quot;20,1,2,0&quot;&gt;2D 캐릭터의 화면상 위치, 총알이 날아가는 속도와 방향&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span data-path-to-node=&quot;20,2,0,0&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;20,2,0,0&quot;&gt;Vector3(x, y, z)&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span data-path-to-node=&quot;20,2,1,0&quot;&gt;3D 공간의 x(가로), y(높이), z(깊이) 좌표&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span data-path-to-node=&quot;20,2,2,0&quot;&gt;3D 몬스터의 스폰 위치, 카메라가 바라보는 각도&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span data-path-to-node=&quot;20,3,0,0&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;20,3,0,0&quot;&gt;도착점 - 출발점&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span data-path-to-node=&quot;20,3,1,0&quot;&gt;(목표 위치) - (내 위치)를 빼서 &lt;b data-index-in-node=&quot;21&quot; data-path-to-node=&quot;20,3,1,0&quot;&gt;방향 벡터&lt;/b&gt; 계산&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span data-path-to-node=&quot;20,3,2,0&quot;&gt;플레이어를 쫓아오는 좀비의 이동 방향, 총구에서 마우스를 향하는 방향&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span data-path-to-node=&quot;20,4,0,0&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;20,4,0,0&quot;&gt;length()&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span data-path-to-node=&quot;20,4,1,0&quot;&gt;벡터(화살표)의 &lt;b data-index-in-node=&quot;9&quot; data-path-to-node=&quot;20,4,1,0&quot;&gt;길이&lt;/b&gt;를 실수(float)로 반환&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span data-path-to-node=&quot;20,4,2,0&quot;&gt;차의 현재 속력 계기판 표시, 화살이 날아가는 힘 계산&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span data-path-to-node=&quot;20,5,0,0&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;20,5,0,0&quot;&gt;normalized()&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span data-path-to-node=&quot;20,5,1,0&quot;&gt;방향은 유지하고 길이를 &lt;b data-index-in-node=&quot;13&quot; data-path-to-node=&quot;20,5,1,0&quot;&gt;1.0으로 고정(정규화)&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span data-path-to-node=&quot;20,5,2,0&quot;&gt;8방향 이동 시 대각선에서 속도가 비정상적으로 빨라지는 현상 방지&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span data-path-to-node=&quot;20,6,0,0&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;20,6,0,0&quot;&gt;distance_to(목표)&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span data-path-to-node=&quot;20,6,1,0&quot;&gt;현재 위치에서 목표 위치까지의 &lt;b data-index-in-node=&quot;17&quot; data-path-to-node=&quot;20,6,1,0&quot;&gt;직선거리&lt;/b&gt; 계산&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span data-path-to-node=&quot;20,6,2,0&quot;&gt;타워 디펜스에서 타워 사거리 내에 몬스터가 진입했는지 감지&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;</description>
      <category>게임/고도엔진 문법</category>
      <category>gdscript</category>
      <category>godot engine</category>
      <category>고도 엔진</category>
      <author>밀감감</author>
      <guid isPermaLink="true">https://milgamshumi.tistory.com/27</guid>
      <comments>https://milgamshumi.tistory.com/27#entry27comment</comments>
      <pubDate>Wed, 15 Jul 2026 12:00:52 +0900</pubDate>
    </item>
    <item>
      <title>26. [고도 엔진] 열거형(enum)을 활용한 캐릭터 상태 기계(State Machine) 만들기</title>
      <link>https://milgamshumi.tistory.com/26</link>
      <description>&lt;p data-path-to-node=&quot;0&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-path-to-node=&quot;0&quot; data-ke-size=&quot;size16&quot;&gt;이 카테고리는 고도 엔진(Godot Engine) 관련 문법을 기록하고 저장하기 위해 만든 공간입니다.&lt;/p&gt;
&lt;p data-path-to-node=&quot;1&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-path-to-node=&quot;1&quot; data-ke-size=&quot;size16&quot;&gt;이번에는 캐릭터를 조작하는 게임을 만들다 보면 is_running, is_jumping, is_attacking 같은 참/거짓(boolean) 변수가 끝도 없이 늘어나는 경험을 하게 됩니다. &quot;공격 중에는 점프할 수 없다&quot;, &quot;죽어있을 때는 움직일 수 없다&quot; 같은 예외 처리를 하려다 보면 if 문이 거미줄처럼 얽힌 이른바 '스파게티 코드'가 탄생하죠.&lt;/p&gt;
&lt;p data-path-to-node=&quot;3&quot; data-ke-size=&quot;size16&quot;&gt;이러한 문제를 깔끔하게 해결해 주는 디자인 패턴이 바로 상태 기계(Finite State Machine, FSM)입니다. 캐릭터가 오직 '하나의 상태'만 가질 수 있도록 열거형(enum)으로 묶어서 관리하는 방법을 알아보겠습니다.&lt;/p&gt;
&lt;h3 data-path-to-node=&quot;5&quot; data-ke-size=&quot;size23&quot;&gt;1. enum 선언과 상태 변수 만들기&lt;/h3&gt;
&lt;p data-path-to-node=&quot;6&quot; data-ke-size=&quot;size16&quot;&gt;열거형(enum)은 연관된 상수들의 집합에 이름을 붙여주는 기능입니다. 0, 1, 2, 3 같은 숫자 대신 IDLE, RUN, JUMP 같은 읽기 쉬운 이름을 사용하여 상태를 정의할 수 있습니다.&lt;/p&gt;
&lt;p data-path-to-node=&quot;7&quot; data-ke-size=&quot;size16&quot;&gt;스크립트 최상단에 캐릭터가 가질 수 있는 상태들을 enum으로 정의하고, 현재 상태를 저장할 변수를 하나 만들어 줍니다.&lt;/p&gt;
&lt;div data-ved=&quot;0CAAQhtANahgKEwjqttaiucCVAxUAAAAAHQAAAAAQ7QE&quot; data-hveid=&quot;0&quot;&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;&lt;span&gt;GDScript&lt;/span&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;pre class=&quot;crystal&quot;&gt;&lt;code&gt;# 1. 캐릭터가 가질 수 있는 모든 상태를 enum으로 정의합니다.
enum State {
    IDLE,   # 대기
    RUN,    # 달리기
    JUMP,   # 점프
    ATTACK, # 공격
    DEAD    # 사망
}

# 2. 현재 상태를 저장할 변수를 만들고, 기본값을 IDLE(대기)로 설정합니다.
var current_state: State = State.IDLE
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;h3 data-path-to-node=&quot;10&quot; data-ke-size=&quot;size23&quot;&gt;2. match 문으로 상태별 동작 분리하기 (행동 로직)&lt;/h3&gt;
&lt;p data-path-to-node=&quot;11&quot; data-ke-size=&quot;size16&quot;&gt;이제 매 프레임마다 실행되는 _physics_process (또는 _process) 함수 안에서 현재 상태가 무엇인지 확인하고, 그에 맞는 행동만 하도록 코드를 분리합니다.&lt;/p&gt;
&lt;p data-path-to-node=&quot;12&quot; data-ke-size=&quot;size16&quot;&gt;이때 if-elif 문 대신 고도 엔진의 &lt;b data-index-in-node=&quot;23&quot; data-path-to-node=&quot;12&quot;&gt;match 문&lt;/b&gt;(다른 언어의 switch-case와 유사)을 사용하면 코드를 훨씬 직관적으로 작성할 수 있습니다.&lt;/p&gt;
&lt;div data-ved=&quot;0CAAQhtANahgKEwjqttaiucCVAxUAAAAAHQAAAAAQ7gE&quot; data-hveid=&quot;0&quot;&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;&lt;span&gt;GDScript&lt;/span&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;pre class=&quot;yaml&quot;&gt;&lt;code&gt;func _physics_process(delta):
    # 현재 상태에 따라 실행할 코드를 깔끔하게 나눕니다.
    match current_state:
        State.IDLE:
            # 대기 중일 때의 애니메이션 재생 및 마찰력 적용
            velocity.x = move_toward(velocity.x, 0, SPEED)
            
        State.RUN:
            # 달릴 때의 이동 로직
            var direction = Input.get_axis(&quot;ui_left&quot;, &quot;ui_right&quot;)
            velocity.x = direction * SPEED
            
        State.JUMP:
            # 공중에 있을 때의 중력 적용 로직
            velocity.y += gravity * delta
            
        State.ATTACK:
            # 공격 중에는 이동하지 못하도록 속도를 0으로 고정
            velocity.x = 0
            
        State.DEAD:
            # 사망 시 아무 동작도 하지 않고 함수 종료
            return
            
    move_and_slide()
    check_transitions() # 상태 전환을 검사하는 함수 호출
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;h3 data-path-to-node=&quot;15&quot; data-ke-size=&quot;size23&quot;&gt;3. 상태 전환하기 (조건 검사)&lt;/h3&gt;
&lt;p data-path-to-node=&quot;16&quot; data-ke-size=&quot;size16&quot;&gt;행동 로직을 만들었으니, 언제 다른 상태로 넘어갈지(Transition) 규칙을 정해줘야 합니다. 코드가 섞이지 않도록 상태 전환을 검사하는 함수를 따로 분리하는 것이 좋습니다.&lt;/p&gt;
&lt;div data-ved=&quot;0CAAQhtANahgKEwjqttaiucCVAxUAAAAAHQAAAAAQ7wE&quot; data-hveid=&quot;0&quot;&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;&lt;span&gt;GDScript&lt;/span&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;func check_transitions():
    # 사망 상태라면 다른 어떤 상태로도 변할 수 없습니다.
    if current_state == State.DEAD:
        return
        
    match current_state:
        State.IDLE:
            # 이동 키를 누르면 달리기 상태로 전환
            if Input.get_axis(&quot;ui_left&quot;, &quot;ui_right&quot;) != 0:
                change_state(State.RUN)
            # 점프 키를 누르면 점프 상태로 전환
            elif Input.is_action_just_pressed(&quot;ui_accept&quot;):
                change_state(State.JUMP)
                
        State.RUN:
            # 이동을 멈추면 다시 대기 상태로 전환
            if Input.get_axis(&quot;ui_left&quot;, &quot;ui_right&quot;) == 0:
                change_state(State.IDLE)
            elif Input.is_action_just_pressed(&quot;ui_accept&quot;):
                change_state(State.JUMP)

# 상태가 바뀔 때 한 번만 실행되어야 하는 코드(애니메이션 변경 등)를 처리하는 헬퍼 함수
func change_state(new_state: State):
    current_state = new_state
    
    match current_state:
        State.IDLE:
            $AnimationPlayer.play(&quot;idle&quot;)
        State.RUN:
            $AnimationPlayer.play(&quot;run&quot;)
        State.JUMP:
            velocity.y = JUMP_VELOCITY
            $AnimationPlayer.play(&quot;jump&quot;)
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;h3 data-path-to-node=&quot;19&quot; data-ke-size=&quot;size23&quot;&gt;한눈에 보는 상태 기계(FSM) 핵심 요약&lt;/h3&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-path-to-node=&quot;20&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;키워드 / 기능&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&lt;b&gt;기능 설명&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&lt;b&gt;상태 기계에서의 역할&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span data-path-to-node=&quot;20,1,0,0&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;20,1,0,0&quot;&gt;enum&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span data-path-to-node=&quot;20,1,1,0&quot;&gt;관련된 숫자 상수들에 이름을 붙여 그룹화합니다.&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span data-path-to-node=&quot;20,1,2,0&quot;&gt;IDLE, RUN 등 캐릭터가 가질 수 있는 &lt;b data-index-in-node=&quot;25&quot; data-path-to-node=&quot;20,1,2,0&quot;&gt;상태의 종류를 정의&lt;/b&gt;합니다.&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span data-path-to-node=&quot;20,2,0,0&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;20,2,0,0&quot;&gt;상태 변수&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span data-path-to-node=&quot;20,2,1,0&quot;&gt;현재 어떤 상태인지 저장하는 하나의 변수입니다.&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span data-path-to-node=&quot;20,2,2,0&quot;&gt;여러 개의 boolean 변수(is_running 등)를 &lt;b data-index-in-node=&quot;32&quot; data-path-to-node=&quot;20,2,2,0&quot;&gt;단 하나의 변수&lt;/b&gt;로 대체합니다.&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span data-path-to-node=&quot;20,3,0,0&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;20,3,0,0&quot;&gt;match 문&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span data-path-to-node=&quot;20,3,1,0&quot;&gt;변수의 값에 따라 실행할 코드를 분기(나눔)합니다.&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span data-path-to-node=&quot;20,3,2,0&quot;&gt;현재 상태에 따라 &lt;b data-index-in-node=&quot;10&quot; data-path-to-node=&quot;20,3,2,0&quot;&gt;이동, 애니메이션 등의 동작을 다르게 처리&lt;/b&gt;합니다.&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span data-path-to-node=&quot;20,4,0,0&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;20,4,0,0&quot;&gt;상태 전환 (Transition)&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span data-path-to-node=&quot;20,4,1,0&quot;&gt;특정 조건이 만족되었을 때 상태 변수의 값을 바꿉니다.&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span data-path-to-node=&quot;20,4,2,0&quot;&gt;키 입력이나 땅 착지 등의 이벤트를 감지하여 &lt;b data-index-in-node=&quot;25&quot; data-path-to-node=&quot;20,4,2,0&quot;&gt;다른 상태로 넘겨줍니다.&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;</description>
      <category>게임/고도엔진 문법</category>
      <category>gdscript</category>
      <category>godot engine</category>
      <category>고도 엔진</category>
      <author>밀감감</author>
      <guid isPermaLink="true">https://milgamshumi.tistory.com/26</guid>
      <comments>https://milgamshumi.tistory.com/26#entry26comment</comments>
      <pubDate>Tue, 14 Jul 2026 12:00:25 +0900</pubDate>
    </item>
    <item>
      <title>25. [고도 엔진] 부드러운 움직임의 비밀: 트윈(Tween) 시스템 완벽 가이드</title>
      <link>https://milgamshumi.tistory.com/25</link>
      <description>&lt;p data-path-to-node=&quot;0&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-path-to-node=&quot;0&quot; data-ke-size=&quot;size16&quot;&gt;이 카테고리는 고도 엔진(Godot Engine) 관련 문법을 기록하고 저장하기 위해 만든 공간입니다.&lt;/p&gt;
&lt;p data-path-to-node=&quot;1&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-path-to-node=&quot;1&quot; data-ke-size=&quot;size16&quot;&gt;이번에는 게임에서 굳게 닫혀있던 돌문이 스르륵 열리거나, 레벨업 팝업 창이 하고 튀어나오고, 데미지를 입은 몬스터가 서서히 투명해지며 사라지는 연출, 부드러운 애니메이션을 _process 함수에서 매 프레임마다 수학 공식을 써가며 수동으로 만들려면 여간 골치 아픈 일이 아닙니다.&lt;/p&gt;
&lt;p data-path-to-node=&quot;3&quot; data-ke-size=&quot;size16&quot;&gt;이럴 때 고도 엔진의 &lt;b data-index-in-node=&quot;12&quot; data-path-to-node=&quot;3&quot;&gt;트윈(Tween)&lt;/b&gt; 시스템을 사용하면 &quot;A상태에서 B상태로 N초 동안 부드럽게 변해라!&quot;라고 단 몇 줄의 코드로 우아하게 명령할 수 있습니다. 고도 4버전에서 더욱 강력하고 쓰기 편해진 트윈 활용법을 알아봅시다.&lt;/p&gt;
&lt;h3 data-path-to-node=&quot;5&quot; data-ke-size=&quot;size23&quot;&gt;1. 트윈 생성과 기본 사용법: A에서 B로 이동하기&lt;/h3&gt;
&lt;p data-path-to-node=&quot;6&quot; data-ke-size=&quot;size16&quot;&gt;고도 4에서는 노드 트리에 번거롭게 Tween 노드를 추가할 필요 없이, 스크립트 안에서 create_tween() 함수를 호출하여 즉석에서 트윈을 만들 수 있습니다.&lt;/p&gt;
&lt;p data-path-to-node=&quot;7&quot; data-ke-size=&quot;size16&quot;&gt;가장 기본이 되는 함수는 tween_property(대상, &quot;속성명&quot;, 목표값, 시간) 입니다.&lt;/p&gt;
&lt;div data-ved=&quot;0CAAQhtANahgKEwjqttaiucCVAxUAAAAAHQAAAAAQxAE&quot; data-hveid=&quot;0&quot;&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;&lt;span&gt;GDScript&lt;/span&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;func move_forward():
    # 1. 현재 씬 트리에서 새로운 트윈 객체를 생성합니다.
    var tween = create_tween()
    
    # 2. 현재 노드(self)의 &quot;position&quot; 속성을, 
    # Vector2(500, 100) 위치로, 2.0초(초 단위) 동안 부드럽게 변경합니다.
    tween.tween_property(self, &quot;position&quot;, Vector2(500, 100), 2.0)
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;blockquote data-path-to-node=&quot;9&quot; data-ke-style=&quot;style1&quot;&gt;
&lt;p data-path-to-node=&quot;9,0&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;9,0&quot;&gt;참고:&lt;/b&gt; 트윈은 연결된 노드가 삭제되면(예: 몬스터가 죽으면) 자동으로 함께 소멸하므로 메모리 누수를 걱정할 필요가 없습니다!&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 data-path-to-node=&quot;11&quot; data-ke-size=&quot;size23&quot;&gt;2. 순차 실행과 동시 실행 (체이닝과 Parallel)&lt;/h3&gt;
&lt;p data-path-to-node=&quot;12&quot; data-ke-size=&quot;size16&quot;&gt;트윈의 진정한 강력함은 여러 개의 애니메이션을 줄줄이 엮을 수 있다는 점입니다. 고도 4의 트윈은 기본적으로 순차 실행(Sequence)됩니다. 즉, 앞에 적힌 명령이 끝나야 다음 명령이 실행됩니다. 만약 여러 개를 &lt;b data-index-in-node=&quot;121&quot; data-path-to-node=&quot;12&quot;&gt;동시에(Parallel)&lt;/b&gt; 실행하고 싶다면 parallel()을 사용하면 됩니다.&lt;/p&gt;
&lt;div data-ved=&quot;0CAAQhtANahgKEwjqttaiucCVAxUAAAAAHQAAAAAQxQE&quot; data-hveid=&quot;0&quot;&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;&lt;span&gt;GDScript&lt;/span&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;func appear_and_move():
    var tween = create_tween()
    
    # 1. 처음에 스케일을 0에서 1로 1초 동안 키웁니다. (뿅 나타나는 효과)
    tween.tween_property(self, &quot;scale&quot;, Vector2(1, 1), 1.0)
    
    # 2. 크기가 다 커진 '이후에' 오른쪽으로 2초 동안 이동합니다.
    tween.tween_property(self, &quot;position:x&quot;, 300, 2.0)
    
    # 3. 이동하는 '동시에' 색상을 서서히 투명하게(알파값 0) 만듭니다.
    # parallel()이 붙었으므로 바로 위 명령(이동)과 함께 실행됩니다.
    tween.parallel().tween_property(self, &quot;modulate:a&quot;, 0.0, 2.0)
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;h3 data-path-to-node=&quot;15&quot; data-ke-size=&quot;size23&quot;&gt;3. 이징과 트랜지션: 고무공처럼 통통 튀게 만들기&lt;/h3&gt;
&lt;p data-path-to-node=&quot;16&quot; data-ke-size=&quot;size16&quot;&gt;물체가 단순히 일정한 속도로 슥~ 움직이면 로봇이나 기계장치 같아서 재미가 없습니다. set_trans()와 set_ease()를 활용하면 처음에 천천히 출발하다가 나중에 빨라지거나, 도착 지점에서 고무공처럼 통통 튀는 역동적인 연출을 추가할 수 있습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;17&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;17,0,0&quot;&gt;Transition (전환 방식):&lt;/b&gt; 움직임의 뼈대 (예: 선형, 사인 곡선, 바운스, 탄성 등)&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;17,1,0&quot;&gt;Easing (완화 방식):&lt;/b&gt; 트랜지션을 시작점(IN), 끝점(OUT), 양쪽(IN_OUT) 중 어디에 강하게 적용할지 결정&lt;/li&gt;
&lt;/ul&gt;
&lt;div data-ved=&quot;0CAAQhtANahgKEwjqttaiucCVAxUAAAAAHQAAAAAQxgE&quot; data-hveid=&quot;0&quot;&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;&lt;span&gt;GDScript&lt;/span&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;func bounce_popup():
    var tween = create_tween()
    
    # 통통 튀는 바운스 효과(TRANS_BOUNCE)를 도착할 때(EASE_OUT) 적용합니다.
    tween.set_trans(Tween.TRANS_BOUNCE)
    tween.set_ease(Tween.EASE_OUT)
    
    # 화면 밖에서 화면 중앙으로 1.5초 동안 튕기며 들어옵니다.
    tween.tween_property(self, &quot;position&quot;, Vector2(400, 300), 1.5)
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;h3 data-path-to-node=&quot;20&quot; data-ke-size=&quot;size23&quot;&gt;4. tween_callback(): 애니메이션이 끝난 후 할 일 정하기&lt;/h3&gt;
&lt;p data-path-to-node=&quot;21&quot; data-ke-size=&quot;size16&quot;&gt;트윈이 모두 끝난 직후에 특정 함수를 실행하거나 노드를 삭제하고 싶을 때가 있습니다. 이때는 tween_callback()을 사용하여 끝나는 타이밍에 맞춰 코드를 실행할 수 있습니다.&lt;/p&gt;
&lt;div data-ved=&quot;0CAAQhtANahgKEwjqttaiucCVAxUAAAAAHQAAAAAQxwE&quot; data-hveid=&quot;0&quot;&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;&lt;span&gt;GDScript&lt;/span&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;func fade_out_and_die():
    var tween = create_tween()
    
    # 1초 동안 서서히 투명해집니다.
    tween.tween_property(self, &quot;modulate:a&quot;, 0.0, 1.0)
    
    # 투명해지는 애니메이션이 끝나면 queue_free() 함수를 호출하여 노드를 삭제합니다.
    tween.tween_callback(self.queue_free)
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;h3 data-path-to-node=&quot;24&quot; data-ke-size=&quot;size23&quot;&gt;한눈에 보는 트윈(Tween) 핵심 요약&lt;/h3&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-path-to-node=&quot;25&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;함수 / 기능&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&lt;b&gt;기능 설명&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&lt;b&gt;실전 활용 예시&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span data-path-to-node=&quot;25,1,0,0&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;25,1,0,0&quot;&gt;create_tween()&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span data-path-to-node=&quot;25,1,1,0&quot;&gt;스크립트에서 즉석으로 새로운 트윈 객체를 생성합니다.&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span data-path-to-node=&quot;25,1,2,0&quot;&gt;무언가를 부드럽게 움직이거나 변화시킬 때 무조건 가장 먼저 호출&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span data-path-to-node=&quot;25,2,0,0&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;25,2,0,0&quot;&gt;tween_property()&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span data-path-to-node=&quot;25,2,1,0&quot;&gt;(대상 노드, &quot;속성&quot;, 목표값, 시간)을 지정하여 속성을 부드럽게 변경합니다.&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span data-path-to-node=&quot;25,2,2,0&quot;&gt;캐릭터 이동, 카메라 줌인/줌아웃, UI 페이드인 등&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span data-path-to-node=&quot;25,3,0,0&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;25,3,0,0&quot;&gt;parallel()&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span data-path-to-node=&quot;25,3,1,0&quot;&gt;바로 앞의 트윈 애니메이션과 &lt;b data-index-in-node=&quot;16&quot; data-path-to-node=&quot;25,3,1,0&quot;&gt;동시에&lt;/b&gt; 실행되도록 묶어줍니다.&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span data-path-to-node=&quot;25,3,2,0&quot;&gt;크기가 커지면서 동시에 회전하는 화려한 스킬 이펙트 연출&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span data-path-to-node=&quot;25,4,0,0&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;25,4,0,0&quot;&gt;set_trans() / set_ease()&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span data-path-to-node=&quot;25,4,1,0&quot;&gt;애니메이션의 곡선(가속, 감속, 튕김 등)을 설정하여 자연스러움을 더합니다.&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span data-path-to-node=&quot;25,4,2,0&quot;&gt;젤리 몬스터의 출렁거림, 자동차의 급정거 연출&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span data-path-to-node=&quot;25,5,0,0&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;25,5,0,0&quot;&gt;tween_callback()&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span data-path-to-node=&quot;25,5,1,0&quot;&gt;특정 지점에서 원하는 함수를 호출(실행)합니다.&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span data-path-to-node=&quot;25,5,2,0&quot;&gt;몬스터가 땅으로 꺼지는 연출(이동) 완료 후 아이템 드랍 함수 실행&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;</description>
      <category>게임/고도엔진 문법</category>
      <category>gdscript</category>
      <category>godot engine</category>
      <category>고도 엔진</category>
      <author>밀감감</author>
      <guid isPermaLink="true">https://milgamshumi.tistory.com/25</guid>
      <comments>https://milgamshumi.tistory.com/25#entry25comment</comments>
      <pubDate>Mon, 13 Jul 2026 12:00:29 +0900</pubDate>
    </item>
    <item>
      <title>24. [고도 엔진] 시간에 따른 동작 제어: Timer 노드와 코드 타이머 생성</title>
      <link>https://milgamshumi.tistory.com/24</link>
      <description>&lt;p data-path-to-node=&quot;0&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-path-to-node=&quot;0&quot; data-ke-size=&quot;size16&quot;&gt;이 카테고리는 고도 엔진(Godot Engine) 관련 문법을 기록하고 저장하기 위해 만든 공간입니다.&lt;/p&gt;
&lt;p data-path-to-node=&quot;1&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-path-to-node=&quot;1&quot; data-ke-size=&quot;size16&quot;&gt;이번에는 게임 개발을 하다 보면 &quot;3초 뒤에 몬스터를 소환해라&quot;, &quot;1초마다 플레이어의 체력을 회복시켜라&quot;, &quot;스킬을 한 번 쓰면 5초 동안 쿨타임을 적용해라&quot; 같이 시간에 따라 무언가를 제어해야 하는 상황이 정말 많이 발생합니다. 고도 엔진에서는 이를 위해 &lt;b data-index-in-node=&quot;140&quot; data-path-to-node=&quot;2&quot;&gt;Timer 노드&lt;/b&gt;를 사용하거나, &lt;b data-index-in-node=&quot;157&quot; data-path-to-node=&quot;2&quot;&gt;코드로 직접 타이머를 생성&lt;/b&gt;하는 두 가지 방법을 제공합니다.&lt;/p&gt;
&lt;h3 data-path-to-node=&quot;4&quot; data-ke-size=&quot;size23&quot;&gt;1. Timer 노드 활용하기: 에디터에서 직관적으로 관리하기&lt;/h3&gt;
&lt;p data-path-to-node=&quot;5&quot; data-ke-size=&quot;size16&quot;&gt;가장 시각적이고 직관적인 방법은 씬(Scene) 구조에 Timer 노드를 직접 추가하는 것입니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;6&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;6,0,0&quot;&gt;주요 속성(Inspector 창):&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;6,0,1&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;6,0,1,0,0&quot;&gt;Wait Time:&lt;/b&gt; 타이머가 작동할 시간(초 단위)입니다. (예: 2.0 = 2초)&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;6,0,1,1,0&quot;&gt;One Shot:&lt;/b&gt; 이 옵션이 켜져 있으면(true) 시간이 다 된 후 한 번만 작동하고 멈춥니다. 꺼져 있으면(false) 무한히 반복됩니다.&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;6,0,1,2,0&quot;&gt;Autostart:&lt;/b&gt; 이 옵션이 켜져 있으면 게임이 시작되자마자 타이머가 자동으로 굴러갑니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;6,1,0&quot;&gt;시그널(Signal) 연결:&lt;/b&gt;&lt;/li&gt;
&lt;li data-path-to-node=&quot;6,1,0&quot;&gt;타이머의 시간이 모두 흐르면 timeout이라는 시그널이 발생합니다. 이 시그널을 스크립트에 연결하여 원하는 코드를 실행합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;div data-ved=&quot;0CAAQhtANahgKEwjqttaiucCVAxUAAAAAHQAAAAAQmwE&quot; data-hveid=&quot;0&quot;&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;&lt;span&gt;GDScript&lt;/span&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;pre class=&quot;autoit&quot;&gt;&lt;code&gt;@onready var my_timer = $Timer

func _ready():
    # Autostart를 켜지 않았다면, 코드로 타이머를 시작할 수 있습니다.
    my_timer.start() 

# Timer 노드의 timeout 시그널과 연결된 함수입니다.
func _on_timer_timeout():
    print(&quot;시간이 다 되었습니다! 몬스터를 소환합니다.&quot;)
    
    # One Shot이 켜져 있다면, 필요할 때 다시 start()를 호출해 재사용할 수 있습니다.
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;h3 data-path-to-node=&quot;9&quot; data-ke-size=&quot;size23&quot;&gt;2. 코드로 타이머 생성하기: 깔끔하고 유연하게 제어하기&lt;/h3&gt;
&lt;p data-path-to-node=&quot;10&quot; data-ke-size=&quot;size16&quot;&gt;씬 트리에 구구절절 노드를 추가하기 귀찮거나, 특정 상황에서만 잠깐 쓰고 버릴 타이머가 필요하다면 스크립트 안에서 즉석으로 타이머를 만들어 쓸 수 있습니다. 고도 엔진 4에서는 get_tree().create_timer()를 사용하면 단 한 줄로 이 작업을 수행할 수 있습니다.&lt;/p&gt;
&lt;p data-path-to-node=&quot;11&quot; data-ke-size=&quot;size16&quot;&gt;이 방식은 특히 &lt;b data-index-in-node=&quot;9&quot; data-path-to-node=&quot;11&quot;&gt;await 키워드&lt;/b&gt;와 결합했을 때 엄청난 시너지를 냅니다. 코드가 실행되다가 타이머가 끝날 때까지 그 자리에서 일시 정지(대기)하게 만들 수 있기 때문입니다.&lt;/p&gt;
&lt;div data-ved=&quot;0CAAQhtANahgKEwjqttaiucCVAxUAAAAAHQAAAAAQnAE&quot; data-hveid=&quot;0&quot;&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;&lt;span&gt;GDScript&lt;/span&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;pre class=&quot;autoit&quot;&gt;&lt;code&gt;func cast_spell():
    print(&quot;마법 캐스팅 시작... (2초 소요)&quot;)
    
    # 1. 2초짜리 일회성 타이머를 생성하고, timeout 시그널이 올 때까지 코드 실행을 '대기'합니다.
    await get_tree().create_timer(2.0).timeout
    
    print(&quot;마법 발사!!&quot;)

func hit_effect():
    # 2. 피격 시 캐릭터를 빨갛게 바꿨다가 0.1초 뒤에 원래대로 되돌리는 연출
    modulate = Color.RED
    await get_tree().create_timer(0.1).timeout
    modulate = Color.WHITE
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;h3 data-path-to-node=&quot;14&quot; data-ke-size=&quot;size23&quot;&gt;3. 고급 팁: 씬 트리(Scene Tree) 타이머 vs 일반 내장 타이머&lt;/h3&gt;
&lt;p data-path-to-node=&quot;15&quot; data-ke-size=&quot;size16&quot;&gt;get_tree().create_timer()로 만든 타이머는 게임 속 세상의 시간 흐름을 따릅니다. 만약 게임을 일시 정지(get_tree().paused = true)시키면 이 타이머도 함께 멈춥니다.&lt;/p&gt;
&lt;p data-path-to-node=&quot;16&quot; data-ke-size=&quot;size16&quot;&gt;만약 일시 정지 화면(메뉴 창 등)에서 게임 시간과 상관없이 독립적으로 작동하는 타이머를 코드로 만들고 싶다면, 아래와 같이 직접 Timer 객체를 생성해서 자식으로 등록해 주어야 합니다.&lt;/p&gt;
&lt;div data-ved=&quot;0CAAQhtANahgKEwjqttaiucCVAxUAAAAAHQAAAAAQnQE&quot; data-hveid=&quot;0&quot;&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;&lt;span&gt;GDScript&lt;/span&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;pre class=&quot;autoit&quot;&gt;&lt;code&gt;func create_custom_timer():
    # 메모리상에 타이머 객체를 만듭니다.
    var custom_timer = Timer.new()
    custom_timer.wait_time = 3.0
    custom_timer.one_shot = true
    
    # 일시 정지 상태에서도 이 타이머는 무시하고 계속 흘러가도록 설정합니다.
    custom_timer.process_mode = Node.PROCESS_MODE_ALWAYS
    
    # 씬 트리에 추가하고 시그널을 코드로 연결합니다.
    add_child(custom_timer)
    custom_timer.timeout.connect(func(): print(&quot;일시정지 중에도 작동하는 타이머 완료!&quot;))
    
    custom_timer.start()
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;h3 data-path-to-node=&quot;19&quot; data-ke-size=&quot;size23&quot;&gt;한눈에 보는 시간 제어 핵심 요약&lt;/h3&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-path-to-node=&quot;20&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;방식&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&lt;b&gt;기능 설명&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&lt;b&gt;실전 타이머 제어 활용 예시&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span data-path-to-node=&quot;20,1,0,0&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;20,1,0,0&quot;&gt;Timer 노드 (에디터)&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span data-path-to-node=&quot;20,1,1,0&quot;&gt;씬 구조에 노드를 배치하고 timeout 시그널을 연결해 반복 동작을 수행합니다.&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span data-path-to-node=&quot;20,1,2,0&quot;&gt;주기적으로 총알을 쏘는 포탑, 1초마다 독 데미지를 입히는 장판&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span data-path-to-node=&quot;20,2,0,0&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;20,2,0,0&quot;&gt;await ... create_timer(초).timeout&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span data-path-to-node=&quot;20,2,1,0&quot;&gt;코드로 즉석 일회성 타이머를 생성하고, 시간이 다 될 때까지 코드를 일시 중단합니다.&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span data-path-to-node=&quot;20,2,2,0&quot;&gt;스킬 시전 대기 시간(딜레이), 콤보 공격 유효 시간 계산, 피격 반짝임 연출&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span data-path-to-node=&quot;20,3,0,0&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;20,3,0,0&quot;&gt;Timer.Wait Time&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span data-path-to-node=&quot;20,3,1,0&quot;&gt;타이머가 만료되기까지 걸리는 시간 설정 속성입니다.&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span data-path-to-node=&quot;20,3,2,0&quot;&gt;쿨타임을 5초에서 대장장이 강화 후 4초로 줄이는 로직 구현 등&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span data-path-to-node=&quot;20,4,0,0&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;20,4,0,0&quot;&gt;Timer.One Shot&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span data-path-to-node=&quot;20,4,1,0&quot;&gt;타이머의 반복 여부를 결정합니다. (true=1회성, false=무한 반복)&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span data-path-to-node=&quot;20,4,2,0&quot;&gt;true: 대시(Dash) 지속시간 제어 / false: 초당 골드 자동 획득 시스템&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;</description>
      <category>게임/고도엔진 문법</category>
      <category>gdscript</category>
      <category>godot engine</category>
      <category>고도 엔진</category>
      <author>밀감감</author>
      <guid isPermaLink="true">https://milgamshumi.tistory.com/24</guid>
      <comments>https://milgamshumi.tistory.com/24#entry24comment</comments>
      <pubDate>Sat, 11 Jul 2026 13:17:04 +0900</pubDate>
    </item>
    <item>
      <title>23. [고도 엔진] 예측 불가능한 재미: 난수 생성기(randi, randf) 활용법</title>
      <link>https://milgamshumi.tistory.com/23</link>
      <description>&lt;p data-path-to-node=&quot;0&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-path-to-node=&quot;0&quot; data-ke-size=&quot;size16&quot;&gt;이 카테고리는 고도 엔진(Godot Engine) 관련 문법을 기록하고 저장하기 위해 만든 공간입니다.&lt;/p&gt;
&lt;p data-path-to-node=&quot;1&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-path-to-node=&quot;1&quot; data-ke-size=&quot;size16&quot;&gt;이번에는 RPG 게임에서 검을 휘두를 때마다 데미지가 10~15 사이로 다르게 들어가고, 몬스터를 잡았을 때 30% 확률로 희귀한 아이템이 떨어지는 것을 본 적이 있으실 겁니다. 게임에 '예측 불가능한 재미(무작위성)'를 불어넣는 핵심 기술이 바로 난수(Random Number)입니다. 고도 엔진에서 가장 자주 쓰이는 난수 생성 함수들을 알아보겠습니다.&lt;/p&gt;
&lt;h3 data-path-to-node=&quot;4&quot; data-ke-size=&quot;size23&quot;&gt;1. randi_range(): 주사위 굴리기 (정수 난수)&lt;/h3&gt;
&lt;p data-path-to-node=&quot;5&quot; data-ke-size=&quot;size16&quot;&gt;randi는 Random Integer(무작위 정수)의 줄임말입니다. 기본 randi() 함수는 0부터 엄청나게 큰 숫자(약 42억) 사이의 무작위 숫자를 뱉어내기 때문에 그대로 쓰기에는 조금 불편합니다. 그래서 실전에서는 최소값과 최대값을 직접 지정할 수 있는 randi_range()를 훨씬 더 많이 사용합니다.&lt;/p&gt;
&lt;p data-path-to-node=&quot;5&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-path-to-node=&quot;5&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;GDScript&lt;/span&gt;&lt;/p&gt;
&lt;div data-ved=&quot;0CAAQhtANahcKEwjqttaiucCVAxUAAAAAHQAAAAAQcg&quot; data-hveid=&quot;0&quot;&gt;
&lt;div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;func attack():
    # 1. 10부터 15 사이의 '정수(10, 11, 12, 13, 14, 15)' 중 하나를 무작위로 뽑습니다.
    var damage = randi_range(10, 15)
    print(&quot;몬스터에게 &quot;, damage, &quot;의 데미지를 입혔습니다!&quot;)

    # 2. 1부터 6까지, 6면체 주사위를 굴립니다.
    var dice_roll = randi_range(1, 6)
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;h3 data-path-to-node=&quot;9&quot; data-ke-size=&quot;size23&quot;&gt;2. randf_range()와 randf(): 디테일한 조정과 확률 (실수 난수)&lt;/h3&gt;
&lt;p data-path-to-node=&quot;10&quot; data-ke-size=&quot;size16&quot;&gt;randf는 Random Float(무작위 실수/소수점)의 줄임말입니다. 정수와 달리 소수점 아래까지 쪼개지는 숫자가 필요할 때 사용합니다. 타이머의 대기 시간을 무작위로 설정하거나, 화면 내의 무작위 X 좌표에 몬스터를 소환할 때 유용합니다.&lt;/p&gt;
&lt;p data-path-to-node=&quot;11&quot; data-ke-size=&quot;size16&quot;&gt;특히 괄호 안이 비어있는 기본 randf()는 &lt;b data-index-in-node=&quot;26&quot; data-path-to-node=&quot;11&quot;&gt;0.0에서 1.0 사이의 실수&lt;/b&gt;를 반환하는데, 이를 활용하면 게임에서 'n% 확률로 성공' 같은 시스템을 아주 쉽게 만들 수 있습니다.&lt;/p&gt;
&lt;div data-ved=&quot;0CAAQhtANahcKEwjqttaiucCVAxUAAAAAHQAAAAAQcw&quot; data-hveid=&quot;0&quot;&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;&lt;span&gt;GDScript&lt;/span&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;func spawn_monster():
    # 1. 화면의 X좌표 100.0 ~ 800.0 사이의 임의의 위치(소수점 포함)를 뽑습니다.
    var random_x = randf_range(100.0, 800.0)
    position.x = random_x
    
func open_chest():
    # 2. 30% 확률로 희귀 아이템 드랍하기
    # randf()는 0.0 ~ 1.0을 반환하므로, 0.3 이하일 확률은 정확히 30%가 됩니다.
    if randf() &amp;lt;= 0.3:
        print(&quot;축하합니다! 전설의 검을 획득했습니다!&quot;)
    else:
        print(&quot;평범한 돌멩이가 나왔습니다.&quot;)
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;h3 data-path-to-node=&quot;14&quot; data-ke-size=&quot;size23&quot;&gt;3. pick_random(): 배열에서 아무거나 하나 뽑기 (보너스 팁)&lt;/h3&gt;
&lt;p data-path-to-node=&quot;15&quot; data-ke-size=&quot;size16&quot;&gt;고도 엔진의 배열(Array)에는 아주 강력하고 편리한 내장 함수인 pick_random()이 있습니다. 아이템 목록이나 적들의 이름 목록을 배열로 만들어두고, 그중 하나를 무작위로 추첨하고 싶을 때 복잡한 계산 없이 단 한 줄로 해결할 수 있습니다.&lt;/p&gt;
&lt;div data-ved=&quot;0CAAQhtANahcKEwjqttaiucCVAxUAAAAAHQAAAAAQdA&quot; data-hveid=&quot;0&quot;&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;&lt;span&gt;GDScript&lt;/span&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;pre class=&quot;go&quot;&gt;&lt;code&gt;var loot_table = [&quot;포션&quot;, &quot;골드&quot;, &quot;화살&quot;, &quot;폭탄&quot;]

func get_random_loot():
    # 배열에 들어있는 4개의 아이템 중 하나를 무작위로 선택해서 반환합니다.
    var dropped_item = loot_table.pick_random()
    print(dropped_item, &quot;을(를) 주웠습니다!&quot;)
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;h3 data-path-to-node=&quot;18&quot; data-ke-size=&quot;size23&quot;&gt;한눈에 보는 난수 생성 핵심 요약&lt;/h3&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-path-to-node=&quot;19&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;함수 / 기능&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&lt;b&gt;기능 설명&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&lt;b&gt;실전 디버깅 활용 예시&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span data-path-to-node=&quot;19,1,0,0&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;19,1,0,0&quot;&gt;randi_range(min, max)&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span data-path-to-node=&quot;19,1,1,0&quot;&gt;min과 max 사이의 무작위 &lt;b data-index-in-node=&quot;17&quot; data-path-to-node=&quot;19,1,1,0&quot;&gt;정수&lt;/b&gt;를 반환합니다. (min, max 포함)&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span data-path-to-node=&quot;19,1,2,0&quot;&gt;무기 데미지 계산 (10~15), 주사위 굴리기, 획득 골드량 결정&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span data-path-to-node=&quot;19,2,0,0&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;19,2,0,0&quot;&gt;randf_range(min, max)&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span data-path-to-node=&quot;19,2,1,0&quot;&gt;min과 max 사이의 무작위 **실수(소수점)**를 반환합니다.&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span data-path-to-node=&quot;19,2,2,0&quot;&gt;몬스터 스폰 위치(좌표) 결정, 1.5초~3.0초 사이의 랜덤한 공격 딜레이&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span data-path-to-node=&quot;19,3,0,0&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;19,3,0,0&quot;&gt;randf()&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span data-path-to-node=&quot;19,3,1,0&quot;&gt;0.0부터 1.0 사이의 무작위 실수를 반환합니다.&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span data-path-to-node=&quot;19,3,2,0&quot;&gt;0.1(10%), 0.5(50%) 등 &lt;b data-index-in-node=&quot;21&quot; data-path-to-node=&quot;19,3,2,0&quot;&gt;확률 시스템&lt;/b&gt;을 구현할 때 (if randf() &amp;lt;= 확률:)&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span data-path-to-node=&quot;19,4,0,0&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;19,4,0,0&quot;&gt;배열.pick_random()&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span data-path-to-node=&quot;19,4,1,0&quot;&gt;배열(Array) 안에 들어있는 데이터 중 하나를 무작위로 뽑습니다.&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span data-path-to-node=&quot;19,4,2,0&quot;&gt;보물상자에서 여러 종류의 아이템 중 하나를 무작위로 떨어뜨릴 때&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span data-path-to-node=&quot;19,5,0,0&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;19,5,0,0&quot;&gt;randi()&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span data-path-to-node=&quot;19,5,1,0&quot;&gt;0부터 약 42억 사이의 무작위 정수를 반환합니다.&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span data-path-to-node=&quot;19,5,2,0&quot;&gt;(범위가 너무 넓어 게임 플레이 로직보다는 암호화나 난수 시드 등에 주로 쓰임)&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>게임/고도엔진 문법</category>
      <category>gdscript</category>
      <category>godot engine</category>
      <category>고도 엔진</category>
      <author>밀감감</author>
      <guid isPermaLink="true">https://milgamshumi.tistory.com/23</guid>
      <comments>https://milgamshumi.tistory.com/23#entry23comment</comments>
      <pubDate>Sat, 11 Jul 2026 13:00:02 +0900</pubDate>
    </item>
    <item>
      <title>22. [고도 엔진] 게임 도중 총알 발사하기: 코드로 씬(Scene) 생성(instantiate)</title>
      <link>https://milgamshumi.tistory.com/22</link>
      <description>&lt;p data-path-to-node=&quot;0&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-path-to-node=&quot;0&quot; data-ke-size=&quot;size16&quot;&gt;이 카테고리는 고도 엔진(Godot Engine) 관련 문법을 기록하고 저장하기 위해 만든 공간입니다.&lt;/p&gt;
&lt;p data-path-to-node=&quot;0&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-path-to-node=&quot;1&quot; data-ke-size=&quot;size16&quot;&gt;이번에는 게임 플레이 도중 플레이어가 총을 쏘면 총알이 나타나고, 보스가 몬스터를 소환하며, 몬스터가 죽으면서 아이템을 떨어뜨립니다. 이처럼 게임이 실행되는 도중에 실시간으로 새로운 오브젝트를 만들어내는 것은 게임 개발의 핵심입니다. 고도 엔진에서는 미리 만들어둔 씬(Scene)을 코드를 통해 찍어내는(Instantiate) 방식으로 이 작업을 수행합니다. 총알 발사를 예시로 씬을 동적으로 생성하는 3단계를 알아보겠습니다.&lt;/p&gt;
&lt;h3 data-path-to-node=&quot;4&quot; data-ke-size=&quot;size23&quot;&gt;1. preload(): 씬 설계도 가져오기&lt;/h3&gt;
&lt;p data-path-to-node=&quot;5&quot; data-ke-size=&quot;size16&quot;&gt;무언가를 찍어내려면 먼저 '설계도'가 필요합니다. 고도 엔진에서는 미리 만들어 저장해 둔 .tscn 파일이 바로 설계도(PackedScene) 역할을 합니다. 스크립트 맨 위에서 preload() 함수를 사용해 이 씬 파일을 메모리에 미리 불러옵니다.&lt;/p&gt;
&lt;p data-path-to-node=&quot;5&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-path-to-node=&quot;5&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b data-path-to-node=&quot;6,0&quot; data-index-in-node=&quot;0&quot;&gt;참고:&lt;/b&gt;&lt;span style=&quot;color: #666666; text-align: center;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;파일 시스템(FileSystem) 패널에서 .tscn 파일을 클릭한 채로 스크립트 에디터 창으로 드래그 앤 드롭하면 복잡한 경로를 직접 칠 필요 없이 쉽게 입력할 수 있습니다.&lt;/span&gt;&lt;/p&gt;
&lt;div data-ved=&quot;0CAAQhtANahcKEwjqttaiucCVAxUAAAAAHQAAAAAQTA&quot; data-hveid=&quot;0&quot;&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;&lt;span&gt;GDScript&lt;/span&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;pre class=&quot;vala&quot;&gt;&lt;code&gt;# Bullet.tscn 씬 파일을 설계도로 사용하기 위해 불러옵니다.
# 씬 로드는 상수(const)나 @onready 변수에 할당하는 것이 일반적입니다.
const BULLET_SCENE = preload(&quot;res://Scenes/Bullet.tscn&quot;)
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;h3 data-path-to-node=&quot;9&quot; data-ke-size=&quot;size23&quot;&gt;2. instantiate(): 설계도로 실체(노드) 만들기&lt;/h3&gt;
&lt;p data-path-to-node=&quot;10&quot; data-ke-size=&quot;size16&quot;&gt;설계도를 가져왔다면, 이제 총을 쏠 때마다 이 설계도를 바탕으로 실제 '총알 노드'를 만들어야 합니다. 이때 사용하는 함수가 instantiate()입니다. 이 함수는 씬과 똑같이 생긴 복제본을 메모리상에 뚝딱 만들어냅니다.&lt;/p&gt;
&lt;p data-path-to-node=&quot;11&quot; data-ke-size=&quot;size16&quot;&gt;&lt;i data-index-in-node=&quot;0&quot; data-path-to-node=&quot;11&quot;&gt;(※ 참고: 고도 엔진 3버전에서 사용하던 instance() 함수가 4버전에서는 instantiate()로 이름이 변경되었습니다.)&lt;/i&gt;&lt;/p&gt;
&lt;div data-ved=&quot;0CAAQhtANahcKEwjqttaiucCVAxUAAAAAHQAAAAAQTQ&quot; data-hveid=&quot;0&quot;&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;&lt;span&gt;GDScript&lt;/span&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;pre class=&quot;mipsasm&quot;&gt;&lt;code&gt;func shoot():
    # 1. 씬 설계도를 바탕으로 실제 총알 노드(인스턴스)를 생성합니다.
    var bullet_instance = BULLET_SCENE.instantiate()
    
    # 2. 방금 만들어진 총알의 출발 위치를 플레이어(혹은 총구)의 위치로 맞춥니다.
    bullet_instance.global_position = self.global_position
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;h3 data-path-to-node=&quot;14&quot; data-ke-size=&quot;size23&quot;&gt;3. add_child(): 게임 세상(씬 트리)에 등장시키기&lt;/h3&gt;
&lt;p data-path-to-node=&quot;15&quot; data-ke-size=&quot;size16&quot;&gt;instantiate()로 총알을 만들고 위치까지 정해줬지만, 아직 게임 화면에는 보이지 않을 것입니다. 고도 엔진에서는 모든 노드가 '씬 트리(Scene Tree)'라는 족보에 등록되어야만 비로소 화면에 나타나고 작동하기 때문입니다. 마지막으로 add_child() 함수를 사용해 이 총알을 씬 트리에 추가해주면 발사 과정이 끝납니다!&lt;/p&gt;
&lt;div data-ved=&quot;0CAAQhtANahcKEwjqttaiucCVAxUAAAAAHQAAAAAQTg&quot; data-hveid=&quot;0&quot;&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;&lt;span&gt;GDScript&lt;/span&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;pre class=&quot;mipsasm&quot;&gt;&lt;code&gt;func shoot():
    var bullet_instance = BULLET_SCENE.instantiate()
    bullet_instance.global_position = self.global_position
    
    # 3. 씬 트리에 총알을 추가하여 게임 화면에 실제로 등장시킵니다.
    # (주의: 단순히 add_child(bullet_instance)를 하면 총알이 플레이어의 자식이 되어
    # 플레이어가 이동할 때 총알도 같이 따라다니는 어색한 상황이 발생할 수 있습니다.
    # 따라서 보통 씬 트리의 최상위 노드나 별도의 컨테이너 노드에 추가하는 것이 좋습니다.)
    get_tree().root.add_child(bullet_instance) 
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;h3 data-path-to-node=&quot;18&quot; data-ke-size=&quot;size23&quot;&gt;한눈에 보는 씬 생성(Instantiate) 핵심 요약&lt;/h3&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-path-to-node=&quot;19&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;함수 / 키워드&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&lt;b&gt;기능 설명&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&lt;b&gt;실전 활용 예시&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span data-path-to-node=&quot;19,1,0,0&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;19,1,0,0&quot;&gt;preload(경로)&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span data-path-to-node=&quot;19,1,1,0&quot;&gt;게임 실행 전, 지정된 경로의 씬(.tscn)을 미리 메모리에 불러옵니다.&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span data-path-to-node=&quot;19,1,2,0&quot;&gt;총알, 몬스터, 파티클 등 게임 도중 자주 생성되는 씬의 설계도를 저장해 둘 때&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span data-path-to-node=&quot;19,2,0,0&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;19,2,0,0&quot;&gt;instantiate()&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span data-path-to-node=&quot;19,2,1,0&quot;&gt;불러온 씬(PackedScene)을 바탕으로 실제 노드(복제본)를 메모리에 생성합니다.&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span data-path-to-node=&quot;19,2,2,0&quot;&gt;마우스 클릭이나 공격 버튼을 누를 때마다 새로운 총알 오브젝트를 만들어낼 때&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span data-path-to-node=&quot;19,3,0,0&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;19,3,0,0&quot;&gt;add_child(노드)&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span data-path-to-node=&quot;19,3,1,0&quot;&gt;생성된 노드를 씬 트리에 자식으로 추가하여 화면에 나타나게 합니다.&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span data-path-to-node=&quot;19,3,2,0&quot;&gt;만들어진 총알이나 소환된 몬스터를 게임 세상에 실제로 등장시킬 때&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span data-path-to-node=&quot;19,4,0,0&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;19,4,0,0&quot;&gt;queue_free()&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span data-path-to-node=&quot;19,4,1,0&quot;&gt;&lt;i data-index-in-node=&quot;0&quot; data-path-to-node=&quot;19,4,1,0&quot;&gt;(보너스)&lt;/i&gt; 노드를 안전하게 삭제하고 메모리를 비워줍니다.&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span data-path-to-node=&quot;19,4,2,0&quot;&gt;벽이나 몬스터에 맞거나 화면 밖으로 멀리 날아간 총알을 없앨 때&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;</description>
      <category>게임/고도엔진 문법</category>
      <category>gdscript</category>
      <category>godot engine</category>
      <category>고도 엔진</category>
      <author>밀감감</author>
      <guid isPermaLink="true">https://milgamshumi.tistory.com/22</guid>
      <comments>https://milgamshumi.tistory.com/22#entry22comment</comments>
      <pubDate>Fri, 10 Jul 2026 12:05:15 +0900</pubDate>
    </item>
    <item>
      <title>21. [고도 엔진] 키보드와 마우스 입력받기: Input 싱글톤과 _input() 함수</title>
      <link>https://milgamshumi.tistory.com/21</link>
      <description>&lt;p data-path-to-node=&quot;0&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-path-to-node=&quot;0&quot; data-ke-size=&quot;size16&quot;&gt;이 카테고리는 고도 엔진(Godot Engine) 관련 문법을 기록하고 저장하기 위해 만든 공간입니다.&lt;/p&gt;
&lt;p data-path-to-node=&quot;0&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-path-to-node=&quot;1&quot; data-ke-size=&quot;size16&quot;&gt;이번에는&amp;nbsp;게임에서 플레이어의 조작(입력)을 받는 것은 상호작용의 가장 기본적인 단계입니다. 캐릭터를 움직이거나, 총을 쏘고, UI 버튼을 클릭하는 모든 행동이 바로 이 '입력'에서 시작됩니다. 고도 엔진에서는 크게 두 가지 방법으로 입력을 처리할 수 있습니다. 바로 언제 어디서든 키보드나 마우스의 상태를 확인할 수 있는 Input 싱글톤과, 이벤트가 발생할 때마다 즉각적으로 반응하는 _input() 함수입니다.&lt;/p&gt;
&lt;h3 data-path-to-node=&quot;4&quot; data-ke-size=&quot;size23&quot;&gt;1. Input 싱글톤: 지금 버튼 누르고 있어? (상태 확인)&lt;/h3&gt;
&lt;p data-path-to-node=&quot;5&quot; data-ke-size=&quot;size16&quot;&gt;Input은 고도 엔진이 기본적으로 제공하는 전역 객체(싱글톤)입니다. 게임 루프(예: _process나 _physics_process) 안에서 &lt;b data-index-in-node=&quot;81&quot; data-path-to-node=&quot;5&quot;&gt;현재 특정 키가 눌려 있는지&lt;/b&gt; 매 프레임마다 확인할 때 주로 사용합니다. 캐릭터의 이동이나 자동 소총 발사처럼 '지속적인 눌림 상태'가 필요한 기능에 아주 적합합니다.&lt;/p&gt;
&lt;p data-path-to-node=&quot;5&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-path-to-node=&quot;5&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b data-path-to-node=&quot;6,0&quot; data-index-in-node=&quot;0&quot;&gt;참고:&lt;/b&gt;&lt;span style=&quot;color: #666666; text-align: center;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;고도 엔진의 상단 메뉴 **[프로젝트] -&amp;gt; [프로젝트 설정] -&amp;gt; [입력 맵(Input Map)]**에서 ui_right, ui_accept 같은 액션(Action) 이름과 단축키를 미리 정의해 두면 코드를 훨씬 깔끔하게 관리할 수 있습니다.&lt;/span&gt;&lt;/p&gt;
&lt;div data-ved=&quot;0CAAQhtANahcKEwjqttaiucCVAxUAAAAAHQAAAAAQIw&quot; data-hveid=&quot;0&quot;&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;&lt;span&gt;GDScript&lt;/span&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;pre class=&quot;autoit&quot;&gt;&lt;code&gt;func _process(delta):
    # 1. is_action_pressed: 'ui_right' 액션(보통 오른쪽 화살표)을 &quot;누르고 있는 동안&quot; 계속 실행됩니다.
    if Input.is_action_pressed(&quot;ui_right&quot;):
        position.x += 100 * delta
    
    # 2. is_action_just_pressed: 스페이스바를 &quot;방금 막 눌렀을 때&quot; (딱 한 번) 실행됩니다.
    if Input.is_action_just_pressed(&quot;ui_accept&quot;):
        print(&quot;점프!&quot;)
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;h3 data-path-to-node=&quot;9&quot; data-ke-size=&quot;size23&quot;&gt;2. _input(event) 함수(이벤트 반응)&lt;/h3&gt;
&lt;p data-path-to-node=&quot;10&quot; data-ke-size=&quot;size16&quot;&gt;_input(event)는 엔진이 새로운 입력을 감지할 때마다 자동으로 호출되는 내장 함수입니다. 마우스 클릭, 휠 굴리기, 키보드 타이핑 등 &lt;b data-index-in-node=&quot;80&quot; data-path-to-node=&quot;10&quot;&gt;순간적인 이벤트&lt;/b&gt;를 처리할 때 유용합니다. 매 프레임마다 확인하는 것이 아니라 입력이 들어올 때만 작동하므로, UI 창을 열고 닫거나 단발성 스킬을 사용하는 등의 작업에 훨씬 효율적입니다.&lt;/p&gt;
&lt;div data-ved=&quot;0CAAQhtANahcKEwjqttaiucCVAxUAAAAAHQAAAAAQJA&quot; data-hveid=&quot;0&quot;&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;&lt;span&gt;GDScript&lt;/span&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;pre class=&quot;cs&quot;&gt;&lt;code&gt;func _input(event):
    # 1. 마우스 버튼 클릭 이벤트인지 확인합니다.
    if event is InputEventMouseButton:
        # 왼쪽 마우스 버튼이 '눌린' 상태인지 검사합니다.
        if event.button_index == MOUSE_BUTTON_LEFT and event.pressed:
            print(&quot;마우스 왼쪽 버튼 클릭 위치: &quot;, event.position)
    
    # 2. 특정 액션 키(ESC 등)가 방금 눌렸는지 확인합니다.
    if event.is_action_pressed(&quot;ui_cancel&quot;):
        print(&quot;메뉴 창 열기!&quot;)
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;h3 data-path-to-node=&quot;13&quot; data-ke-size=&quot;size23&quot;&gt;3. 마우스 위치 가져오기&lt;/h3&gt;
&lt;p data-path-to-node=&quot;14&quot; data-ke-size=&quot;size16&quot;&gt;슈팅 게임에서 총구를 마우스 방향으로 돌리거나 마우스 커서가 있는 위치에 마법진을 생성하려면, 현재 마우스의 좌표를 알아내는 것이 중요합니다. 2D 게임에서는 노드의 내장 함수인 get_global_mouse_position()을 사용하여 게임 세계(월드) 기준의 마우스 좌표를 아주 쉽게 얻어올 수 있습니다.&lt;/p&gt;
&lt;div data-ved=&quot;0CAAQhtANahcKEwjqttaiucCVAxUAAAAAHQAAAAAQJQ&quot; data-hveid=&quot;0&quot;&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;&lt;span&gt;GDScript&lt;/span&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;pre class=&quot;autoit&quot;&gt;&lt;code&gt;func _process(delta):
    # 2D 게임의 경우, 게임 세계(Global) 기준 마우스 위치를 가져옵니다.
    var global_mouse_pos = get_global_mouse_position()
    
    # 플레이어 캐릭터가 항상 마우스 포인터 쪽을 바라보게 만듭니다.
    look_at(global_mouse_pos)
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;h3 data-path-to-node=&quot;17&quot; data-ke-size=&quot;size23&quot;&gt;한눈에 보는 입력 처리 핵심 요약&lt;/h3&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-path-to-node=&quot;18&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;함수 / 기능&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&lt;b&gt;기능 설명&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&lt;b&gt;실전 입력 처리 활용 예시&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span data-path-to-node=&quot;18,1,0,0&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;18,1,0,0&quot;&gt;Input.is_action_pressed()&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span data-path-to-node=&quot;18,1,1,0&quot;&gt;해당 키/액션이 &lt;b data-index-in-node=&quot;9&quot; data-path-to-node=&quot;18,1,1,0&quot;&gt;눌려있는 동안&lt;/b&gt; 계속 true를 반환합니다.&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span data-path-to-node=&quot;18,1,2,0&quot;&gt;캐릭터의 상하좌우 이동, 자동차 엑셀 밟기 등&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span data-path-to-node=&quot;18,2,0,0&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;18,2,0,0&quot;&gt;Input.is_action_just_pressed()&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span data-path-to-node=&quot;18,2,1,0&quot;&gt;해당 키/액션이 &lt;b data-index-in-node=&quot;9&quot; data-path-to-node=&quot;18,2,1,0&quot;&gt;방금 눌린 순간&lt;/b&gt;에만 1회 true를 반환합니다.&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span data-path-to-node=&quot;18,2,2,0&quot;&gt;단발성 점프, 공격 버튼, 아이템 사용 등&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span data-path-to-node=&quot;18,3,0,0&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;18,3,0,0&quot;&gt;_input(event)&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span data-path-to-node=&quot;18,3,1,0&quot;&gt;키보드, 마우스 등의 &lt;b data-index-in-node=&quot;12&quot; data-path-to-node=&quot;18,3,1,0&quot;&gt;입력 이벤트가 새롭게 발생할 때&lt;/b&gt; 호출됩니다.&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span data-path-to-node=&quot;18,3,2,0&quot;&gt;인벤토리 열기, 마우스 클릭 이벤트 감지 등&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span data-path-to-node=&quot;18,4,0,0&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;18,4,0,0&quot;&gt;get_global_mouse_position()&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span data-path-to-node=&quot;18,4,1,0&quot;&gt;현재 마우스 포인터의 &lt;b data-index-in-node=&quot;12&quot; data-path-to-node=&quot;18,4,1,0&quot;&gt;월드(Global) 좌표&lt;/b&gt;를 Vector2로 가져옵니다.&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span data-path-to-node=&quot;18,4,2,0&quot;&gt;캐릭터가 마우스 방향을 쳐다보거나 그 위치로 총알을 쏠 때&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;</description>
      <category>게임/고도엔진 문법</category>
      <category>gdscript</category>
      <category>godot engine</category>
      <category>고도 엔진</category>
      <author>밀감감</author>
      <guid isPermaLink="true">https://milgamshumi.tistory.com/21</guid>
      <comments>https://milgamshumi.tistory.com/21#entry21comment</comments>
      <pubDate>Thu, 9 Jul 2026 10:35:15 +0900</pubDate>
    </item>
    <item>
      <title>20. [고도 엔진] 에러 처리와 디버깅 팁 (assert, print_debug, 중단점 활용)</title>
      <link>https://milgamshumi.tistory.com/20</link>
      <description>&lt;p data-path-to-node=&quot;0&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-path-to-node=&quot;0&quot; data-ke-size=&quot;size16&quot;&gt;이 카테고리는 고도 엔진(Godot Engine) 관련 문법을 기록하고 저장하기 위해 만든 공간입니다.&lt;/p&gt;
&lt;p data-path-to-node=&quot;1&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-path-to-node=&quot;1&quot; data-ke-size=&quot;size16&quot;&gt;이번에는 '에러 처리와 디버깅 팁 (assert, print_debug, 중단점 활용)'입니다.&lt;/p&gt;
&lt;p data-path-to-node=&quot;2&quot; data-ke-size=&quot;size16&quot;&gt;게임 개발은 흔히 '20%의 코딩과 80%의 디버깅(버그 잡기)'으로 이루어져 있다고 합니다. 코드가 길어지고 씬이 복잡해지면 어디서 문제가 발생했는지 찾기가 모래사장에서 바늘 찾기처럼 어려워집니다. 이때 고도 엔진이 제공하는 강력한 디버깅 도구들을 활용하면, 버그가 숨어있는 정확한 위치와 원인을 순식간에 찾아낼 수 있습니다.&lt;/p&gt;
&lt;h3 data-path-to-node=&quot;4&quot; data-ke-size=&quot;size23&quot;&gt;1. print_debug(): 나 여기 있어요! (위치 추적기)&lt;/h3&gt;
&lt;p data-path-to-node=&quot;5&quot; data-ke-size=&quot;size16&quot;&gt;일반적인 print() 함수는 출력창에 글자만 달랑 띄워주기 때문에, 코드가 수천 줄이 넘어가면 이 메시지가 도대체 어디서 나온 건지 찾기 힘듭니다. 반면 print_debug()를 사용하면 메시지와 함께 '어떤 스크립트의 몇 번째 줄에서 실행되었는지'를 상세히 알려줍니다.&lt;/p&gt;
&lt;div data-ved=&quot;0CAAQhtANahgKEwj61vXnjKyVAxUAAAAAHQAAAAAQ_QI&quot; data-hveid=&quot;0&quot;&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;&lt;span&gt;GDScript&lt;/span&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;func take_damage(amount: int):
    hp -= amount
    
    # 출력 예시: 데미지 처리됨!
    # At: res://Scripts/Player.gd:15 - take_damage()
    print_debug(&quot;데미지 처리됨! 남은 체력: &quot;, hp)
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;h3 data-path-to-node=&quot;8&quot; data-ke-size=&quot;size23&quot;&gt;2. assert(): 조건이 안 맞으면 당장 멈춰! (안전띠)&lt;/h3&gt;
&lt;p data-path-to-node=&quot;9&quot; data-ke-size=&quot;size16&quot;&gt;assert()는 괄호 안의 조건이 '거짓(false)'일 경우, 즉시 게임을 일시 정지시키고 에러를 뿜어내는 아주 강력한 함수입니다. &quot;이 변수에는 절대 마이너스 값이 들어가면 안 돼!&quot; 혹은 &quot;이 노드는 무조건 존재해야 해!&quot;처럼, 게임이 치명적으로 망가지기 전에 미리 경고를 울리게 만드는 안전장치입니다. (※ 정식 출시(Export)된 게임에서는 무시되므로 성능에 영향을 주지 않습니다.)&lt;/p&gt;
&lt;div data-ved=&quot;0CAAQhtANahgKEwj61vXnjKyVAxUAAAAAHQAAAAAQ_gI&quot; data-hveid=&quot;0&quot;&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;&lt;span&gt;GDScript&lt;/span&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;pre class=&quot;autoit&quot;&gt;&lt;code&gt;@onready var weapon = $WeaponNode

func _ready():
    # 1. 무기 노드가 씬에 존재하는지(null이 아닌지) 강력하게 검사합니다.
    # 만약 경로가 틀려서 노드를 못 찾았다면 여기서 게임이 멈추고 메시지를 띄웁니다.
    assert(weapon != null, &quot;치명적 에러: 무기 노드를 찾을 수 없습니다!&quot;)
    
func heal(amount: int):
    # 2. 회복량이 0보다 큰지 검사합니다. (마이너스 회복 금지)
    assert(amount &amp;gt; 0, &quot;회복량은 반드시 0보다 커야 합니다!&quot;)
    hp += amount
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;h3 data-path-to-node=&quot;12&quot; data-ke-size=&quot;size23&quot;&gt;3. 중단점(Breakpoint): 시간 정지와 변수 관찰&lt;/h3&gt;
&lt;p data-path-to-node=&quot;13&quot; data-ke-size=&quot;size16&quot;&gt;코드가 도대체 어떻게 굴러가고 있는지 한 줄 한 줄 뜯어보고 싶을 때가 있습니다. 스크립트 에디터 창의 왼쪽 숫자(줄 번호) 옆 빈 공간을 클릭하면 빨간색 점(중단점)이 생깁니다. 게임이 실행되다가 이 줄에 도달하면 시간이 멈추며, 그때 하단 디버거(Debugger) 창에서 모든 변수에 현재 어떤 숫자가 들어있는지 낱낱이 확인할 수 있습니다. 코드로 직접 멈추고 싶다면 breakpoint 키워드를 쓰면 됩니다.&lt;/p&gt;
&lt;div data-ved=&quot;0CAAQhtANahgKEwj61vXnjKyVAxUAAAAAHQAAAAAQ_wI&quot; data-hveid=&quot;0&quot;&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;&lt;span&gt;GDScript&lt;/span&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;pre class=&quot;go&quot;&gt;&lt;code&gt;func calculate_complex_damage():
    var base_dmg = 50
    var critical_multiplier = 1.5
    var armor_reduction = 10
    
    # 게임이 여기서 멈춥니다! (이 시점의 위 변수 값들을 에디터에서 확인 가능)
    breakpoint 
    
    var final_dmg = (base_dmg * critical_multiplier) - armor_reduction
    return final_dmg
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;h3 data-path-to-node=&quot;17&quot; data-ke-size=&quot;size23&quot;&gt;한눈에 보는 디버깅 핵심 요약&lt;/h3&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-path-to-node=&quot;18&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;함수 / 기능&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&lt;b&gt;기능 설명&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&lt;b&gt;실전 디버깅 활용 예시&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span data-path-to-node=&quot;18,1,0,0&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;18,1,0,0&quot;&gt;print()&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span data-path-to-node=&quot;18,1,1,0&quot;&gt;가장 기본적인 출력 함수&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span data-path-to-node=&quot;18,1,2,0&quot;&gt;단순히 현재 점수나 상태를 콘솔 창에 띄워볼 때&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span data-path-to-node=&quot;18,2,0,0&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;18,2,0,0&quot;&gt;print_debug()&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span data-path-to-node=&quot;18,2,1,0&quot;&gt;메시지와 함께 호출된 스크립트 위치(줄 번호) 출력&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span data-path-to-node=&quot;18,2,2,0&quot;&gt;수많은 몬스터 중 도대체 어떤 몬스터가 에러를 냈는지 추적할 때&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span data-path-to-node=&quot;18,3,0,0&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;18,3,0,0&quot;&gt;assert(조건, 메시지)&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span data-path-to-node=&quot;18,3,1,0&quot;&gt;조건이 거짓일 때 에디터 실행을 멈추고 에러 발생시킴&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span data-path-to-node=&quot;18,3,2,0&quot;&gt;치명적인 버그(음수 체력, 없는 아이템 사용 등)를 사전에 차단할 때&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span data-path-to-node=&quot;18,4,0,0&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;18,4,0,0&quot;&gt;중단점 (Breakpoint)&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span data-path-to-node=&quot;18,4,1,0&quot;&gt;에디터 UI에서 빨간 점을 찍어 해당 줄에서 게임을 일시 정지&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span data-path-to-node=&quot;18,4,2,0&quot;&gt;복잡한 데미지 계산식에서 중간에 값이 어떻게 변하는지 관찰할 때&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span data-path-to-node=&quot;18,5,0,0&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;18,5,0,0&quot;&gt;breakpoint&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span data-path-to-node=&quot;18,5,1,0&quot;&gt;스크립트에 직접 적어 중단점과 동일하게 작동시키는 키워드&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span data-path-to-node=&quot;18,5,2,0&quot;&gt;특정 if 조건문에 진입했을 때만 게임을 멈춰서 확인하고 싶을 때&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;</description>
      <category>게임/고도엔진 문법</category>
      <category>gdscript</category>
      <category>godot engine</category>
      <category>고도 엔진</category>
      <author>밀감감</author>
      <guid isPermaLink="true">https://milgamshumi.tistory.com/20</guid>
      <comments>https://milgamshumi.tistory.com/20#entry20comment</comments>
      <pubDate>Wed, 8 Jul 2026 13:00:31 +0900</pubDate>
    </item>
  </channel>
</rss>