Attention: Here be dragons
This is the latest
(unstable) version of this documentation, which may document features
not available in or compatible with released stable versions of Godot.
Checking the stable version of the documentation...
运动学角色(2D)
前言
是的,名字听起来很奇怪。“运动学角色”是什么东西?使用这个名称的原因是物理引擎问世之处,它们被称为“动力学(Dynamics)”引擎(因为它们主要处理碰撞响应)。人们做了许多尝试,想使用动力学引擎创建角色控制器,但它并不像看起来那么容易。Godot 拥有你能找到的最好的动力学角色控制器(可以在 2d/platformer 演示中查看),但使用它需要相当高水平的技能和对物理引擎的理解(或者对试验和试错有足够的耐心)。
像 Havok 这样的物理引擎似乎认为力学角色控制器是最好的选择,而其他物理引擎(PhysX)则更愿意推广运动学(Kinematic)的角色控制器。
那么区别是什么呢?:
动力学角色控制器使用的是一个具有无限惯性张量的刚体。这是一个不能旋转的刚体. 物理引擎总是让物体移动和碰撞, 然后一并解决它们的碰撞. 这使得动态角色控制器能够与其他物理对象无缝交互, 就像在平台游戏演示中看到的那样. 然而, 这些互动并不总是可预测的. 碰撞可能需要多于一帧的时间来解决, 所以几个碰撞可能看起来会有很小的位移. 这些问题是可以解决的, 但需要一定的技巧.
运动学角色控制器总是假设以非碰撞状态开始,并将总是移动到非碰撞状态。如果它开始时处于碰撞状态, 将像刚体一样尝试释放自己, 但这是特例, 而不是规则. 这使得它们的控制和运动更可预测, 更容易编程. 然而, 有一个缺点, 它们不能直接与其他物理对象交互, 除非在代码中手动完成.
这个简短教程将重点介绍运动学角色控制器。它使用传统的碰撞处理方法,这种方法在底层并不一定更简单,但可以很好地隐藏并呈现为一个 API。
物理过程处理
为了管理运动物体或角色的逻辑, 总是建议使用物理过程处理, 因为它在物理步骤之前被调用并且它的执行与物理服务器同步, 它也被以被每秒相同的次数调用. 这使得物理和运动计算以比使用常规过程处理更可预测的方式工作, 如果帧速率太高或太低, 则可能具有尖峰或丢失精度.
extends CharacterBody2D
func _physics_process(delta):
pass
using Godot;
public partial class MyCharacterBody2D : CharacterBody2D
{
public override void _PhysicsProcess(double delta)
{
}
}
场景设置
为了进行测试,这里有一个场景(来自 tilemap 教程):kinematic_character_2d_starter.zip。我们将为角色创建一个新场景。使用机器人精灵并创建一个这样的场景:

你会注意到在我们的 CollisionShape2D 节点旁边有一个警告图标,这是因为我们还没有为它定义形状。请在 CollisionShape2D 的形状(Shape)属性中新建一个 CircleShape2D。点击 CircleShape2D 就会显示相关的选项,将其中的半径(Radius)设置为 30:

注意: 正如之前在物理教程中提到的, 物理引擎无法处理大多数类型形状的缩放, 只有碰撞多边形, 平面和段才有效, 所以, 总是改变形状的半径等参数, 而不是缩放它. 对于运动体或刚性体或静态体本身也是如此, 因为它们的比例会影响形状的比例.
现在, 为这个角色创建一个脚本, 上面作为例子的那个脚本可以作为基础.
最后, 实例化tilemap中的角色场景, 并使地图场景成为主场景, 因此在按下播放时运行.

移动动态角色
回到角色场景,打开脚本,魔法现在开始了!运动学物体默认不会执行任何操作,但它有一个有用的函数,称为 CharacterBody2D.move_and_collide()
。该函数以 Vector2 作为参数,并尝试将该运动应用于运动学物体。如果发生碰撞,它会在碰撞发生时立即停止。
所以, 让我们向下移动我们的精灵, 直到它撞上地板:
extends CharacterBody2D
func _physics_process(delta):
move_and_collide(Vector2(0, 1)) # Move down 1 pixel per physics frame
using Godot;
public partial class MyCharacterBody2D : CharacterBody2D
{
public override void _PhysicsProcess(double delta)
{
// Move down 1 pixel per physics frame
MoveAndCollide(new Vector2(0, 1));
}
}
结果是角色会移动, 但在击中地板时会停止. 很酷, 对吧?
下一步将加入重力, 这样一来, 它的行为就更像一个常规的游戏角色:
extends CharacterBody2D
const GRAVITY = 200.0
func _physics_process(delta):
velocity.y += delta * GRAVITY
var motion = velocity * delta
move_and_collide(motion)
using Godot;
public partial class MyCharacterBody2D : CharacterBody2D
{
private const float Gravity = 200.0f;
public override void _PhysicsProcess(double delta)
{
var velocity = Velocity;
velocity.Y += (float)delta * Gravity;
Velocity = velocity;
var motion = velocity * (float)delta;
MoveAndCollide(motion);
}
}
现在人物平滑下落. 我们让它向两边行走, 在按下方向键时向左或向右. 记住, 正在使用的值(至少对于速度)单位是像素/秒.
这增加了按左键和右键行走的基本支持:
extends CharacterBody2D
const GRAVITY = 200.0
const WALK_SPEED = 200
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
# "move_and_slide" already takes delta time into account.
move_and_slide()
using Godot;
public partial class MyCharacterBody2D : CharacterBody2D
{
private const float Gravity = 200.0f;
private const int WalkSpeed = 200;
public override void _PhysicsProcess(double delta)
{
var velocity = Velocity;
velocity.Y += (float)delta * Gravity;
if (Input.IsActionPressed("ui_left"))
{
velocity.X = -WalkSpeed;
}
else if (Input.IsActionPressed("ui_right"))
{
velocity.X = WalkSpeed;
}
else
{
velocity.X = 0;
}
Velocity = velocity;
// "MoveAndSlide" already takes delta time into account.
MoveAndSlide();
}
}
试一试.
这是平台游戏的良好起点. 可以在随引擎分发的演示zip中找到更完整的演示, 或者在https://github.com/godotengine/godot-demo-projects/tree/master/2d/kinematic_character中找到.