DOTS Unity.Physics物理引擎碰撞事件处理

最近DOTS发布了正式的版本,同时基于DOTS的理念实现了一套高性能的物理引擎,今天我们给大家分享和介绍一下这个物理引擎的碰撞事件处理以及核心相关概念。

Unity.Physics物理引擎的主要流程与Pipeline

Unity.Physics物理引擎做仿真迭代计算的时候主要通过以下步骤来执行:

  • step1: 从entity里面的ECS组件中获取我们当前的物体的状态数据;
  • step2: 做粗略的broadphase计算阶段,遍历物理世界里面所有的body, 通过AABB包围计算,来快速的判断哪些物体,可能相交;粗略计算,把不会相交的排除掉, 不会相交的就不会改变运动状态;
  • step3: narrowphase阶段: 把可能相交的物体,做进一步的精确的计算;根据他们的物理形状,计算出来准确的碰撞点与相关的碰撞信息;
  • step4: 基于这些碰撞信息, 我们的物理引擎会计算具体的碰撞信息,关节,摩檫力,阻力等计算, 结合物理的原理,计算出来我们的物理刚体的速度,角速度等运动状态。
  • step5: 根据基于全新的运动状态,把所有运动的物体,向前迭代计算(线性速度,角速度,摩擦力等),计算出这帧新的刚体的位置等信息;
  • step6: Unity Physic 通过 ExportPhysicsWorld System 把物理刚体的位置速度等,同步给节点Entity的LocalTransform组件与PhysicVelocity等组件,这样渲染的entity,就会跟着物理引擎的刚体同步移动;

DOTS中基于System与SystemGroup 树行结构来决定DOTS中的迭代顺序,这个是DOTS中很重要的一个概念。Unity Physics将上面步骤与逻辑基于ECS设计思想,分别设计了相关的System与System Group,结构如下:

-->FixedStepSimulationSystemGroup:
 -->PhysicsSystemGroup 
      -->PhysicsInitializeGroup(System Group)
      -->PhysicsSimulationGroup(SystemGroup)
          -->PhysicsCreateBodyPairsGroup
          -->PhysicsCreateContactsGroup
          -->PhysicsCreateJacobiansGroup
          -->PhysicsSolveAndIntegrateGroup
      -->System: ExportPhysicsWorld 

所有物理引擎的迭代计算都是基于FixedStepSimulationSystemGroup,即按照固定的时间间隔来迭代物理仿真,保持物理引擎的一致性与稳定性。所有的物理引擎的仿真计算都放在PysicsSystemGroup下。PysicsSystemGroup包含PhysicsInitializeGroup ,PhysicsSimulationGroup 与一个ExportPhysicsWorld System。上面提到的Step1,在PhysicsInitializeGroup阶段完成, step2~step5在PhysicsSimulationGroup中完成, PhysicsSimulationGroup完成后物理引擎的一帧的迭代计算完成,最后通过ExportPhysicsWorld的System把把物理引擎的内部数据同步到Entity的PhysicsVelocity, LocalTransform等ECS组件。在PhysicsSimulationGroup又有4个subgroup,他们分别对应step2~step5的执行步骤。

Unity Physics碰撞检测事件处理

当PhysicsSimulationGroup的分组执行完成以后,就完成了整个物理引擎的仿真与迭代计算。仿真过程中会产生一个PhysicsWorld,物理世界里面的所有的刚体等相关物理数据(位置,速度等)都可以通过PhysicsWorld得到,最后还被导出到Entity的ECS组件里面。在物理仿真中所有的事件都会被保存到Simulation对象中,这些事件包括了我们常见的碰撞事件与触发器事件。传统模式下我们是通过回调函数来处理的,DOTS模式下我们是在一个System环节内统一来处理这些事件。物理引擎的碰撞与触发事件处理流程如下:

  • Step1: 编写一个System处理逻辑,来处理物理事件;
  • Step2: 指定好System执行的时机,一定要在PhysicsSimulationGroup之前或者之后,这样才能拿到碰撞事件的数据;
  • Step3: 通过编写Job,来遍历当前所有发生的碰撞事件,然后编写每个碰撞事件的处理逻辑;
  • Step4: 获取存储事件的Simulation单例,传递给job来进行具体执行;

碰撞事件的处理:

当所有的模拟迭代计算完成后,会把过程中的所有碰撞事件对存放到Simulation对象中,我们可以通过(SystemBase|SystemAPI|EntityQuery).GetSingleton<SimulationSingleton>().AsSimulation()获取Simulation对象。

要处理所有的碰撞事件,我们先编写一个System用来编写事件处理逻辑,然后编写一个Job,继承自IcollisionEventsJob,这样就可以在Job中遍历所有的碰撞事件,每个碰撞事件都调用Job的Execute函数,在它里面来处理每个碰撞事件的逻辑。代码如下:

