物理引擎

物理现象:重力/浮力,碰撞,形变,流体,破碎,受物理约束的移动
很多游戏并不用物理引擎模拟重力,而是用代码逻辑模拟

物理组件(刚体)
Convex(indices)
Sweep(vertexes)
质量
动静摩擦力
阻尼
弹性系数
初始冲量
初始扭矩
被动模式
碰撞事件
组ID

Continuous collision detection 设置CCD连续碰撞检测
对于高速移动的物体间碰撞,一种最直接的想法就是把物体两帧之间的位置相连,形成一个长条状的形状再进行碰撞检测。其他更复杂和高效的方法可以参考这篇介绍ACRL:连续碰撞检测 - CCD

常见问题
脱离卡点:先简化成2D处理,一个很简单的思路就是——targetPos处画一个圆,然后把我们的Shape放进去看看和其他Collider哪里会相交、哪里不会。这个问题就简化成了圆与多边形求交……事实上我们也不需要多边形那么精确,将碰撞体转成Box或Sphere来看就可以了,因为我们要给玩家足够的活动空间、旋转
其实也可以借助NavMesh查询,放置在可达点而非不可达、或者浮岛上

UE4:FindTeleportSpot 尝试找到一个可接受的非碰撞位置来放置TestActor,尽可能接近PlaceLocation,成功返回true
EncroachingBlockingGeometry,实际上会打射线、Sweep来检测,其实就是给目标点求场景Overlap交点(用OverlapMultiByChannel接口得到所有overlap的对象,其实是委托PhysX处理)
这里就是对每一个overlap结果处理,用ComputePenetration接口算出MTD(得到最小所需位移和方向),再记录到OutProposedAdjustment中,每一个重叠的物体我们都得到一个修正,累积所有的修正就得到了最终可放置的位置

修正计算的函数:Overlap_GeomInternal。详询:PhysX::computePenetration
当两个物体相交时,PhysX 可以计算物体必须平移以将它们分开的最小距离和方向。一个几何对象必须是盒子、球体、胶囊或凸网格,而另一个可以是任何类型

UE4的方式比较正式,但也比较耗,取巧的方法:因为我们总是有一个朝向要放置的(不然我不知道你是要放在里还是外),所以我们可以选一个点,朝目标点打射线,得到一个hitPos,这个点就是交点,那么只要不与交点重叠就不会有碰撞,可以放置

物理材质
运用RBTemplate,一个CollisionShape中可以有多个不同的碰撞部分,设置为不同的材质ID,使其碰撞时有不同的质感(对应不同的PhysicsMaterial资源)

场景静态刚体
PhysicsStaticSceneBody
PhysicsDynamicSceneBody

动态加载刚体
自动决定需要加载物理的刚体 的机制,可节省内存消耗
Chunk:将世界xoz平面划分为方形网格,再在Y轴上下扩充为一个长方体(ChunkEdgeLength+ChunkHalfHeight),是引擎进行动态加载管理的最小单位(chunk内的rgbd会一起加载)
FocusArea:2n*2n个相邻的chunk组成的长方体区
当任意输入一个点时,那么会找到这个点所属的Chunk,用Chunk在x轴和z轴的最小值作为FocusArea的中心。引擎支持同时有多个FocusArea,不同FocusArea之间可以有交叉。引擎里始终会存在一个主FocusArea,这个FocusArea跟随玩家或者相机的移动而更新位置。同时引擎还支持0到多个辅助FocusArea。辅助FocusArea在创建的时候指定位置之后,就不可以再更改其位置了。

动态加载算法

如果一个Chunk属于任何一个FocusArea,那么称该Chunk为激活的。引擎每帧会根据逻辑刷新FocusArea。
  • 当FocusArea出现位置改变,或者出现删减增加时,会找到不再属于任何FocusArea的Chunk,也会找到新加入到FocusArea中的Chunk。
  • 对于已经加载,但不再属于任何激活Chunk的RigidBody,会通知其卸载。对于没有加载,但属于某个激活Chunk的RigidBody,会通知其加载。
也就是说,动态加载的单位是Chunk

查询与回调接口

  • bool PhysicsSpaceComponent::IsPosReady(const Vector3& pos)
  • bool PhysicsSpaceComponent::AddPosReadyCallback(const Vector3& pos, PosReadyCallback&& callback)

主FocusArea的更新逻辑

PhysicsSpace决定主FocusArea的中心点时,按照下面的规则:
  • 如果是游戏运行时:
  1. 当玩家(ClientPlayer)控制的entity进入场景以后,用该entity的position
  2. 如果1中的条件不满足,用PhysicsSpace.DefaultFocusCenter的值。
  • 如果是在编辑器中:
  1. 当玩家(ClientPlayer)控制的entity进入场景以后,用该entity的position。
  2. 如果1中的条件不满足,用ClientPlayer的相机位置。
  3. 如果ClientPlayer没有相机,用PhysicsSpace.DefaultFocusCenter的值。
