Attention: Here be dragons

This is the latest (unstable) version of this documentation, which may document features not available in or compatible with released stable versions of Godot.

몹 씬 설계하기

이 파트에서는 몹이라고 부르는 괴물을 코딩해 보겠습니다. 다음 단원에서는 몹을 플레이 가능한 영역에 무작위로 소환하겠습니다.

새 씬에서 몬스터 자체를 디자인해 봅시다. 노드 구조는 player.tscn 씬과 비슷할 것입니다.

다시 한 번 CharacterBody3D 노드를 루트로 하여 씬을 생성합니다. 이름을 Mob으로 지정합니다. 자식 노드인 Node3D를 추가하고 이름을 Pivot으로 지정합니다. 그리고 파일시스템 독에서 mob.glb 파일을 Pivot으로 끌어다 놓아 몬스터의 3D 모델을 씬에 추가합니다.

../../_images/drag_drop_mob.webp

새로 만든 mob 노드의 이름을 Character로 변경할 수 있습니다.

image0

바디가 동작하려면 콜리전 모양이 필요합니다. 씬의 루트인 Mob 노드를 마우스 오른쪽 버튼으로 클릭하고 자식 노드 추가를 클릭합니다.

image1

CollisionShape3D를 추가합니다.

image2

인스펙터에서 BoxShape3D모양속성에 할당합니다.

../../_images/08.create_box_shape3D.webp

3D 모델에 더 잘 맞도록 크기를 변경해야 합니다. 주황색 점을 클릭하고 드래그하여 대화형으로 변경할 수 있습니다.

상자는 바닥에 닿아야 하며 모델보다 약간 얇아야 합니다. 물리 엔진은 플레이어의 구체가 상자의 모서리에 닿으면 콜리전이 발생하는 방식으로 작동합니다. 상자가 3D 모델에 비해 너무 크면 몬스터와 멀리 떨어진 곳에서 죽을 수 있으며 플레이어에게 게임이 불공평하게 느껴질 수 있습니다.

image4

내 상자가 괴물보다 더 큰 것을 알 수 있습니다. 이 게임에서는 위에서 고정된 시점을 사용하여 장면을 보고 있기 때문에 괜찮습니다. 콜리전 모양이 모델과 정확히 일치할 필요는 없습니다. 테스트할 때 게임의 느낌에 따라 형태와 크기를 결정해야 합니다.

화면 밖에서 괴물 제거하기

게임 레벨에서 일정한 시간 간격으로 괴물을 소환할 것입니다. 조심하지 않으면 괴물의 수가 무한대로 늘어날 수 있는데, 이런 상황을 원하지 않습니다. 각 몹 인스턴스에는 메모리와 처리 비용이 모두 있으며, 몹이 화면 밖에 있을 때는 비용을 지불하고 싶지 않습니다.

몬스터가 화면을 떠나면 더 이상 필요하지 않으므로 삭제해야 합니다. 고닷에는 오브젝트가 화면을 떠나는 시점을 감지하는 노드인 :ref:`VisibleOnScreenNotifier3D <class_VisibleOnScreenNotifier3D>`가 있으며, 이를 사용하여 몹을 파괴할 것입니다.

참고

객체를 계속 인스턴스화할 때 인스턴스를 항상 생성하고 소멸하는 비용을 피하기 위해 풀링이라는 기술을 사용할 수 있습니다. 풀링은 객체 배열을 미리 생성하고 이를 반복해서 재사용하는 방식으로 구성됩니다.

When working with GDScript, this usually isn't needed. The main reason to use pools is to avoid freezes with garbage-collected languages like C# or Lua. GDScript uses a different technique to manage memory, reference counting, which doesn't have that caveat. You can learn more about that here: 메모리 관리.

Mob 노드를 선택하고 자식 노드 :ref:`VisibleOnScreenNotifier3D <class_VisibleOnScreenNotifier3D>`를 추가합니다. 이번에는 분홍색의 또 다른 상자가 나타납니다. 이 상자가 화면에서 완전히 사라지면 노드는 신호를 방출합니다.

image5

전체 3D 모델을 덮을 때까지 주황색 점을 사용하여 크기를 조절합니다.

|image1|

몹의 움직임 코딩하기

몬스터의 동작을 구현해 봅시다. 이 작업은 두 단계로 진행하겠습니다. 먼저 몬스터를 초기화하는 함수를 정의하는 스크립트를 Mob 에 작성합니다. 그런 다음 main.tscn 씬에서 무작위 소환 메커니즘을 코딩하고 거기에서 함수를 호출합니다.

노드에 스크립트를 추가하기.

|image7|

먼저 시작할 이동 코드는 다음과 같습니다. min_speedmax_speed 라는 두 프로퍼티를 정의하여 임의의 속도 범위를 정의하고, 나중에 CharacterBody3D.velocity 를 정의하는 데 사용할 것입니다.

