|
|
6 月之前 | |
|---|---|---|
| .. | ||
| Image | 6 月之前 | |
| README.md | 6 月之前 | |
https://zhuanlan.zhihu.com/p/685714685
https://news.16p.com/842025.html
角色移动是一个复杂的计算过程,为了让游戏操作更加顺畅,往往需要非常多的细节处理,这些特殊的移动处理逻辑叫做 collide and slide
正如 Phys-x 物理引擎的 SDK 文档中所说,如果使用物理引擎来控制角色移动存在很多问题
tunneling effect
tunneling effect 当角色移动过快的时候,可能会穿过墙壁,因此需要限制角色最大速度tunneling,当角色在角落向前推时,会出现角色抖动的情况,因为物理引擎不断将其前后移动到略有不同的位置restitution 的存在,会产生回弹效果由于以上这些问题的存在,角色移动不能直接使用 物理引擎 来控制
虚幻引擎将碰撞检测的信息封装成 FHitResult,虽然下面这些理论知识与虚幻引擎无关,为了方便参数解释,还是提前说明
| FHitResult 的属性 | 解释 |
|---|---|
| bBlockingHit | 是否发生碰撞 |
| Time | 碰撞后实际移动距离除以检测移动距离 |
| Distance | 碰撞后实际移动距离 |
| Location | 碰撞后最终位置 |
| ImpactPoint | 碰撞接触点 |
| Normal | 碰撞切面法向量 |
| ImpactNormal | 碰撞切面法向量(非胶囊体和球体检测与Normal不同) |
| TraceStart | 检测开始位置 |
| TraceEnd | 检测结束位置 |
| bStartPenetrating | 是否在检测开始就有渗透情况 |
| PenetrationDepth | 渗透深度 |
Sweep 检测
Sweep扫过、掠过、大范围伸展
以当前坐标为起点,以当前 速度 和 Delta 计算下一帧的理论坐标为终点,进行检测
如果中间检测到碰撞,则无法移动到终点坐标,从碰撞点计算角色下一帧真实坐标
TraceStart 表示 检测起点,值为当前坐标TraceEnd 表示 检测终点,值为理论下一帧坐标ImpactPoint 表示 碰撞接触点Location 表示 下一帧的实际坐标Distance 表示 下一帧的实际坐标与当前坐标之间的距离Time 表示 $\frac{Distance}{TraceEnd-TraceStart}$,是一个 0~1 之间的值InitialOverlaps 检测
也就是在开始位置就检测到了重叠
这个时候 bStartPenetrating 值为 true,表示检测到了 InitialOverlaps
Penetrating 表示重叠深度
Penetrating: 渗透、贯穿、穿过
角色移动组件分层设计,各自负责不同的职责
| 组件 | 作用 |
|---|---|
| UMovementComponent | 基础移动 |
| UNavMovementComponent | 导航集成 |
| UPawnMovementComponent | 输入响应 |
| UCharacterMovementComponent | 角色物理 |
MovementComponent 为基类,提供基本的更新坐标逻辑。通常用于 可推动的物理道具
UpdateComponent),通常是根组件,比如 ACharacter 的根胶囊体组件UpdatedPrimitive),用于物理交互,如果 UpdateComponent 能转换成 UPrimitiveComponent 会直接使用 UpdateComponentMoveComponentFlags,用于控制更新行为的精细开关Velocity 存储实时移动速度向量,是驱动组件运动的核心数据PlaneConstraintNormal 定义移动约束平面的法线方向((0,1,1) 限制 Y 轴移动)PlaneConstraintOrigin 定义移动约束平面的原点坐标,用于计算组件与平面的空间关系UpdatedComponent = IsValid(NewUpdatedComponent) ? NewUpdatedComponent : NULL;
UpdatedPrimitive = Cast<UPrimitiveComponent>(UpdatedComponent);
NavMovementComponent 提供了AI寻路用的一些接口。通常用于 AI 控制的非人形物体
NavAgentProps 定义导航代理的物理特性和移动能力(如:半径、高度、最大速度、加速度等),用于路径计算和碰撞检测FixedPathBrakingDistance 定义减速到停止的距离,由 bUseFixedBrakingDistanceForPaths 控制是否启用PathFollowingComp 处理路径跟随逻辑,通常是 UPathFollowingComponentPawnMovementComponent 定义了接受输入的接口。其Owner必须为APawn子类。通常用于自定义载具等
UMovementComponent::MoveUpdateComponent 是真正执行物体移动逻辑的函数接口,并且该函数不是虚函数,子类无法重写
FORCEINLINE_DEBUGGABLE bool UMovementComponent::MoveUpdatedComponent(const FVector& Delta, const FRotator& NewRotation, bool bSweep, FHitResult* OutHit, ETeleportType Teleport)
{
return MoveUpdatedComponentImpl(Delta, NewRotation.Quaternion(), bSweep, OutHit, Teleport);
}
bool UMovementComponent::MoveUpdatedComponentImpl( const FVector& Delta, const FQuat& NewRotation, bool bSweep, FHitResult* OutHit, ETeleportType Teleport)
{
if (UpdatedComponent)
{
const FVector NewDelta = ConstrainDirectionToPlane(Delta);
return UpdatedComponent->MoveComponent(NewDelta, NewRotation, bSweep, OutHit, MoveComponentFlags, Teleport);
}
return false;
}
对的,该函数直接调用 UpdatedComponent 的 MoveComponent,让组件自己更新自己
不过 USceneComponent::MoveComponentImpl 是虚函数,可以被子类重写,所以说根据设置的 UpdatedComponent 的不同,最后执行的移动逻辑也不相同
对于 USceneComponent::MoveComponentImpl 的实现逻辑是比较简单的
Mobility 必须是 MovableConditionalUpdateComponentToWorld 确保组件的 Transform 已经更新Delta.IsZero(),零位移表示不用移动,也就无需后续计算InternalSetWorldLocationAndRotationUpdateOverlaps,主要是递归更新 AttachedChild,并且更新自己的 PhysicsVolume 信息InternalSetWorldLocationAndRotation 逻辑相对简单
RelativeLocation 和 RelativeRotation 的值bCanEverAffectNavigation 更新导航网格的信息前面说过 MoveComponentImpl 是一个虚函数
在 UMovementComponent 中的 UpdatedComponent 通常被设置为对象的根组件,而 ACharacter 的根组件是 UCapsuleComponent
UCapsuleComponent -> UShapeComponent -> UPrimitiveComponent
所以对于 ACharacter 来说,更新坐标执行的是 UPrimitiveComponent::MoveComponentImpl