Table of Contents

角色

初级 设计师

角色(Character) 用于玩家和 NPC 的移动。带有角色组件的实体可以通过 Move、TryJump 移动,并可通过设置其 Position 属性进行传送。

将角色组件添加到实体

  1. 在 Scene Editor 中,选择要添加组件的实体。

  2. 在 Property Grid 中,点击 Add component,悬停在 Physics - Bepu 上并选择 CharacterComponent。

    添加角色组件

Note

你需要为这个新创建的角色设置一个碰撞体,可以通过 Collider 属性来完成。胶囊体(Capsule)形状适合大多数角色碰撞体。更多信息请参见碰撞体形状。

Warning

切勿将网格碰撞体用于角色,只将它们用于静态体;它们的速度太慢,无法用作角色。如果你确实需要比基本形状更复杂的形状,请改用凸包,参见碰撞体形状。

有关此组件属性的更多细节,请查看 API。

质量与 MaximumHorizontalForce

虽然角色组件的移动表现为更接近运动学的方式,但它仍然基于力来实现, 这样做是为了确保推动物体和设置约束是直观的。

角色要移动,首先必须施加足够的力来移动其自身碰撞体的 Mass(质量)。 它们施加的力的大小基于 Speed 和 MoveVector,但最终受限于 MaximumHorizontalForce。 如果这个值太低,角色的加速度、减速度和最高速度都会降低。

这种关系甚至延伸到物理世界:如果角色靠着一个大对象,或者被约束到一个大对象上, 那个对象的 Mass 也会被考虑在内。如果 MaximumHorizontalForce 太低,施加的力将不足以推动该对象。

你可以利用这个属性并平衡不同对象的质量,来控制角色能够拉动哪些刚体、不能拉动哪些刚体。

MoveVector 内部机制

当角色接触到 SlopeAngle 以下斜坡上的对象时,它会将该对象的表面视为"支撑面", 角色会附着在这个表面上并沿其行走。在表面上行走是通过将其视为 2D 平面来实现的。 由于它是一个 2D 平面,角色只有两个自由度可以沿其移动, MoveVector 的值控制着沿这些轴的移动。

但是,作为 API 的使用者,这太抽象而难以操作,你需要一种更直观的方式来控制移动,而不管重力或表面方向如何。 这就是为什么 MoveVector 是从角色的 Orientation 投影而来的,将其转换为更直观的"局部"轴。

常见陷阱

向错误的方向移动

MoveVector 相对于角色的 Orientation 是局部的。

  • X 是局部 X 轴,+X 为其左侧,-X 为其右侧。
  • Y 是局部 Z 轴,+Z 为前方,-Z 为后方。

你可以看看编辑器中箭头 Gizmo 的样子,以便更直观地了解它的布局。

如果你想重新定向这个方向,可以在将其传递给 MoveVector 之前转换该向量,或者改变角色的 Orientation, 最好通过 SetTargetPose(Quaternion targetOrientation), 或者自定义角色组件

滑冰现象:启动和停止耗时太长

控制这种可能不想要的行为,可以通过调整角色碰撞体的 Mass、 它的 MaximumHorizontalForce,有时还有表面的摩擦系数。

  • 摩擦系数定义表面的粗糙程度;0 表示完全光滑的表面,会保持角色的动量,而值为 1 则相反。
  • 质量和 MaximumHorizontalForce 在这里有描述

无论 Speed 设置多大都太慢

参见质量与 MaximumHorizontalForce

自定义角色控制器

创建控制器时,建议创建一个从该组件派生的新类,而不是直接使用它, 这样你可以访问它的内部状态并用自己的逻辑覆盖它,以更好地适应你的游戏。

using BepuPhysics;
using Stride.BepuPhysics;
using Stride.BepuPhysics.Systems.Characters;

public class MyCharacterController : CharacterComponent
{
    protected override void SimulationUpdate(BepuSimulation sim, float simTimeStep, ref InternalCharacterData character, in BodyReference characterBody, out bool wakeupBody)
    {
        // 将基于本组件的 Orientation、MoveVector 及其他属性,修改 'character'(本组件在物理引擎中的内部状态)
        base.SimulationUpdate(sim, simTimeStep, ref character, characterBody, out wakeupBody);
        // 你可以在此处根据需要进一步修改 'character'
    }

    public override void TryJump()
    {
        base.TryJump();
    }
}

你也可以从 CharacterComponentAbstract 派生,从完全空白开始实现你自己的物理和属性:

using BepuPhysics;
using Stride.BepuPhysics;
using Stride.BepuPhysics.Systems.Characters;

public class MyCharacterController : CharacterComponentAbstract
{
    protected override void SimulationUpdate(BepuSimulation sim, float simTimeStep, ref InternalCharacterData character, in BodyReference characterBody, out bool wakeupBody)
    {
        // 你必须直接修改 'character' 和 `wakeupBody`,才能影响本组件在物理引擎中的内部状态
        // 最好从复制 CharacterComponent 的源实现开始:https://github.com/stride3d/stride/blob/master/sources/engine/Stride.BepuPhysics/Stride.BepuPhysics/CharacterComponent.cs
        // 然后根据你的需要进行调整
    }
}

另请参阅