extends CharacterBody3D

# Minimum speed of the mob in meters per second.
@export var min_speed = 10
# Maximum speed of the mob in meters per second.
@export var max_speed = 18


func _physics_process(_delta):
    move_and_slide()

플레이어와 마찬가지로 CharacterBody3D.move_and_slide() 함수를 호출하여 몹을 매 프레임마다 이동시킵니다. 이번에는 매 프레임마다 ``velocity``를 업데이트하지 않고, 몬스터가 장애물에 부딪히더라도 일정한 속도로 움직이며 화면을 벗어나도록 합니다.

CharacterBody3D.velocity 를 계산하기 위해 다른 함수를 정의해야 합니다. 이 함수는 몬스터가 플레이어를 향해 움직이고 그 각도와 속도를 모두 무작위화합니다.

이 함수는 몹의 소환 위치인 start_positionplayer_position을 인수로 받습니다.

몹을 start_position 에 배치하고 look_at_from_position() 메서드를 사용하여 플레이어를 향해 돌리고, Y축을 중심으로 임의의 양을 회전시켜 각도를 랜덤화합니다. 아래 randf_range()-PI / 4 라디안과 PI / 4 라디안 사이의 임의의 값을 출력합니다.

# This function will be called from the Main scene.
func initialize(start_position, player_position):
    # We position the mob by placing it at start_position
    # and rotate it towards player_position, so it looks at the player.
    look_at_from_position(start_position, player_position, Vector3.UP)
    # Rotate this mob randomly within range of -45 and +45 degrees,
    # so that it doesn't move directly towards the player.
    rotate_y(randf_range(-PI / 4, PI / 4))

임의의 위치를 얻었으니 이제 random_speed 가 필요합니다. randi_range() 는 임의의 정수 값을 제공하므로 유용하며, min_speedmax_speed 를 사용합니다. random_speed 는 그냥 정수이며, CharacterBody3D.velocity 를 곱하는 데 사용합니다. random_speed 가 적용된 후 CharacterBody3D.velocity Vector3를 플레이어를 향해 회전시킵니다.

func initialize(start_position, player_position):
    # ...

    # We calculate a random speed (integer)
    var random_speed = randi_range(min_speed, max_speed)
    # We calculate a forward velocity that represents the speed.
    velocity = Vector3.FORWARD * random_speed
    # We then rotate the velocity vector based on the mob's Y rotation
    # in order to move in the direction the mob is looking.
    velocity = velocity.rotated(Vector3.UP, rotation.y)

화면 떠나기

몹이 화면을 떠날 때 파괴해야 합니다. 이를 위해 VisibleOnScreenNotifier3D 노드의 screen_exited 신호를 Mob 에 연결합니다.

VisibleOnScreenNotifier3D 노드를 선택하고 인터페이스의 오른쪽에서 노드 독으로 이동합니다. screen_exited() 시그널을 더블 클릭합니다.

|image9|

시그널을 Mob에 연결합니다

|image10|

이렇게 하면 몹 스크립트에 _on_visible_on_screen_notifier_3d_screen_exited() 라는 새 함수가 추가됩니다. 이 함수에서 queue_free() 메서드를 호출합니다. 이 함수는 호출된 인스턴스를 파괴합니다.

func _on_visible_on_screen_notifier_3d_screen_exited():
    queue_free()

괴물이 게임에 참가할 준비가 되었습니다! 다음 부분에서는 게임 레벨에 괴물을 소환합니다.

다음은 참조할 수 있는 전체 Mob.gd 스크립트입니다.

extends CharacterBody3D

# Minimum speed of the mob in meters per second.
@export var min_speed = 10
# Maximum speed of the mob in meters per second.
@export var max_speed = 18

func _physics_process(_delta):
    move_and_slide()

# This function will be called from the Main scene.
func initialize(start_position, player_position):
    # We position the mob by placing it at start_position
    # and rotate it towards player_position, so it looks at the player.
    look_at_from_position(start_position, player_position, Vector3.UP)
    # Rotate this mob randomly within range of -45 and +45 degrees,
    # so that it doesn't move directly towards the player.
    rotate_y(randf_range(-PI / 4, PI / 4))

    # We calculate a random speed (integer)
    var random_speed = randi_range(min_speed, max_speed)
    # We calculate a forward velocity that represents the speed.
    velocity = Vector3.FORWARD * random_speed
    # We then rotate the velocity vector based on the mob's Y rotation
    # in order to move in the direction the mob is looking.
    velocity = velocity.rotated(Vector3.UP, rotation.y)

func _on_visible_on_screen_notifier_3d_screen_exited():
    queue_free()