Physics in game development using KinematicBody2D

Introduction

Godot provides multiple collision objects to provide collision detection and response. Trying to determine which option to use for your project can be confusing. If you understand the working principle and advantages and disadvantages of each problem, you can avoid these problems and simplify development. In this tutorial, we will study the KinematicBody2D node and show some examples of using it.

note

This document assumes that you are familiar with the various physical institutions of Godot. Please read the introduction to physics first .

What is a sports organization?

KinematicBody2D is used to realize the body controlled by code. Moving objects will detect collisions with other objects when they are moving, but they are not affected by the physical characteristics of the engine such as gravity or friction. Although this means you have to write some code to create their behavior, it also means you can more precisely control how they move and react.

prompt

A KinematicBody2D can be affected by gravity and other forces, but the movement must be calculated in the code. The physics engine does not move KinematicBody2D.

Movement and collision

When moving KinematicBody2D, you should not set its properties directly. Instead, use the move_and_collide() ormove_and_slide() method. These methods move an object along a given vector and stop immediately if a collision with another object is detected. After a collision with KinematicBody2D, any collision response must be manually encoded.

caveat

You should only perform physical exercises in the _physics_process() callback.

The two movement methods have different uses, and later in this tutorial, you will see examples of how they work.

move_and_collide

This method uses one parameter: Vector2, which indicates the relative movement of the human body. Usually, this is your velocity vector multiplied by the frame time step (delta). If the engine detects a collision at any position along this vector, the body will immediately stop moving. If this happens, the method will return a KinematicCollision2D object.

KinematicCollision2D is an object that contains data about collisions and collision objects. Using this data, you can calculate the collision response.

move_and_slide

The move_and_slide() method is designed to simplify the collision response in common situations where you want one object to slide along another. For example, it is particularly useful in platform games or top-down games.

prompt

Use move_and_slide() to automatically calculate the frame-based movement delta. Isn't it by multiplying your velocity vector delta and
passing it to the previous move_and_slide().

In addition to the velocity vector, move_and_slide() can use many other parameters to customize the sliding behavior:

  • up_direction-default value: Vector2( 0, 0)

    This parameter allows you to define which surfaces the engine should consider as the floor. Setting this allows you to use is_on_floor(), is_on_wall() and is_on_ceiling() methods to detect what type of surface the body is in contact with. The default value means that all surfaces are treated as walls.

  • stop_on_slope-default value: false

    This parameter prevents the human body from sliding off the slope when standing.

  • max_slides-default value: 4

    This parameter is the maximum number of collisions before the body stops moving. Setting it too low may completely prevent movement.

  • floor_max_angle-default value: (0.785398 expressed in radians, equal to 45 degrees)

    This parameter is the maximum angle before the surface is no longer considered the "floor".

  • infinite_inertia-default value: true

When this parameter is true, the subject can push RigidBody2D nodes, ignoring their mass, but will not detect collisions with them. If so, false means the body will collide with the rigid body and stop.

move_and_slide_with_snap

This method move_and_slide() adds some other functions by adding snap parameters. As long as this vector is in contact with the ground, the object will remain attached to the ground. Note that, for example, this means you must disable snapping when jumping. You can do this by setting snap to Vector2.ZERO or using move_and_slide().

Collision detection

When using move_and_collide(), KinematicCollision2D returns one directly, and you can use it in your code.

When move_and_slide() calculates the sliding response, multiple collisions may occur during use. To handle these conflicts, use get_slide_count() and get_slide_collision():

# Using move_and_collide.
var collision = move_and_collide(velocity * delta)
if collision:
    print("I collided with ", collision.collider.name)

# Using move_and_slide.
velocity = move_and_slide(velocity)
for i in get_slide_count():
    var collision = get_slide_collision(i)
    print("I collided with ", collision.collider.name)

note

get_slide_count() only counts the number of body collisions and changes of direction.

For details on which collision data is returned, see KinematicCollision2D.

Which exercise method is used?

A common question for new users of Godot is: "How do you decide which sport function to use?" Usually, the response to use is move_and_slide() because it is "simpler", but this is not necessarily the case. One way to think of it, move_and_slide() is a special case, and move_and_collide() is more general. For example, the following two code snippets result in the same collision response:

