运动特征(2d)

介绍

是的,这个名字听起来很奇怪。”运动特征”。那是什么?这个名字的原因是,当物理引擎出现时,它们被称为“动力学”引擎(因为它们主要处理碰撞响应)。许多人试图用动力学引擎来创建一个角色控制器,但这并不像看上去那么简单。Godot有一个你能找到的最好的动态字符控制器的实现(在2D/Platformer演示中可以看到),但是使用它需要相当水平的技能和对物理引擎的理解(或者对尝试和错误的耐心)。

一些物理引擎,如哈沃克,似乎发誓动态字符控制器是最好的选择,而其他(physx)更愿意促进运动的选择。

那么,有什么区别呢?:

  • A 动态字符控制器 使用具有无限惯性张量的刚体。基本上,它是一个不能旋转的刚体。物理引擎总是让物体碰撞,然后一起解决它们的碰撞。这使得动态字符控制器能够无缝地与其他物理对象交互(如platformer演示中所示),但是这些交互并不总是可预测的。碰撞也可能需要一个以上的帧来解决,所以一些碰撞似乎会移动一点点。这些问题可以解决,但需要一定的技巧。

  • A 运动特性控制器 假设总是以非碰撞状态开始,并且总是移动到非碰撞状态。如果它以碰撞状态开始,它会试图释放自己(就像刚体那样),但这是例外,而不是规则。这使得他们的控制和运动更容易预测和编程。但是,作为一个缺点,它们不能直接与其他物理对象交互(除非手工编写代码)。

本简短教程将重点介绍运动学角色控制器。基本上,老式的处理碰撞的方法(在引擎盖下不一定简单,但很好地隐藏起来,并作为一个漂亮而简单的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

您会注意到碰撞形状2d节点旁边有一个警告图标;这是因为我们没有为其定义形状。在碰撞形状2d的形状属性中创建新的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.

现在,为角色创建一个脚本,上面作为示例的脚本应该作为基础。

最后,在tilemap中举例说明角色场景,并使地图场景成为主要场景,所以它在按下play时运行。

../../_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));
    }
}

试试看。

这是一个很好的开场白。更完整的演示可以在与引擎一起分发的演示压缩包中找到,或者在https://github.com/godoengine/godot-demo-projects/tree/master/2d/motionic_character中找到。