Introduction to physics in game development

In game development, you usually need to know when two objects in the game intersect or touch. This is called collision detection . When a collision is detected, you usually want something to happen. This is the so-called collision response .

Godot provides many collision objects in 2D and 3D to provide collision detection and response. Trying to determine which option to use for your project can be confusing. If you understand how each works and its advantages and disadvantages, you can avoid problems and simplify development.

In this guide, you will learn:

  • Godot's four types of collision objects
  • How each collision object works
  • When and why to choose one over the other

note

The examples in this document will use 2D objects. Every 2D physical object and collision shape has a direct equivalent function in 3D, and in most cases, they work almost the same way.

Collision object

Godot provides four physical bodies, which extend CollisionObject2D:

  • Area2D

The Area2D node provides detection and influence. They can detect when objects overlap and can signal when objects enter or leave. Area2D can also use an to cover physical properties in a defined area, such as gravity or damping.
The other three bodies extend PhysicsBody2D:

  • StaticBody2D

Static objects are objects that the physics engine does not move. It participates in collision detection, but does not move in response to collisions. They are most commonly used for objects in the environment or objects that do not require any dynamic behavior.

  • RigidBody2D

This is the node that can simulate 2D physics. You don't need RigidBody2D to directly control a, but apply force (gravity, pulse, etc.) to it, and then the physics engine will calculate the final movement. Read more about using rigid bodies.

  • KinematicBody2D

Objects that provide collision detection but no physics. All motion and collision responses must be implemented in code.
Collision shape A
physical object can have any number of Shape2D objects as sub-objects. These shapes are used to define the collision range of the object and detect contact with other objects.

note

In order to detect collisions, Shape2D must allocate at least one object.

The most common way to assign shapes is to add CollisionShape2D or CollisionPolygon2D as children of the object. These nodes allow you to draw shapes directly in the editor workspace.

important

Be careful not to scale the collision shape in the editor. The "scale" attribute in the inspector should be left as (1,1).
When changing the size of the collision shape, you should always use the size handle instead of the Node2D scale handle. Scaling the shape can cause unexpected collision behavior.

../../_images/player_coll_shape1.png

Physical process callback

The physics engine can spawn multiple threads to improve performance, so it can use at most one complete frame to process physics. Therefore, for the current frame, the value of the body's state variables (such as position or linear velocity) may not be accurate.

To avoid this inaccuracy, any code that needs to access human attributes should be run in the Node._physics_process() callback, which is called at a constant frame rate (60 times per second by default) before each physics step. The method will be passed a delta parameter, which is a floating-point number, which is equal to the elapsed time (in seconds) since the previous step. When using the default 60 Hz physical update rate, it is usually equal to 0.01666... ​​(but not always, see below).

note

It is recommended that delta always use relevant parameters in physics calculations so that the game can run correctly when you change the physics update rate or the player’s device cannot keep up.

Collision layer and mask

The collision layer system is one of the most powerful but often misunderstood collision features. The system allows you to establish complex interactions between various objects. The key concepts are layers and masks. Each CollisionObject2D has 20 different physical layers that can interact with it.

Let's look at each attribute in turn:

  • Collision layer

This describes the layer where the object appears. By default, all entities are on the layer 1.

  • Collision mask

This describes the layer that the body will scan for collision. If the object is not in one of the mask layers, the subject will ignore it. By default, all entities scan layer 1.
These properties can be configured by code or editing in the inspector.

Tracking the purpose of each layer can be difficult, so you may find it useful to assign names to the layers you use. You can assign a name in Project Settings -> Layer Name.

../../_images/physics_layer_names.png

GUI example

There are four types of nodes in the game: wall, player, enemy and coin. Both the player and the enemy should collide with the wall. The player node should detect collisions with enemies and coins at the same time, but the enemies and coins should ignore each other.

First name the first 1-4 layers "wall", "player", "enemy" and "coin", and then use the "layer" attribute to place each node type in its corresponding layer. Then set the "mask" property of each node by selecting the layer that each node should interact with. For example, the settings of the player are as follows:

../../_images/player_collision_layers.png
../../_images/player_collision_mask.png

Code example

In the function call, specify the layer as a bit mask. If the function enables all layers by default, the layer mask will be specified as 0x7fffffff. Your code can use binary, hexadecimal or decimal notation for the layer mask, depending on your preference.

The equivalent code of the above example with layers 1, 3, and 4 enabled is as follows:

# 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 20 is the first bit, layer 1 is the last. The mask for layers 4,3 and 1 is therefore
0b00000000000000001101
# (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 be enabled-1).
# (2^(1-1)) + (2^(3-1)) + (2^(4-1)) = 1 + 4 + 8 = 13
pow(2, 1) + pow(2, 3) + pow(2, 4)

Area2D

Regional nodes provide detection and influence. They can detect when objects overlap and signal when objects enter or leave. Areas can also be used to cover physical properties in defined areas, such as gravity or damping.

Area2D has three main uses:

  • Alternative physical parameters in a given area (such as gravity).
  • Detect when other objects enter or leave the area or what objects are in the current area.
  • Check if other areas overlap.

By default, the area also receives mouse and touch screen input.

StaticBody2D

Static objects are objects that the physics engine does not move. It participates in collision detection, but does not move in response to collisions. However, it can use its sum property to provide motion or rotation to the colliding object as if it were moving. constant_linear_velocityconstant_angular_velocity

The StaticBody2D node is most commonly used for objects in the environment or objects that do not require any dynamic behavior.

Example for StaticBody2D:

  • Platform (including mobile platform)
  • conveyor
  • Walls and other obstacles

RigidBody2D

This is the node that can simulate 2D physics. You cannot directly control RigidBody2D. Instead, you apply force to it, and the physics engine calculates the final movement, including collisions with other objects and collision responses (such as bounce, rotation, etc.).