Insert picture description here

// using MoveAndCollide
var collision = MoveAndCollide(velocity * delta);
if (collision != null)
{
    
    
    velocity = velocity.Slide(collision.Normal);
}
// using MoveAndSlide
velocity = MoveAndSlide(velocity);

Anything you do can use move_and_slide() to complete move_and_collide(), but it may take more code. However, as we can see in the example below, in some cases move_and_slide() cannot provide the required response.

In the above example, we assign the velocity returned by move_and_slide() to the velocity variable. This is because when the character collides with the environment, the function will recalculate the speed internally to reflect the speed drop.

For example, if your character falls on the floor, you don't want it to accumulate vertical velocity due to gravity. Instead, you want its vertical velocity to be reset to zero.

move_and_slide() may also recalculate the speed of the moving body multiple times in a loop, because to produce smooth motion, it will move the character and collide up to 5 times by default. At the end of the process, the function returns the character's new velocity, which can be stored in the velocity variable and used in the next frame.

example

To view these examples, please download the example project: using_kinematic2d.zip .

Movement and walls

If you downloaded the sample project, this sample is located in "BasicMovement.tscn".

For this example, add KinematicBody2D aSprite and a CollisionShape2D with two child elements. Use Godot "icon.png" as the texture of the Sprite (drag it from the Filesystem dock to the Texture property Sprite). In the CollisionShape2D "shape" property, select "New RectangleShape2D" and adjust the rectangle size to fit the sprite image.

note

For examples of implementing 2D mobility scenarios, see 2D mobility overview.

Attach the script to KinematicBody2D and add the following code:

using Godot;
using System;

public class KBExample : KinematicBody2D
{
    
    
    public int Speed = 250;
    private Vector2 _velocity = new Vector2();

    public void GetInput()
    {
    
    
        // Detect up/down/left/right keystate and only move when pressed
        _velocity = new Vector2();

        if (Input.IsActionPressed("ui_right"))
            _velocity.x += 1;

        if (Input.IsActionPressed("ui_left"))
            _velocity.x -= 1;

        if (Input.IsActionPressed("ui_down"))
            _velocity.y += 1;

        if (Input.IsActionPressed("ui_up"))
            _velocity.y -= 1;
    }

    public override void _PhysicsProcess(float delta)
    {
    
    
        GetInput();
        MoveAndCollide(_velocity * delta);
    }
}

Run this scene and you will see that move_and_collide() works as expected, moving the body along the velocity vector. Now, let's see what happens when we add some barriers. Add StaticBody2D with rectangular collision shape. To get visibility, you can use sprite, Polygon2D, or turn on "Visible Collision Shape" from the "Debug" menu.

Run the scene again and try to move into the obstacle. You will see that KinematicBody2D cannot penetrate obstacles. However, try to move into the obstacle at an angle, and you will find that the obstacle is like glue-it feels like your body is stuck.

This happens because there is no collision response. move_and_collide() stops the movement of the body when a collision occurs. We need to encode any response from the collision.

Try to change the function to move_and_slide(velocity) and run it again. Note that our delta is removed from the velocity calculation.

move_and_slide() provides the default collision response of sliding the body along the collision object. This is very useful for many game types, and may just get all of the desired behavior.

Bounce/reflection

What if you don't want to respond to sliding collisions? For this example ("BounceandCollide.tscn" in the example project), we have a character that shoots bullets, and we want these bullets to bounce off the wall.

This example uses three scenarios. The main scene contains the player and the wall. The bullet and the wall are separate scenes, so they can be instantiated.

The player is controlled by the w and s keys to move forward and backward. Aim using the mouse pointer. This is the code of Player, using move_and_slide():

using Godot;
using System;

public class KBExample : KinematicBody2D
{
    
    
    private PackedScene _bullet = (PackedScene)GD.Load("res://Bullet.tscn");
    public int Speed = 200;
    private Vector2 _velocity = new Vector2();

    public void GetInput()
    {
    
    
        // add these actions in Project Settings -> Input Map
        _velocity = new Vector2();
        if (Input.IsActionPressed("backward"))
        {
    
    
            _velocity = new Vector2(-Speed/3, 0).Rotated(Rotation);
        }
        if (Input.IsActionPressed("forward"))
        {
    
    
            _velocity = new Vector2(Speed, 0).Rotated(Rotation);
        }
        if (Input.IsActionPressed("mouse_click"))
        {
    
    
            Shoot();
        }
    }

