Unity Physics 学习笔记第七章:精确的碰撞管理
官方手册里有提到,但是没有具体描述。本文主要基于官方示例代码与实践
遇到的问题
前文和手册中都有介绍过 CollisionFilter
,但是这个东西是跟 Collider
绑在一起扔进 Blob 里的。而 Blob 是一种类似引用的东西,因此由同一个带有 Collider
的 Prefab 实例化出的 Entity,他们的 CollisionFilter
也是相同的。如果想要改变,必须要为每个实例化的 Entity 单独创建一个 Collider
的 Blob 资源来让他们的 CollisionFilter
不同。这就非常蛋疼
简单高效但是不正常也不一定高效的解决方案
最简单的一种思路,为每一个实例化的 Entity 单独创建一个 Collider
的 Blob 资源
例如网格碰撞体,可以把 Prefab 上碰撞体中存着点和面的 NativeArray
的引用偷出来,重新定个 CollisionFilter
就行了,因为点和面都是共用的,所以内存上其实不亏
而且使用 CollisionFilter
的效率很高,因此在碰撞处理时的性能上也不亏
不过在一开始创建新的 Blob 的时候就很麻烦而且时间开销较大;并且如果涉及到 Entity 的销毁,可能要把相应的 Blob 资源一起销毁;为了降低 Blob 创建销毁的时间开销,就要开个 Blob 资源的内存池(ECS 里开内存池是有毒吧?)。最终实现起来会变得很蛇皮,我个人认为作为一个正常的人类不应该做这样的事情。
正常的解决方案
根据手册的指示,使用 IBodyPairsJob
具体来说,我们写一个 struct 实现 IBodyPairsJob
接口,并把它注入到 StepPhysicsWorld
中,把需要屏蔽的碰撞 Disable
掉。这样就能对碰撞为所欲为了
下面是个简单的代码,他禁用了所有拥有 NonUniformScale
组件的碰撞体的碰撞
[UpdateBefore (typeof (StepPhysicsWorld))]
class DisableCollisionSystem : SystemBase {
struct DisableCollisionJob : IBodyPairsJob {
public ComponentDataFromEntity<NonUniformScale> nonUniformScaleFromEntity;
public void Execute (ref ModifiableBodyPair pair) {
if (nonUniformScaleFromEntity.Exists (pair.Entities.EntityA) nonUniformScaleFromEntity.Exists (pair.Entities.EntityB))
pair.Disable ();
}
}
BuildPhysicsWorld m_buildPhysicsWorld;
StepPhysicsWorld m_stepPhysicsWorld;
protected override void OnCreate () {
m_buildPhysicsWorld = World.GetOrCreateSystem<BuildPhysicsWorld> ();
m_stepPhysicsWorld = World.GetOrCreateSystem<StepPhysicsWorld> ();
}
protected override void OnUpdate () {
if (m_stepPhysicsWorld.Simulation.Type == SimulationType.NoPhysics) return;
SimulationCallbacks.Callback callback = (ref ISimulation simulation, ref PhysicsWorld world, JobHandle inDeps) => {
return new DisableCollisionJob { nonUniformScaleFromEntity = GetComponentDataFromEntity<NonUniformScale> () }.Schedule (simulation, ref world, inDeps);
};
m_stepPhysicsWorld.EnqueueCallback (SimulationCallbacks.Phase.PostCreateDispatchPairs, callback);
}
}
但是需要注意几个点
- 使用
CollisionFilter
来实现的话,碰撞处理过程中的性能要注入IBodyPairsJob
好;但是使用IBodyPairsJob
不需要考虑上述CollisionFilter
的诸多问题,也避免了销毁与创建的开销; - 上面的代码使用了 lambda,却没有用 Burst 优化,因此会产生 GC,可以暂时用蛇皮方法自己搞搞;但 Unity Physic 之后的版本应该会自己解决这个问题,不用我们操心