[UpdateInGroup(typeof(FixedStepSimulationSystemGroup))]
[UpdateBefore(typeof(PhysicsSimulationGroup))] // We are updating before `PhysicsSimulationGroup` - this means that we will get the events of the previous frame
public partial struct GetNumCollisionEventsSystem : ISystem
{
    [BurstCompile]
    public partial struct CountNumCollisionEvents : ICollisionEventsJob
    {
        public NativeReference<int> NumCollisionEvents;
        public void Execute(CollisionEvent collisionEvent)
        {
            NumCollisionEvents.Value++;
        }
    }

    [BurstCompile]
    public void OnUpdate(ref SystemState state)
    {
        NativeReference<int> numCollisionEvents = new NativeReference<int>(0, Allocator.TempJob);
        
        state.Dependency = new CountNumCollisionEvents
        {
            NumCollisionEvents = numCollisionEvents
        }.Schedule(SystemAPI.GetSingleton<SimulationSingleton>());

        // ...
    }
}

触发器事件TriggerEvent处理:

触发器事件与碰撞事件类似,我们只要编写一个ItriggerEventsJob就可以遍历当前所有的触发器事件了,代码如下:

[UpdateInGroup(typeof(FixedStepSimulationSystemGroup))]
[UpdateAfter(typeof(PhysicsSimulationGroup))] // We are updating after `PhysicsSimulationGroup` - this means that we will get the events of the current frame.
public partial struct GetNumTriggerEventsSystem : ISystem
{
    [BurstCompile]
    public partial struct CountNumTriggerEvents : ITriggerEventsJob
    {
        public NativeReference<int> NumTriggerEvents;
        public void Execute(TriggerEvent collisionEvent)
        {
            NumTriggerEvents.Value++;
        }
    }

    [BurstCompile]
    public void OnUpdate(ref SystemState state)
    {
        NativeReference<int> numTriggerEvents = new NativeReference<int>(0, Allocator.TempJob);
        
        state.Dependency = new CountNumTriggerEvents
        {
            NumTriggerEvents = numTriggerEvents
        }.Schedule(SystemAPI.GetSingleton<SimulationSingleton>());

        // ...
    }
}

今天的分享就到这里,关注我们 + 企.鹅.裙 428 540 563 获取更多的DOTS的相关资料


下面是DOTS的VIP课程前18节视频,免费观看

Unity DOTS进阶与项目实战(B站18集)

第001课DOTS的环境安装与准备事项

第002课 DOTS的核心机制与概述

第003课DOTS的SubScene

第004课Component的概述与普通组件的Baker

第005课System与SystemGroup概述

第006课DOTS中的ECS核心概念总结

第007课Baking系列之Baking与Baker详解

第008课Baking系列之BakingSystem与BakingWorld详解

第009课FilterBakingOutput与PrefabsInBaking

第010课BlobAsset核心机制分析

第011课Aspect核心机制分析

第012课 StructChange核心机制详解

第013课Managed与Unmanaged Component详解与性能分析

第014课ShareComponent核心机制与性能分析

第015课CleanupComponent核心分析

第016课 Dynamic Buffer Component详解与分析

第017课Tag与Chunk Component详解与分析

第018课Enableable与Singleton组件详解与分析

相关推荐
Sitarrrr37 分钟前
【Unity】ScriptableObject的应用和3D物体跟随鼠标移动:鼠标放置物体在场景中
3d·unity
极梦网络无忧39 分钟前
Unity中IK动画与布偶死亡动画切换的实现
unity·游戏引擎·lucene
逐·風9 小时前
unity关于自定义渲染、内存管理、性能调优、复杂物理模拟、并行计算以及插件开发
前端·unity·c#
_oP_i10 小时前
Unity Addressables 系统处理 WebGL 打包本地资源的一种高效方式
unity·游戏引擎·webgl
代码盗圣14 小时前
GODOT 4 不用scons编译cpp扩展的方法
游戏引擎·godot
Leoysq19 小时前
【UGUI】实现点击注册按钮跳转游戏场景
游戏·unity·游戏引擎·ugui
PandaQue21 小时前
《潜行者2切尔诺贝利之心》游戏引擎介绍
游戏引擎
_oP_i1 天前
unity中 骨骼、纹理和材质关系
unity·游戏引擎·材质
Padid1 天前
Unity SRP学习笔记(二)
笔记·学习·unity·游戏引擎·图形渲染·着色器
Tp_jh1 天前
推荐一款非常好用的C/C++在线编译器
linux·c语言·c++·ide·单片机·unity·云原生