    public void Shoot()
    {
    
    
        // "Muzzle" is a Position2D placed at the barrel of the gun
        var b = (Bullet)_bullet.Instance();
        b.Start(GetNode<Node2D>("Muzzle").GlobalPosition, Rotation);
        GetParent().AddChild(b);
    }

    public override void _PhysicsProcess(float delta)
    {
    
    
        GetInput();
        var dir = GetGlobalMousePosition() - GlobalPosition;
        // Don't move if too close to the mouse pointer
        if (dir.Length() > 5)
        {
    
    
            Rotation = dir.Angle();
            _velocity = MoveAndSlide(_velocity);
        }
    }
}

And the code of bullet:

using Godot;
using System;

public class Bullet : KinematicBody2D
{
    
    
    public int Speed = 750;
    private Vector2 _velocity = new Vector2();

    public void Start(Vector2 pos, float dir)
    {
    
    
        Rotation = dir;
        Position = pos;
        _velocity = new Vector2(speed, 0).Rotated(Rotation);
    }

    public override void _PhysicsProcess(float delta)
    {
    
    
        var collision = MoveAndCollide(_velocity * delta);
        if (collision != null)
        {
    
    
            _velocity = _velocity.Bounce(collision.Normal);
            if (collision.Collider.HasMethod("Hit"))
            {
    
    
                collision.Collider.Call("Hit");
            }
        }
    }

    public void OnVisibilityNotifier2DScreenExited()
    {
    
    
        QueueFree();
    }
}

This action occurs in _physics_process(). After using move_and_collide(), if a conflict occurs, KinematicCollision2D returns an object (otherwise it returns Nil).

If there is a return collision, we use normal to reflect the collision of the bullet through the Vector2.bounce() method.

If the collision object (collider) has a hit method, we also call it. In the sample project, we added a flashing color effect to the "wall" to demonstrate this.

Insert picture description here

Platform movement

Let's try a more popular example: 2D platform program. move_and_slide() is an ideal choice for quickly starting and running a functional character controller. If you have downloaded the sample project, you can find it in "Platformer.tscn".

For this example, we assume that you have a level composed of StaticBody2D objects. They can be of any shape and size. In the sample project, we use Polygon2D to create the platform shape.

This is the code of the player body:

using Godot;
using System;

public class KBExample : KinematicBody2D
{
    
    
    [Export] public int RunSpeed = 100;
    [Export] public int JumpSpeed = -400;
    [Export] public int Gravity = 1200;

    Vector2 velocity = new Vector2();
    bool jumping = false;

    public void GetInput()
    {
    
    
        velocity.x = 0;
        bool right = Input.IsActionPressed("ui_right");
        bool left = Input.IsActionPressed("ui_left");
        bool jump = Input.IsActionPressed("ui_select");

        if (jump && IsOnFloor())
        {
    
    
            jumping = true;
            velocity.y = JumpSpeed;
        }

        if (right)
            velocity.x += RunSpeed;
        if (left)
            velocity.x -= RunSpeed;
    }

    public override void _PhysicsProcess(float delta)
    {
    
    
        GetInput();
        velocity.y += Gravity * delta;
        if (jumping && IsOnFloor())
            jumping = false;
        velocity = MoveAndSlide(velocity, new Vector2(0, -1));
    }
}

Insert picture description here

When used move_and_slide(), this function returns a vector that represents the remaining movement after a sliding collision. Reset the value to the value of the character, and velocity can make us tilt up and down smoothly. Try to delete and see what happens if you don't. velocity =

Also note that we have added it as a lower bound normal. The vector points directly above. As a result, if the character collides with an object with that normal, it is treated as the floor. Vector2(0, -1)

Using the ground normal can use is_on_floor() for jumping work. This function will only return true after a move_and_slide() collision, where the normal of the colliding body is the floor carrier at 45 degrees. You can control the maximum angle floor_max_angle by setting.

For example, the angle also allows you to use to implement other functions, such as wall jump is_on_wall().

Guess you like

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