You can modify the behavior of a rigid body with properties such as "mass", "friction" or "bounce", which can be set in the inspector.

The behavior of the human body is also affected by world attributes (such as those set in "Project Settings" -> "Physics"), or by the input of Area2D that covers global physical attributes.

When a rigid body is at rest and has not moved for a period of time, it will go to sleep. The function of a sleeping object is similar to a static object, and its force is not calculated by the physics engine. When force is applied through collision or through code, the body will wake up.

Rigid body mode The
rigid body can be set to one of the following four modes:

  • Rigidity-The body appears as a physical object. It collides with other objects and reacts to the force it exerts. This is the default mode.
  • Static-The body behaves like StaticBody2D and does not move.
  • Character-Similar to "rigid" mode, but the body cannot be rotated.
  • Movement-The behavior of the body is similar to KinematicBody2D and must be moved by code.


One of the benefits of using rigid bodies with RigidBody2D is that you can get many behaviors "for free" without writing any code. For example, if you want to make an "Angry Birds" style game with falling blocks, you only need to create RigidBody2Ds and adjust their properties. Stacking, falling and bouncing will be automatically calculated by the physics engine.

However, if you want to have some control over the body, you should take care-changing the position, linear_velocity, rigid body or other physical properties may cause unexpected behavior. If you need to change any physics-related properties, you should use the _integrate_forces() callback instead of _physics_process(). In this callback, you can access the Physics2DDirectBodyState of the human body, which can safely change properties and synchronize it with the physics engine.

For example, here is the code for the "asteroid" spacecraft:

class Spaceship : RigidBody2D
{
    
    
    private Vector2 _thrust = new Vector2(0, 250);
    private float _torque = 20000;

    public override void _IntegrateForces(Physics2DDirectBodyState state)
    {
    
    
        if (Input.IsActionPressed("ui_up"))
            SetAppliedForce(_thrust.Rotated(Rotation));
        else
            SetAppliedForce(new Vector2());

        var rotationDir = 0;
        if (Input.IsActionPressed("ui_right"))
            rotationDir += 1;
        if (Input.IsActionPressed("ui_left"))
            rotationDir -= 1;
        SetAppliedTorque(rotationDir * _torque);
    }
}

Please note that instead of setting linear_velocity or angular_velocity properties directly, we apply forces (thrust and torque) to the object, and then let the physics engine calculate the resulting motion.

note

When the rigid body goes to sleep, _integrate_forces()
will not call this function. To override this behavior, you will need
to keep the body awake by creating a collision, applying force to it, or disabling the can_sleep attribute. Note that this may have a negative impact on performance.

Contact report
By default, rigid bodies do not track contact, because if there are many rigid bodies in the scene, this may require a lot of memory. To enable contact reporting, set the contacts_reported property to a non-zero value. Then you can get the contact through Physics2DDirectBodyState.get_contact_count() and related functions.

The contact monitoring via signal can be enabled through the contact_monitor attribute. For a list of available signals, see RigidBody2D.

KinematicBody2D

KinematicBody2D entities can detect collisions with other entities, but are not affected by physical properties such as gravity or friction. Instead, they must be controlled by the user through code. The physics engine does not move moving bodies.

When moving the motion mechanism, please do not set it directly. Instead, use the move_and_collide() ormove_and_slide() method. These methods move an object along a given vector, and if a collision with another object is detected, it will stop immediately. After a body collision, any collision response must be manually coded.

Motion collision response After
a collision, you may want your body to bounce, slide along a wall, or change the properties of the object it hits. The way you handle the collision response depends on the method you use to move KinematicBody2D.

When move_and_collide is
used, move_and_collide(), this function returns a KinematicCollision2D object, which contains information about collisions and collision bodies. You can use this information to determine the response.

For example, if you want to find a point in space where a collision occurs:

class Body : KinematicBody2D
{
    
    
    private Vector2 _velocity = new Vector2(250, 250);

    public override void _PhysicsProcess(float delta)
    {
    
    
        var collisionInfo = MoveAndCollide(_velocity * delta);
        if (collisionInfo != null)
        {
    
    
            var collisionPoint = collisionInfo.GetPosition();
        }
    }
}

Or bounce off the collision object:

class Body : KinematicBody2D
{
    
    
    private Vector2 _velocity = new Vector2(250, 250);

    public override void _PhysicsProcess(float delta)
    {
    
    
        var collisionInfo = MoveAndCollide(_velocity * delta);
        if (collisionInfo != null)
            _velocity = _velocity.Bounce(collisionInfo.Normal);
    }
}
move_and_slide

Sliding is a common collision reaction. Imagine a player moving along a wall in a top-down game, or moving up and down a slope in a platform game. Although you can write this kind of reaction after you can use move_and_collide(), move_and_slide() provides a convenient way to achieve sliding without writing a lot of code.

caveat

move_and_slide() automatically includes the time step in the calculation, so there should be no multiplied velocity vector passing delta.

For example, use the following code to make a character that can walk on the ground (including slopes) and jump when standing on the ground:

class Body : KinematicBody2D
{
    
    
    private float _runSpeed = 350;
    private float _jumpSpeed = -1000;
    private float _gravity = 2500;

    private Vector2 _velocity = new Vector2();

    private void GetInput()
    {
    
    
        _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;
    }

    public override void _PhysicsProcess(float delta)
    {
    
    
        _velocity.y += _gravity * delta;
        GetInput();
        _velocity = MoveAndSlide(velocity, new Vector2(0,-1));
    }
}

For more detailed information on usage, see the moving character (2D) move_and_slide(), including a demo project with detailed code.

Guess you like

Origin blog.csdn.net/qq_44273429/article/details/111253654