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.
Checking the stable version of the documentation...
물리 소개
게임 개발 중 종종 게임의 두 물체가 교차하거나 접촉할 때를 알아야 합니다. 이를 콜리전 감지라고 합니다. 콜리전이 감지될 때, 일반적이라면 무언가가 발생하길 원할겁니다. 이를 콜리전 응답이라고 합니다.
Godot는 콜리전 감지와 반응을 모두 제공하기 위해 2D와 3D로 구성된 다수의 콜리전 오브젝트를 제공합니다. 프로젝트에 이 중 어떠한 것을 사용할지 결정하려는 것은 혼란스러울 수 있습니다. 하지만 각각의 작동 방식과 장단점을 이해한다면 이러한 문제를 피하고 개발을 간소화할 수 있습니다.
이 가이드에서 다음 내용을 배울 것입니다:
Godot의 4가지 콜리전 오브젝트 유형
각 콜리전 오브젝트의 작동 방식
한 유형을 다른 유형보다 선택해야 하는 시기 및 이유
참고
이 문서의 예제에서는 2D 오브젝트를 사용합니다. 모든 2D 물리 오브젝트와 콜리전 모양은 3D에서 직접적으로 동등하며 대부분의 경우 거의 동일한 방식으로 작동합니다.
경고
Physics in Godot, regardless of physics engine, is not deterministic, the nature of physics engine determinism is very complex and has to do with many factors, this means physics is not guaranteed to run the same way for seemingly identical situations.
콜리전 오브젝트
Godot는 모두 :ref:`CollisionObject2D <class_CollisionObject2D>`을 확장하는 네 종류의 충돌 객체를 제공합니다. 아래 나열된 마지막 세 개는 물리 몸체이며 추가로 :ref:`PhysicsBody2D <class_PhysicsBody2D>`을 확장합니다.
- Area2D
Area2D노드는 감지와 영향력을 제공합니다. 그들은 오브젝트들이 언제 겹치는지를 감지할 수 있고, 바디가 들어가거나 나올 때 시그널을 방출할 수 있습니다.Area2D는 또한 정의된 영역에서 중력이나 제동과 같은 물리적 속성을 오버라이드하는 데에도 사용될 수 있습니다.
- StaticBody2D
정적 바디는 물리 엔진에 의해 움직이지 않는 물체입니다. 그것은 콜리전 감지에는 관여하지만, 콜리전에 대응하여 움직이지는 않습니다. 이것들은 환경의 일부이거나 동적 동작이 필요하지 않은 오브젝트에 가장 많이 사용됩니다.
- RigidBody2D
이것은 시뮬레이션된 2D 물리를 구현하는 노드입니다.
RigidBody2D를 직접적으로 제어하지는 않지만, 그대신 (중력, 충격 등) 힘을 가하며 물리 엔진은 그것의 결과적인 움직임을 계산합니다. 리지드 바디 사용에 대해 더 알아보기.
- Area2D
콜리전 감지 기능을 제공하지만, 물리는 제공하지 않는 body입니다. 모든 이동 및 콜리전 반응은 코드로 구현되어야 합니다.
물리 머티리얼
:ref:`PhysicsMaterial <class_PhysicsMaterial>`을 사용하도록 정적 몸체와 강체를 구성할 수 있습니다. 이를 통해 물체의 마찰과 바운스를 조정하고 흡수성 및/또는 거친지 설정할 수 있습니다.
콜리전 모양
물리 바디는 원하는 수의 Shape2D 오브젝트를 자식으로 수용할 수 있습니다. 이러한 모양은 오브젝트의 콜리전 경계를 정의하고 다른 오브젝트와의 접촉을 감지하는 데 사용됩니다.
참고
콜리전을 감지하기 위해, 최소한 하나 이상의 Shape2D가 오브젝트에 할당되어야 합니다.
모양을 지정하는 가장 일반적인 방법은 오브젝트의 자식으로 CollisionShape2D또는 CollisionPolygon2D를 추가하는 것입니다. 이러한 노드들은 편집기 작업 공간에서 직접 모양을 그릴 수 있도록 합니다.
중요
편집기에서 콜리전 모양을 변경하지 않도록 주의하세요. 인스펙터의 "Scale" 속성은 (1, 1)로 유지되어야 합니다. 콜리전 모양을 변경할 때는 항상 Node2D 크기 핸들이 아닌 크기 핸들을 사용해야 합니다. 모양의 크기를 조절하는 것은 예기치 않은 콜리전 행동이 일어날 수 있습니다.
물리 처리 콜백
물리 엔진은 고정된 속도(기본값은 초당 60회 반복)로 실행됩니다. 이 속도는 일반적으로 렌더링되는 내용과 사용 가능한 리소스에 따라 변동되는 프레임 속도와 다릅니다.
모든 물리 관련 코드가 이 고정 속도로 실행되는 것이 중요합니다. 따라서 Godot는 :ref:`물리와 유휴 처리 <doc_idle_and_physics_processing>`을 구별합니다. 각 프레임을 실행하는 코드를 유휴 처리라고 하며 각 물리 틱에서 실행되는 코드를 물리 처리라고 합니다. Godot는 각 처리 속도에 대해 하나씩 두 가지 다른 콜백을 제공합니다.
이러한 부정확성을 피하기 위해, 바디의 속성에 접근해야 하는 모든 코드는 각 물리 단계 전에 일정한 프레임률 (기본 초당 60회)로 호출되는 Node._physics_process() 콜백에서 실행되어야 합니다.
참고
물리 계산과 관련된 경우 항상 delta 매개변수를 사용하는 것이 좋습니다. 그러면 물리 업데이트 속도를 변경하거나 플레이어의 장치가 따라잡을 수 없는 경우 게임이 올바르게 작동합니다.
콜리전 레이어와 마스크
가장 강력하지만 자주 오해되는 콜리전 기능로 콜리전 레이어 시스템이 있습니다. 이 시스템을 사용하면 다양한 오브젝트 간의 복잡한 상호작용을 구축할 수 있습니다. 주요 개념은 레이어와 마스크입니다. 각 CollisionObject2D 에는 서로 상호작용할 수 있는 20개의 서로 다른 물리 레이어가 있습니다.
각 속성을 차례대로 살펴보겠습니다:
- collision_layer
이것은 오브젝트가 나타나는 레이어를 형성합니다. 기본적으로 모든 바디는 레이어 ``1``에 있습니다.
- collision_mask
이것은 바디가 콜리전을 위해 스캔하는 레이어를 뜻합니다. 오브젝트가 마스크 레이어 중 하나에 있지 않으면, 바디는 이를 무시합니다. 기본적으로 모든 바디는 레이어
1을 스캔합니다.
이러한 속성은 코드를 통해, 혹은 인스펙터에서 편집하여 구성할 수 있습니다.
각 레이어를 사용하는 대상을 추적하는 것은 어려울 수 있으므로, 사용 중인 레이어에 이름을 지정하는 것이 유용할 수 있습니다. 이름은 프로젝트 설정 -> Layer Names에서 지정할 수 있습니다.
예:
당신의 게임에는 4가지의 노드 타입이 있습니다: 벽, 플레이어, 적, 코인. 플레이어와 적 모두 벽과 충돌해야 합니다. 플레이어 노드는 적과 코인의 콜리전을 모두 감지해야 하지만, 적과 코인은 서로 무시해야 합니다.
레이어 1-4를 "walls", "player", "enemies", 그리고 "coins"으로 이름을 지정하는 걸로 시작하고, "Layer" 속성을 사용하여 각 노드 타입을 각각의 레이어에 배치합니다. 그런 다음 각 노드가 상호작용할 레이어를 선택하여 각 노드의 "Mask" 속성을 설정합니다. 예를 들어, 플레이어의 설정은 다음과 같습니다:
Timer 예제
함수 호출에서 레이어는 비트마스크로 지정됩니다. 기능이 기본적으로 모든 레이어를 활성화하는 경우 레이어 마스크는 ``0xffffffff``로 지정됩니다. 코드에서는 기본 설정에 따라 레이어 마스크에 이진수, 16진수 또는 십진수 표기법을 사용할 수 있습니다.
레이어 1, 3, 4가 활성화된 위 예제의 코드는 다음과 같습니다.
# Example: Setting mask value for enabling layers 1, 3 and 4
# Binary - set the bit corresponding to the layers you want to enable (1, 3, and 4) to 1, set all other bits to 0.
# Note: Layer 32 is the first bit, layer 1 is the last. The mask for layers 4, 3 and 1 is therefore:
0b00000000_00000000_00000000_00001101
# (This can be shortened to 0b1101)
# Hexadecimal equivalent (1101 binary converted to hexadecimal).
0x000d
# (This value can be shortened to 0xd.)
# Decimal - Add the results of 2 to the power of (layer to be enabled - 1).
# (2^(1-1)) + (2^(3-1)) + (2^(4-1)) = 1 + 4 + 8 = 13
#
# We can use the `<<` operator to shift the bit to the left by the layer number we want to enable.
# This is a faster way to multiply by powers of 2 than `pow()`.
# Additionally, we use the `|` (binary OR) operator to combine the results of each layer.
# This ensures we don't add the same layer multiple times, which would behave incorrectly.
(1 << 1 - 1) | (1 << 3 - 1) | (1 << 4 - 1)
# The above can alternatively be written as:
# pow(2, 1 - 1) + pow(2, 3 - 1) + pow(2, 4 - 1)
다음과 같이 주어진 CollisionObject2D <class_CollisionObject2D>`에 대해 ``set_collision_layer_value(layer_number, value)` 또는 ``set_collision_mask_value(layer_number, value)``를 호출하여 비트를 독립적으로 설정할 수도 있습니다.
# Example: Setting mask value to enable layers 1, 3, and 4.
var collider: CollisionObject2D = $CollisionObject2D # Any given collider.
collider.set_collision_mask_value(1, true)
collider.set_collision_mask_value(3, true)
collider.set_collision_mask_value(4, true)
내보내기 주석을 사용하면 사용자 친화적인 GUI를 통해 편집기에서 비트마스크를 내보낼 수 있습니다.
@export_flags_2d_physics var layers_2d_physics
2D 및 3D 모두에서 렌더링 및 탐색 레이어에 추가 내보내기 주석을 사용할 수 있습니다. :ref:`doc_gdscript_exports_exporting_bit_flags`를 참조하세요.
Area2D
Area 노드는 감지와 영향력을 제공합니다. 그들은 오브젝트들이 언제 겹치는지를 감지할 수 있고, 바디가 들어가거나 나올 때 시그널을 방출합니다. Area는 또한 정의된 영역에서 중력이나 제동과 같은 물리적 속성을 오버라이드하는 데에도 사용될 수 있습니다.
Area2D에는 3가지 주요 용도가 있습니다:
특정 지역에서 (중력과 같은) 물리적 매개변수를 오버라이드합니다.
다른 바디가 특정 지역에 들어오거나 나가는 때를 감지하거나 현재 지역에 있는 바디가 무엇인 지를 탐지합니다.
다른 영역들이 겹치는지 확인합니다.
기본적으로, 영역은 마우스 및 터치스크린 입력도 받습니다.
StaticBody2D
정적 바디는 물리 엔진에 의해 움직이지 않는 물체입니다. 그것은 콜리전 감지에는 참여하지만 콜리전에 대응하여 움직이지 않습니다. 그러나, constant_linear_velocity와 constant_angular_velocity의 속성을 이용하여 마치 움직이는 것처럼 충돌하는 바디에 움직임이나 회전을 전달할 수 있습니다.
StaticBody2D 노드는 환경에 속하거나 동적 동작을 수행할 필요가 없는 오브젝트에 가장 많이 사용됩니다.
StaticBody2D의 사용 예:
플랫폼 (이동 플랫폼 포함)
컨베이어 벨트
벽 및 기타 장애물
RigidBody2D
이것은 시뮬레이션 된 2D 물리를 구현하는 노드입니다. 당신은 RigidBody2D를 직접 제어하지는 않습니다. 대신 힘을 가하게 되면 물리 엔진은 다른 물체와의 콜리전을 포함한 결과 움직임과 바운스, 회전 등의 콜리전 반응을 계산합니다.
인스펙터에서 설정할 수 있는 "Mass", "Friction", 또는 "Bounce"와 같은 속성을 통해 리지드 바디의 행동을 수정할 수 있습니다.
바디의 행동은 또한 프로젝트 설정 > 물리에서 설정한 세계 속성의 영향을 받거나 전역 물리 속성을 오버라이딩하는 Area2D를 입력한 것에 영향을 받습니다.
리지드 바디가 쉬는 상태이고 한동안 움직이지 않으면, 잠들게 됩니다. 잠든 바디는 정적 바디처럼 작용하며, 그 힘은 물리 엔진에 의해 계산되지 않습니다. 콜리전 또는 코드를 통해 힘이 가해지면 바디가 깨어나게 됩니다.
RigidBody2D 사용하기
리지드 바디를 사용하는 것의 이점 중 하나는 코드를 쓰지 않고도 많은 행동을 "자유롭게" 할 수 있다는 것입니다. 예를 들어, 떨어지는 블록으로 "앵그리 버드"-스타일의 게임을 만드는 경우, RigidBody2D들을 만들고 해당 속성을 조정하기만 하면 됩니다. 쌓기, 낙하 및 튕김은 물리 엔진에 의해 자동으로 계산됩니다.
하지만, 만약 여러분이 바디를 어느 정도 통제하고 싶다면, 조심히 다루어야 합니다 - position, linear_velocity 또는 리지드 바디의 다른 물리적 속성을 바꾸면 예상치 못한 행동을 초래할 수 있습니다. 물리학 관련 속성을 변경해야 하는 경우 _physics_process() 대신 _integrate_forces() 콜백을 사용해야 합니다. 이 콜백에서는 안전하게 속성을 변경하고 물리 엔진과 동기화할 수 있는 Physics2DDirectBodyState에 접근할 수 있습니다.
예를 들어, 다음은 "소행성" 스타일 우주선의 코드입니다:
extends RigidBody2D
var thrust = Vector2(0, -250)
var torque = 20000
func _integrate_forces(state):
if Input.is_action_pressed("ui_up"):
state.apply_force(thrust.rotated(rotation))
else:
state.apply_force(Vector2())
var rotation_direction = 0
if Input.is_action_pressed("ui_right"):
rotation_direction += 1
if Input.is_action_pressed("ui_left"):
rotation_direction -= 1
state.apply_torque(rotation_direction * torque)
using Godot;
public partial class Spaceship : RigidBody2D
{
private Vector2 _thrust = new Vector2(0, -250);
private float _torque = 20000;
public override void _IntegrateForces(PhysicsDirectBodyState2D state)
{
if (Input.IsActionPressed("ui_up"))
{
state.ApplyForce(_thrust.Rotated(Rotation));
}
else
{
state.ApplyForce(new Vector2());
}
var rotationDir = 0;
if (Input.IsActionPressed("ui_right"))
{
rotationDir += 1;
}
if (Input.IsActionPressed("ui_left"))
{
rotationDir -= 1;
}
state.ApplyTorque(rotationDir * _torque);
}
}
linear_velocity 또는 angular_velocity를 직접 설정하는 것이 아니라 바디에 힘을 (thrust과 torque) 적용하고 그 결과적인 움직임을 물리 엔진이 계산하도록 한다는 점에 유의해야 합니다.
참고
리지드 바디가 잠에 들 때 _integrate_forces() 함수는 호출되지 않을 것입니다. 이 동작을 오버라이드 하려면 콜리전을 만들거나, 콜리전을 적용하거나, 힘을 가하거나, can_sleep 속성을 비활성화하여 바디를 깨어 있게 해야 합니다. 이것이 퍼포먼스에 안 좋은 영향을 줄 수 있으니 조심하세요.
접촉 알림
기본적으로 리지드 바디는 접촉면을 추적하지 않는데, 많은 바디가 씬 내에 있을 경우 엄청난 양의 메모리가 요구되기 때문입니다. 접촉 알림를 사용하려면 contacts_reported 속성을 0이 아닌 값으로 설정합니다. 그렇게 하면 접촉 알림은 Physics2DDirectBodyState.get_contact_count()이나 관련된 기능을 통해 얻을 수 있습니다.
시그널을 통한 접촉 모니터링은 contact_monitor 속성을 통해 활성화할 수 있습니다. 사용 가능한 시그널 목록은 RigidBody2D를 참조하세요.
캐릭터바디2D
KinematicBody2D 바디는 다른 물체와의 콜리전을 감지하지만 중력이나 마찰과 같은 물리적 성질의 영향을 받지 않습니다. 대신 코드를 통해 사용자가 제어해야 합니다. 물리 엔진은 키네마틱 바디를 움직이지 않습니다.
키네마틱 바디를 옮길 때는 위치를 직접 정해서는 안 됩니다. 대신 move_and_collide() 또는 move_and_slide() 메서드를 사용합니다. 이 메서드들은 주어진 벡터를 따라 바디를 움직이며, 다른 바디와의 콜리전이 감지되면 즉시 정지합니다. 바디가 충돌한 후에는 모든 콜리전 응답을 수동으로 코딩해야 합니다.
키네마틱 콜리전 응답
콜리전 후 바디가 튀어 오르거나, 벽을 따라 미끄러지거나, 부딪힌 오브젝트의 속성을 변경하기를 원할 수 있습니다. 콜리전 응답을 다루기 위해선 KinematicBody2D을 움직이기 위해 사용된 메서드에 의해 결정됩니다.
move_and_collide
이 함수는 move_and_collide()를 사용할 때 콜리전과 충돌하는 물체에 대한 정보가 포함된 KinematicCollision2D 오브젝트를 반환합니다. 이 정보를 사용하여 응답을 확인할 수 있습니다.
예를 들어, 콜리전이 발생한 공간에서 지점을 찾으려면 다음과 같이 하면 됩니다:
extends PhysicsBody2D
var velocity = Vector2(250, 250)
func _physics_process(delta):
var collision_info = move_and_collide(velocity * delta)
if collision_info:
var collision_point = collision_info.get_position()
using Godot;
public partial class Body : PhysicsBody2D
{
private Vector2 _velocity = new Vector2(250, 250);
public override void _PhysicsProcess(double delta)
{
var collisionInfo = MoveAndCollide(_velocity * (float)delta);
if (collisionInfo != null)
{
var collisionPoint = collisionInfo.GetPosition();
}
}
}
혹은 충돌 오브젝트가 튀어나오도록 하려면 다음과 같습니다:
extends PhysicsBody2D
var velocity = Vector2(250, 250)
func _physics_process(delta):
var collision_info = move_and_collide(velocity * delta)
if collision_info:
velocity = velocity.bounce(collision_info.get_normal())
using Godot;
public partial class Body : PhysicsBody2D
{
private Vector2 _velocity = new Vector2(250, 250);
public override void _PhysicsProcess(double delta)
{
var collisionInfo = MoveAndCollide(_velocity * (float)delta);
if (collisionInfo != null)
{
_velocity = _velocity.Bounce(collisionInfo.GetNormal());
}
}
}
move_and_slide
슬라이딩은 일반적인 콜리전 반응입니다. 플레이어가 탑뷰 게임에서 벽을 따라 움직이거나 플랫폼 안에서 오르내리는 경사를 상상해 보세요. move_and_collide()를 사용한 후 직접 이를 코딩할 수 있지만 move_and_slide()는 많은 코드를 작성하지 않고 슬라이딩 이동을 실행할 수 있는 편리한 방법입니다.
경고
move_and_slide() 는 자동으로 타임 스텝를 계산에 포함하므로, 속도 벡터에 delta를 곱하면 안 됩니다.
예를 들어, 다음 코드를 사용하여 지면(경사 포함)을 따라 걸을 수 있고 지면에 있을 때 점프할 수 있는 캐릭터를 만들 수 있습니다:
extends CharacterBody2D
var run_speed = 350
var jump_speed = -1000
var gravity = 2500
func get_input():
velocity.x = 0
var right = Input.is_action_pressed('ui_right')
var left = Input.is_action_pressed('ui_left')
var jump = Input.is_action_just_pressed('ui_select')
if is_on_floor() and jump:
velocity.y = jump_speed
if right:
velocity.x += run_speed
if left:
velocity.x -= run_speed
func _physics_process(delta):
velocity.y += gravity * delta
get_input()
move_and_slide()
using Godot;
public partial class Body : CharacterBody2D
{
private float _runSpeed = 350;
private float _jumpSpeed = -1000;
private float _gravity = 2500;
private void GetInput()
{
var velocity = Velocity;
velocity.X = 0;
var right = Input.IsActionPressed("ui_right");
var left = Input.IsActionPressed("ui_left");
var jump = Input.IsActionPressed("ui_select");
if (IsOnFloor() && jump)
{
velocity.Y = _jumpSpeed;
}
if (right)
{
velocity.X += _runSpeed;
}
if (left)
{
velocity.X -= _runSpeed;
}
Velocity = velocity;
}
public override void _PhysicsProcess(double delta)
{
var velocity = Velocity;
velocity.Y += _gravity * (float)delta;
Velocity = velocity;
GetInput();
MoveAndSlide();
}
}
자세한 코드가 포함된 데모 프로젝트를 포함하여 move_and_slide() 사용에 대한 자세한 사항은 키네마틱 캐릭터 (2D)를 참조하세요.