运动学角色(二维)

简介

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

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

那么区别是什么呢?:

  • 一个 dynamic character controller 使用一个具有无限惯性张量的刚体.这是一个不能旋转的刚体.物理引擎总是让物体移动和碰撞,然后一并解决它们的碰撞.这使得动态角色控制器能够与其他物理对象无缝交互,就像在平台游戏演示中看到的那样.然而,这些互动并不总是可预测的.碰撞可能需要多于一帧的时间来解决,所以几个碰撞可能看起来会有很小的位移.这些问题是可以解决的,但需要一定的技巧.

  • 假设一个 kinematic character controller 总是以非碰撞状态开始,并将总是移动到非碰撞状态.如果它开始时处于碰撞状态,将像刚体一样尝试释放自己,但这是特例,而不是规则.这使得它们的控制和运动更可预测,更容易编程.然而,有一个缺点,它们不能直接与其他物理对象交互,除非在代码中手动完成.

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

注意:正如之前在物理教程中提到的,物理引擎无法处理大多数类型形状的缩放,只有碰撞多边形、平面和段才有效,所以,总是改变形状的半径等参数,而不是缩放它.对于运动体或刚性体或静态体本身也是如此,因为它们的比例会影响形状的比例.

现在,为这个角色创建一个脚本,上面作为例子的那个脚本可以作为基础.

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

../../_images/kbinstance.png

移动动态角色

回到角色场景,打开脚本,魔法开始了!动态物体默认不会做任何事情,但它有一个有用的函数,叫做 KinematicBody2D.move_and_collide() .该函数以一个 Vector2 作为参数,并尝试将该运动应用到动态物体.如果发生了碰撞,它就在碰撞的瞬间停止.

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

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));
    }
}

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

下一步将加入重力,这样一来,它的行为就更像一个常规的游戏角色:

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);
    }
}

现在人物平稳下落.我们让它在触摸方向键时向两边或左、右边行走.记住,正在使用的值(至少对于速度)是像素/秒.

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

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中找到.