脚本可以设置PhysicsSpace.DefaultFocusCenter的值。同时可以通过PhysicsSpace.CurrentFocusCenter来查询当前主FocusArea的中心点(上图中的红点)。通过PhysicsSpace.FocusSource来查询玩家位置的决定方式。
PhysicsSpace更新主FocusArea是在每帧的tick中完成。在某些情况下(如设置了Focus的位置,需要马上更新),脚本可以通过调用PhysicsSpace.UpdateFocus函数来强制更新主FocusArea。
在一些使用情况下,脚本会在场景全部准备好以后,才将Entity放入场景中。这时,需要设置PhysicsSpace.DefaultFocusCenter的值。

需要加载的物理资源

  • 一个RigidBody是否被动态加载,是由RigidBody上的IsSceneComponent属性决定的。当该属性为True时,表示这个RB遵守动态加载的规则。
  • 同时,也可以通过ILevel上的NeedPhysics属性来控制一个level中的所有SceneComponent的RB是否进行动态加载。当该属性为True时,Level中的所有SceneComponent为True的RB会被动态加载;为False时,所有的SceneComponent为True的RB不会被加载。
  • 通过PyClientScenario.LoadWorld加载的Level,NeedPhysics属性默认为True。通过PyClientScenario.LoadWorldLevels加载的Level,NeedPhysics属性默认为False。
ChunkEdgeLength,ChunkHalfHeight和FocusHalfChunkSize都可以在编辑器中进行设置,它们的值会存放在IWorld文件中。
脚本可以在PhysicsSpace创建之前,通过MPhysics的下列接口,修改Focus区域的大小。注意,需要使用MPhysics.SetIsOverrideLoadRange函数,设置IsOverrideLoadRange为true,才能覆盖IWorld文件中记录的动态加载参数。……

物理碰撞
场景编辑器查看碰撞线
PVD查看
import MPhysics
MPhysics.ReconnectToDebugViewer()

模型的骨骼碰撞
在SE中创建,一般是将骨骼拖拽到碰撞体窗口来创建,或者为骨骼+碰撞体创建。所以这种碰撞体一般都是绑骨骼的(类型只有 圆柱/球)
骨骼碰撞(黄碰撞体)只是用了进行碰撞检测的,如果Collider设置了与碰撞骨架碰撞,它就可以将碰撞信息发送到脚本

CharCtrl

移动平台起跳时,惯性速度的使用

  • CheckOverlap 玩家碰撞是否和其他刚体重叠(或包含) epsilon值的范围是0~0.5

碰到障碍物后的滑动表现

  • 玩家控制器在移动的时候碰到障碍物,会从障碍物的边上划过去,提升操控的手感。目前支持两种滑动方式
  • PhysX的默认方式,当玩家控制器碰到障碍物后,会将移动速度按照碰撞表面进行分解,分解为与表面垂直的分量和与表面相切的分量。两个分量的和等于原来的速度。玩家控制器会按照切分量移动。当速度和表面垂直时,切分量为0,这是不会有滑动
  • Messiah提供的方式是在PhysX的默认方式上进行了修改。会保证切分量的大小等于原来速度乘以一个系数,如果切分量为0,会向左滑动。

穿插纠正设置

  • bool GetOverlapRecoverWithScene()
移动平台

SytemID编码

过滤施法者骨骼

RB.JoinIgnoreGroup 常用于过滤掉某个entity的rb,或具体某个rb,使其不发生碰撞
可能会ByRBEngineObj,ByRBComp,ByRBShape,ByEngineEntity等;最后实质都是BySystemID,因为该编码就是用于分类各种rb的,由RBSystemIDManager获取
LeaveIgnoreGroup 恢复

布料系统
和刚体相对的柔体(SoftBody)

质点-弹簧模型
工作流:单层布料映射双层模型,制作成ClothModel

Ragdoll 布娃娃系统
可变性角色动画系统(动画+物理)
常见的用处是死亡后的碰撞效果

布娃娃物理动画

布娃娃是在角色骨骼上添加一些刚体,然后在相邻的刚体上添加旋转约束
物理动画是在播放动画的基础上,添加上一些物理效果。个人理解就是动画驱动位移,但位移结果由物理决定
对布娃娃角色来说:
  • 跟随动画:动作完全跟随骨骼移动
  • 物理动画:使用动画约束的刚体,此时动画会影响物理更新效果,即动画约束
  • 纯物理:动作完全跟随物理刚体
为了将物理效果和动画效果相互叠加,首先需要把当前的刚体位置映射到骨骼上,即2.1中的布娃娃刚体到骨骼Trasnform的推导; 其次需要把使得当前的动画影响物理刚体的位移、旋转。一般的做法是通过计算骨骼的位移、旋转差量,换算成加速度并设置到刚体上。或者尝试把物理刚体的位移反馈到动画骨骼上。
PhysX本身支持,甚至可以设置约束强度等参数,使得可以控制效果更偏向动画,还是更偏向物理。

Graph结点:RagdollNode、Constraint、Keyframe、Balancer、Impulse,IK……

物理破碎
PhysX 3.4+Blast,用APEX Destruction PhysXLab制作破碎资源,再转为blast文件 导入messiah编辑器,制作成DestructbileModel资源。


DamageMaterial是用来描述破碎属性的,决定破碎物体和刚体发生碰撞时受到的伤害量。

只要给Entity添加DestructibleModelComponent和DestructibleFamily两个Comp即可添加破碎功能















comments powered by Disqus