动力学角色(二维)

简介

是的,名字听起来很奇怪。 “动力学角色”。 那是什么? 原因是当物理引擎问世时,它们被称为“动力”引擎(因为它们主要处理碰撞响应)。 人们做了许多尝试,想使用动力引擎创建一个角色控制器,但它并不像看起来那么容易。 Godot拥有您能找到的最好的动态角色控制器(可以在 二维/游戏平台这个演示中查看),但使用它需要相当高水平的技能和对物理引擎的理解(或者对试验和试错有足够的耐心)。

像Havok这样的物理引擎似乎认为动态角色控制器是最好的选择,而其他物理引擎(PhysX)则更愿意推广动力学。

那么区别是什么呢?:

  • A dynamic character controller uses a rigid body with an infinite inertia tensor. It’s a rigid body that can’t rotate. Physics engines always let objects move and collide, then solve their collisions all together. This makes dynamic character controllers able to interact with other physics objects seamlessly, as seen in the platformer demo. However, these interactions are not always predictable. Collisions can take more than one frame to be solved, so a few collisions may seem to displace a tiny bit. Those problems can be fixed, but require a certain amount of skill.
  • A kinematic character controller is assumed to always begin in a non-colliding state, and will always move to a non-colliding state. If it starts in a colliding state, it will try to free itself like rigid bodies do, but this is the exception, not the rule. This makes their control and motion a lot more predictable and easier to program. However, as a downside, they can’t directly interact with other physics objects, unless done by hand in code.

这个简短的教程将重点介绍动力学角色控制器。基本上,传统处理冲突的方法(它并不一定在底层更简单,但隐蔽性很好,并且呈现为一个简洁漂亮的API)。

物理过程

为了管理运动物体或角色的逻辑,总是建议使用物理过程,因为它在物理步骤之前被调用并且它的执行与物理服务器同步,它也被称为每秒相同的次数。 这使得物理和运动计算以比使用常规过程更可预测的方式工作,如果帧速率太高或太低,则可能具有尖峰或丢失精度。

extends KinematicBody2D

func _physics_process(delta):
    pass
using Godot;
using System;

public class PhysicsScript : KinematicBody2D
{
    public override void _PhysicsProcess(float delta)
    {
    }
}

场景设置

要测试一下,这里是场景(来自tilemap教程): kbscene.zip。 我们将为角色创造一个新场景。 使用机器人精灵并创建一个这样的场景:

../../_images/kbscene.png

您会注意到,在”二维碰撞形状”(CollisionShape2D)节点旁边有一个警告图标;那是因为我们还没有定义它的形状。在”二维碰撞形状”(CollisionShape2D)的形状属性中创建一个新的二维圆形形状(CircleShape2D)。点击<二维圆形形状>(CircleShape2D)进入选项,将半径设置为30:

../../_images/kbradius.png

Note: As mentioned before in the physics tutorial, the physics engine can’t handle scale on most types of shapes (only collision polygons, planes and segments work), so always change the parameters (such as radius) of the shape instead of scaling it. The same is also true for the kinematic/rigid/static bodies themselves, as their scale affects the shape scale.

Now, create a script for the character, the one used as an example above should work as a base.

最后,实例化tilemap中的角色场景,并使地图场景成为主场景,因此在按下播放时运行。

../../_images/kbinstance.png

Moving the kinematic character

Go back to the character scene, and open the script, the magic begins now! Kinematic body will do nothing by default, but it has a useful function called KinematicBody2D.move_and_collide(). This function takes a Vector2 as an argument, and tries to apply that motion to the kinematic body. If a collision happens, it stops right at the moment of the collision.

所以,让我们向下移动我们的精灵,直到它撞上地板:

extends KinematicBody2D

func _physics_process(delta):
    move_and_collide(Vector2(0, 1)) # Move down 1 pixel per physics frame
using Godot;
using System;

public class PhysicsScript : KinematicBody2D
{
    public override void _PhysicsProcess(float delta)
    {
        // Move down 1 pixel per physics frame
        MoveAndCollide(new Vector2(0, 1));
    }
}

结果是角色会移动,但在击中地板时会停止。 很酷,对吧?

The next step will be adding gravity to the mix, this way it behaves a little more like a regular game character:

extends KinematicBody2D

const GRAVITY = 200.0
var velocity = Vector2()

func _physics_process(delta):
    velocity.y += delta * GRAVITY

    var motion = velocity * delta
    move_and_collide(motion)
using Godot;
using System;

public class PhysicsScript : KinematicBody2D
{
    const float gravity = 200.0f;
    Vector2 velocity;

    public override void _PhysicsProcess(float delta)
    {
        velocity.y += delta * gravity;

        var motion = velocity * delta;
        MoveAndCollide(motion);
    }
}

Now the character falls smoothly. Let’s make it walk to the sides, left and right when touching the directional keys. Remember that the values being used (for speed at least) are pixels/second.

通过向左和向右按下可以增加简单的步行支持:

extends KinematicBody2D

const GRAVITY = 200.0
const WALK_SPEED = 200

var velocity = Vector2()

func _physics_process(delta):
    velocity.y += delta * GRAVITY

    if Input.is_action_pressed("ui_left"):
        velocity.x = -WALK_SPEED
    elif Input.is_action_pressed("ui_right"):
        velocity.x =  WALK_SPEED
    else:
        velocity.x = 0

    # We don't need to multiply velocity by delta because "move_and_slide" already takes delta time into account.

    # The second parameter of "move_and_slide" is the normal pointing up.
    # In the case of a 2D platformer, in Godot, upward is negative y, which translates to -1 as a normal.
    move_and_slide(velocity, Vector2(0, -1))
using Godot;
using System;

public class PhysicsScript : KinematicBody2D
{
    const float gravity = 200.0f;
    const int walkSpeed = 200;

    Vector2 velocity;

    public override void _PhysicsProcess(float delta)
    {
        velocity.y += delta * gravity;

        if (Input.IsActionPressed("ui_left"))
        {
            velocity.x = -walkSpeed;
        }
        else if (Input.IsActionPressed("ui_right"))
        {
            velocity.x = walkSpeed;
        }
        else
        {
            velocity.x = 0;
        }

        // We don't need to multiply velocity by delta because "MoveAndSlide" already takes delta time into account.

        // The second parameter of "MoveAndSlide" is the normal pointing up.
        // In the case of a 2D platformer, in Godot, upward is negative y, which translates to -1 as a normal.
        MoveAndSlide(velocity, new Vector2(0, -1));
    }
}

试一试。

这是平台游戏的良好起点。 可以在随引擎分发的演示zip中找到更完整的演示,或者在https://github.com/godotengine/godot-demo-projects/tree/master/2d/kinematic_character中找到。