导购网站怎么做有特色,杭州网站制作流程,做推广要知道的网站,wordpress加载优化大家好#xff0c;这里是七七#xff0c;今天开始更新物理引擎相关的优化部分了#xff0c;本文介绍的是物理引擎内部工作情况。 
Unity技术有两种不同的物理引擎#xff1a;用于3D物理的Nvidia的PhysX和用于2D物理的开源项目Box2D。然而#xff0c;Unity对它们的实现是高…大家好这里是七七今天开始更新物理引擎相关的优化部分了本文介绍的是物理引擎内部工作情况。 
Unity技术有两种不同的物理引擎用于3D物理的Nvidia的PhysX和用于2D物理的开源项目Box2D。然而Unity对它们的实现是高度抽象的从通过主Unity引擎配置的更高级别Unity API的角度来看两个物理引擎解决方案以功能相同的方式运行。 
无论是哪种情况对Unity的物理引擎了解的越多就越能理解可能的性能增强。本文将介绍一些关于Unity如何实现这些系统的理论。 
一、物理和时间 
物理引擎通常是在时间按固定值前进的假设下运行的Unity的两个物理引擎也都以这种方式运行。每个迭代称为时间步长。物理引擎将只使用特定时间值来处理每个时间步长这与渲染上一帧所花费的时间无关。该时间步长在Unity中称为固定更新的时间步长默认值为20ms。 注意由于体系结构浮点值的表示方式的不同以及客户端之间的延迟如果物理引擎使用可变的时间步长就很难在两台不同的计算机之间产生一致的碰撞和力的结果。这样物理引擎往往会在多人的客户端之间或录制的重播期间生成不一致的结果。 固定的更新在物理引擎执行自己的更新之前处理而这两者之间的联系是不可分割的。这个过程开始于确定是否已经过了足够的时间来开始下一个固定的更新。一旦确定了这一点则结果将有所不同这取决于自上次固定更新以来经过的时间。 
如果经过了足够的时间则固定更新的处理将调用在场景中所有激活的MonoBehaviour中定义的FixedUpdate回调接着处理与固定更新相关的任何协程特别是那些生成WaitForFixedUpdate的协程。注意对于在这两个过程中调用的方法没有执行顺序的保证所以不应该在这个假设下编写代码。一旦这些任务完成物理引擎就可以开始处理当前的时间步长并调回任何必要的触发器和碰撞器。 
相反如果上次固定更新以来经过的时间太短小于20ms则跳过当前的固定更新并且之前列出的所有任务不会再当前迭代期间处理。此时输入、游戏逻辑和渲染将正常进行。完成此活动后Unity将检查是否需要处理下一个固定更新。 
在高帧率下渲染更新可能会在物理引擎获得自身更新机会之前完成多次更新。这个过程在运行时不断重复使固定的更新和物理引擎比渲染具有更高的优先级同时也强制物理模拟具有固定的帧率。 提示为了确保对象在固定更新之间平稳移动物理引擎根据下一次固定更新之前的剩余时间在处理当前状态之后在上一个状态和应处于的状态之间对每个对象的可见位置进行插值。这种插值可以确保对象的移动非常平稳尽管它们的物理位置、速度等更新的频率低于渲染帧率。 FixedUpdate()回调适用于任何期望独立于帧率的游戏行为。AI通常在固定的更新中计算因为如果假设一个固定更新的频率会更容易开发。 
1.1 最大允许的时间步长 
需要注意的是如果自上次固定更新例如游戏暂时卡顿以来已经过了很长时间那么固定更新将继续在相同的固定更新循环中计算直到物理引擎赶上当前时间。如果上一帧画了100ms用于渲染例如一个突然的CPU峰值导致主线程阻塞了很长时间那么物理引擎将需要更新5次。由于默认固定更新的时间步长为20ms在再次调用Update之前还需要调用5次FixedUpdate。当然如果在这5次固定更新时有很多物理活动需要处理例如总共花费了超过20ms处理它们那么物理引擎将继续调用第6次更新。 
因此在物理活动较多时物理引擎处理固定更新的时间可能比模拟的时间要长。例如如果用30ms来处理一个固定的更新模拟20ms的游戏它就已经落后了需要它处理更多的时间步长来尝试和跟上但这可能会导致它落后得更远需要它处理更多的时间步长等等。在这些情况下物理引擎永远无法摆脱固定的更新循环并允许另一帧进行渲染这个问题通常称为死亡螺旋。但是为了防止物理引擎在这些时刻锁定游戏存在允许物理引擎处理每个固定更新循环的最长时间则它将停止并放弃进一步的处理直到下一次渲染更新完成。这种设计允许渲染管线至少将当前状态进行渲染并允许用户输入以及游戏逻辑在物理引擎出现异常的罕见时刻做出一些决策。 
该设置可以通过EditProject SettingsTimeMaximum Alowed Timestep来访问。 
1.2 物理更新和运行时的变化 
当物理引擎以给定的时间步长处理时它必须移动激活的刚体对象检测新的碰撞并调用相应对象的碰撞回调。Unity文档明确指出应该在FixedUpdate和其他物理回调中处理对刚体对象的更改原因正是如此。这些方法与物理引擎的更新频率紧密耦合而不是游戏循环的其他部分如Update。 
这意味着诸如FixedUpdate和OnTriggerEnter的回调函数能够安全更改Rigidbody的位置而诸如Update和对WaitForSeconds或WaitForEndOfFrame的协程却不能。忽略这一建议可能会导致意想不到的物理行为因为在物理引擎有机会捕获和处理所有这些对象之前可能会对同一个对象进行多次更改。 
对Update回调中的对象应用力或脉冲而不考虑这些调用的频率是非常危险的。例如在玩家按住一个键时给Update功能应用10牛顿的力会导致两个不同设备之间的合成速度完全不同于在固定更新中执行相同的操作。事实上不能依赖Update调用的次数是一致的。但是在FixedUpdat回调中这样做会更加一致。因此必须确保在适当的回调中处理所有与物理引擎相关的行为否则就可能引入一些令人困惑很难重现的游戏漏洞。 
从逻辑上讲在任何给定的固定更新迭代中花费的时间越多在下一次游戏逻辑和渲染过程中花费的时间就越少。由于物理引擎几乎没有任何工作要做而且FixedUpdate回调有很多时间来完成它们的工作因此大多数情况下这会导致一些小的、不明显的后台处理任务。然而在某些游戏中物理引擎可能在每次固定更新期间执行大量计算。这种物理处理时间上的瓶颈会影响帧率导致它在当物理引擎负担越来越大的工作负载时帧率急剧下降。基本上渲染管线将尝试正常进行但每当需要进行固定更新时物理引擎处理时间很长渲染管线在帧结束之前几乎没有时间生成画面会导致突然停顿。物理引擎达到允许的最大时间步长会导致过早停止的视觉效果。所有这些加在一起会产生非常糟糕的用户体验。 
因此为了保持平滑、一致的帧率需要通过最小化物理引擎处理任何给定时间步长所需的时间来为渲染释放尽可能多的时间这适用于最佳情况没有移动和最坏情况所有对象同时与其它对象发生碰撞。可以在物理引擎中调整一些与事件相关的特征和值以避免这些性能缺陷。 
二、静态碰撞器和动态碰撞器 
在Unity中术语静态和动态又一个相当极端的命名空间冲突。静态通常意味着所讨论的对象或处理不移动、保持不变或只存在于一个位置而动态则意味着相反对象或处理倾向于改变或移动。然而要记住术语静态和动态的用法在每种情况下都不同。 
动态碰撞器只意味着GameObject包含Collider组件和Rigidbody组件。通过将Rigidbody添加到Collider所附加的相同对象上物理引擎会将该碰撞器视为带有包围物理对象的立体对象它会对外部的力和与其他Rigidbody的碰撞体作出反应。如果一个动态碰撞器与另一个动态碰撞器发生碰撞它们都会基于牛顿运动定律做出反应。 
也可以使用没有附加Rigidbody组件的碰撞器这种称为静态碰撞器。这种碰撞器有效地起到了无形屏障的作用动态碰撞器可以撞到这些屏障但是静态碰撞器不会做出响应。从另一个角度来看就是把没有Rigidbody组件的物体想象成具有无穷大的质量。因此静态碰撞器非常适合用作全局屏障和其他不能移动的障碍物。 
物理引擎自动将动态碰撞器和静态碰撞器分为两种不同的数据结构每种结构都经过优化以处理现有碰撞器的类型。这有助于简化未来的任务例如解析两个静态碰撞器之间的碰撞和脉冲。 
三、碰撞检测 
Unity中的碰撞检测有3种设置可以在Rigidbody组件的Collision Detection属性中设置Discrete离散、Continuous连续和ContinuousDynamic连续动态。 
Discrete设置可以实现离散碰撞检测有效地根据物体的速度和经过的时间在某个时间步长将对象传送一小段距离。一旦所有对象都被移动了物理引擎就会对所有重叠执行便捷进行立体检查将它们视为碰撞并根据它们的物理属性和重叠方式来处理它们。如果小对象移动得太快此方法可能会有丢失碰撞的风险。 
其余的两个设置都将启用连续碰撞检测其工作方式是从当前时间步长的起始和结束位置并见哈这个时间段中是否有任何碰撞。这降低了错过碰撞的风险生成了更景区的模拟但代价是CPU的开销显著高于离散碰撞检测。 
Continuous设置尽在给定碰撞器和静态碰撞器之间用连续碰撞检测。同一碰撞器与动态碰撞器之间的碰撞仍将使用离散碰撞检测。 
同时ContinuousDynamic设置使碰撞器能够与所有静态和动态碰撞器进行连续碰撞检测其在资源消耗方面最大。 
四、碰撞器类型 
Unity中有4种不同类型的3D碰撞器其性能成本从最小到最大依次为球体Sphere、胶囊体Capsule、立方体Box、网格Mesh。 
前三个碰撞器类型通常称为基础类型。包含非常特殊的形状尽管它们通常可以在不同方向缩放以满足某些要求。网格碰撞器可以根据指定的网格自定义为特定形状。还有3种类型的二维碰撞器圆Circle、方框Box和多边形Polygon在功能上分别与球形、立方体和网格碰撞器相似。以下所有信息基本上都可以转换为等效的二维形状。 提示也可以在Unity中生成3D圆柱体但这只是它的图形表现。自动生成的圆柱体使用胶囊体碰撞器表示其物理保卫面积这可能不会产生预期的物理行为。 另外有两种不同的网格碰撞器Convex凸的和Concave凹的。两者的不同之处在于凹形形状至少具有一个大于180度的内角如图所示 提示区分凹形和凸形的一个简单方法是凹形至少有一个凹陷。 两种网格碰撞器类型都使用相同的组件MeshCollider组件这种网格碰撞器类型是通过切换Convex复选框选项生成的。启用此选项将允许对象与所有基本形状球形、长方体等以及其他启用Convex的网格碰撞器碰撞 
此外如果为凹形的网格碰撞器启用了Convex复选框则物理引擎将自动简化该网格碰撞器生成的碰撞器具有能将其包围的最接近的凸形。 
在上图中如果导入右侧的凹形网格并启用Convex复选框它将生成一个更接近左侧凸形形状的碰撞器。在这两种情况下物理引擎都将尝试生成一个碰撞器该碰撞器与附加的网格的形状匹配上限为255个顶点。如果目标网格的顶点数超过此值则在网格生成过程中会引发错误。 
碰撞器组件还包含IsTrigger属性允许将他们视为非物理对象但当其他碰撞器进入或离开它们时仍调用物理时间。这些称为触发体积。通常当一个碰撞器接触、保持接触、或停止接触时分别会调用OnCollosionEnter等三个回调但当碰撞器用作出发体积时将调用OnTriggerEnter等三个回调。 注意由于处理物体间碰撞的复杂性凹形网格碰撞器不能是动态碰撞器只能用作静态碰撞器或触发体积。如果试图将Rigidbody组件添加到凹形网格碰撞器中Unity将完全忽略它。 提示如果真的需要将凹形网格碰撞器作为Rigidbody组件则解决方案是将对象分割成独立的凸形网格碰撞器的组合。例如想利用两个凸形来组合一个L形刚体。不幸的是因为这是一个微妙的决定所以没有自动的方法来实现需要手动执行这样的分解。 五、碰撞矩阵 
物理引擎具有一个碰撞矩阵该矩阵定义允许哪些对象与哪些对象发生碰撞。当处理边界体积重叠和碰撞时物理引擎家那个自动忽略不适合此矩阵的对象。这节省了碰撞检测阶段的物理处理还允许对象彼此移动而不发生任何碰撞。 
碰撞矩阵可以通过Edit | Project Settings | (Physics/Physice2D) | Layers Collision Matrix访问。 
碰撞矩阵系统通过Unity的层Layer系统工作。矩阵表示层与层之间的组合启用复选框意味着在碰撞检测阶段将检查这两个层中的碰撞器。 
要注意的是对于整个项目总共只能有32个层。 
六、Rigidbody激活和休眠状态 
每一个现代物理引擎都有一个共同的优化技术即静止物体的内部状态从活动变为休眠。当Rigidbody处于休眠状态时在固定的更新过程中处理器几乎没有时间来更新对象直到它被外力或碰撞事件唤醒。 
用于确定静止状态的测量值在不同的物理引擎中往往会有所不同可以使用Rigidbody的线速度和角速度、动能、栋梁或其他一些物理属性来计算。Unity的两个物理引擎都是通过评估物体的质量归一化动能来工作的这基本上可以取决于物体速度平方的大小。 
如果物体的速度在短时间内没有超过某个阈值那么物理引擎将假设物体在经历新的碰撞或施加新的力之前不再需要再次移动。在此之前休眠对象将保持其当前位置。可以在Edit | Project Settings | Physice | Sleep Threshold下修改阈值还可以从Profiler窗口的Physics Area中获取活动Rigidbody对象的总数。 
七、射线和对象投射 
物理引擎的另一个常见特征是能够将射线从一个点投射到另一个点并用路径中的一个或多个对象生成碰撞信息这就是所谓的射线投射。通过射线投射来实现一些游戏机制是很常见的比如射击其实现方式通常是执行从玩家到目标位置的射线投射并在其路径中找到任何符合要求的目标。 
还可以通过Physics.OverlapSphere检查在空间中固定点的有限距离内获得的目标列表这通常用于实现效果区域的游戏功能如手榴弹爆炸甚至可以用Physics.SphereCast和Physics.CapsuleCast在空间中向前投射整个对象。这些方法通常用来模拟宽激光束或者只是确定什么东西在移动角色的路径中。 
八、调试物理 
物理错误通常分为两类本来不应该碰撞的一对对象碰撞了本来应该碰撞的没碰撞在碰撞发生之后发生了意想不到的事情。前一种情况往往更容易调试通常是由于碰撞矩阵中的错误射线投射中使用的层不正确或者对象碰撞器的大小或形状错误。后一种情况往往更难解决因为要获得以下3条消息 确定哪个碰撞对象导致了问题在解决之前确定碰撞条件重现碰撞 获得了这3条信息中的任何一条都会使解决方案更容易但这些信息在某些情况下都很难获得。 
Profiler在Physics和Physics2D区域提供了一些测量信息这是相当有用的可以得到CPU活动在不通过类型隔离的所有刚体和刚体组上花费的量这些类型包括动态碰撞器、静态碰撞器、运动对象、触发体积、约束和触点。 
Physics 2D区域包含了更多的信息比如睡眠和活动刚体的数量以及处理时间步长的时间。在这两种情况下详细的细分试图提供了更多的信息。这些信息有助于关注物理性能但它并不能指出在物理行为中出现错误时发生了什么。 
一个更适合帮助调试物理问题的工具是Physics Debugger它可以通过Window | Physics Debugger打开。这个工具有助于从Scene窗口中过滤出不同类型的碰撞器从而更好地了解哪些对象相互碰撞。当然这对确定问题的条件和复现问题没有太大帮助。