文章目录
1.概述 Rigidbody 和 Collider
Unity 中的物理模拟,需要为对象添加2个组件:Rigidbody(刚体) 和 Collider(碰撞器),分别扮演了不同的角色。
1.1刚体 Rigidbody
刚体,定义了对象收到外力后,如何模拟其行为,如翻滚,掉落。
当为一个对象添加了 Rigidbody 组件后,就会模拟受重力而掉落。如果再加上collider组件,则会响应外部的力,而运动。
添加 Rigidbody后,我们就不能通过 transform 组件来移动物体了,只能由物理系统来模拟驱动。当然这不是绝对的,某些特定情境,需要关掉物理模拟,将物体摆放到指定位置,再重新打开物理模拟。
有时,我们希望对象参与物理模拟,但是其行为还是由逻辑控制,比如,对于游戏内的NPC,我们需要由代码控制其运动,同时又需要添加rigidbody,这样才能被Trigger检测到,对于这种情况,可以将 Rigidbody 设置为动力学物体(Is Kinemiac)。Rigidbody 的 Is Kinematic 开关在运行时改变,会影响效率。
Sleeping
当物体的运动速度低于于定义的线性或旋转速度,则物理系统会将对象设置为 sleeping 状态,停止update,以节省性能开销。当由受到外部的力时,再 awake ,开始update。
WakeUP
特殊情况下,可能会出现问题,比如,我们将一个sleeping的茶壶对象下面的桌子在脚本中移走了,因为这个过程没有产生力,所以茶壶不会awake,就会悬浮在那里。物理系统没有提供解决办法,这需要我们自己在代码中将茶壶awake,调用Rigidbody.WakeUp()接口来手动唤醒。
1.2碰撞体 Colliders
1.2.1Collider
定义了物体的形状来进行碰撞模拟。物理碰撞体的形状不需要严格和物体一致,只要能表示其物理形状即可。比如一个复杂的人,我们可以用一个胶囊体来定义其碰撞形状。
Unity内建了很多碰撞体,以下时常用的碰撞体:
- BoxCollider 立方体碰撞体
- SphereCollider 球形碰撞体
- CapsuleCollider 胶囊碰撞体
- MeshCollider 从对象的网格创建碰撞体。MeshCollider之间不支持碰撞检测,效率太低。可以将MeshCollider的Convex选项打开,则能支持MeshCollider之间的碰撞检测,同时提高性能。
- WheelCollider 创建载具的轮子的碰撞体
- TerrainCollider 处理Unity地形系统的碰撞
2D中相应的BoxCollider2D,CircleCollider2D,CapsuleCollider2D,以及其它转为2D建立的碰撞体类型,如PolygonCollider2D。
Box,Sphere,Circle,Capsule这些简单碰撞体的效率相对都是较高的,建议使用这些简碰撞体。
效率排序:Sphere,Capsule,Box,Cilinder,Convex Mesh,Mesh,越来越慢
组合碰撞体 一个物体上挂多个简单碰撞体来模拟其形状,这种就是组合碰撞体,组合碰撞体只能有一个刚体。
**注意:**碰撞体对象进行了不等比例缩放后,碰撞检测可能失效,所以缩放必须时等比例的。
物理材质 物理发生碰撞时,需要根据其表面物理材质来计算最终的受力。例如冰面很滑,篮球摩擦力很大,且很有弹性,这些参数是由物理材质定义的,需要仔细调节物理材质参数来实现我们的模拟效果。
1.2.2碰撞体分类
根据物体是否有刚体组件,以及其Is Kinematic 属性的不同设置,其物理表现分成三类:
-
静态碰撞体
只有Collider,没有Rigidbody。通常管卡的静态部分,比如建筑,障碍,是该类型。
-
刚体碰撞体
Collider + Rigidbody + none-kinematic,正常物理对象,物理系统会一直进行碰撞检测,模拟物理运动效果。
-
Kinematic刚体碰撞体
Collider + Rigidbody + Kinematic,通常用在经常需要变化物理状态的碰撞体上,比如会移动的门。与静态碰撞体不同,该类型会对其它物体长生摩擦力,在发生碰撞时唤醒其它刚体对象。常见例子,布娃娃,是一个Kinematic碰撞体,由动画系统控制。当死亡时,关闭Is Kinematic,由物理系统模拟其肢体状态。
1.2.3碰撞体与触发器
碰撞体 可以在脚本中监听碰撞检测结果。当发生碰撞时,以下脚本接口会被回调
- OnCollitionEnter() 开始碰撞时回调
- OnCollitionStay() 碰撞持续中回调
- OnCollitionLeave() 结束碰撞时回调
碰撞事件表
静态碰撞体 | 刚体碰撞体 | Kinematic刚体碰撞体 | |
---|---|---|---|
静态碰撞体 | N | Y | N |
刚体碰撞体 | Y | Y | Y |
Kinematic刚体碰撞体 | N | Y | N |
触发器 物理对象还可以实现触发器逻辑,将Collider 的Is Trigger 勾选,则触发触发器事件。
- OnTriggerEnter() 进入触发范围时回调
- OnTriggerStar() 在触发范围内时回调
- OnTriggerLeave() 离开触发范围时回调
触发事件表
静态碰撞体 | 刚体碰撞体 | Kinematic刚体碰撞体 | |
---|---|---|---|
静态碰撞体 | N | Y | Y |
刚体碰撞体 | Y | Y | Y |
Kinematic刚体碰撞体 | Y | Y | Y |
1.3关节 Joints
关节是一种物理链接关系,比如门的合页,滑动门的滑轨,甚至绳子。关节总是限制一类运动自由度,并允许一类自由度来定义。比如合页禁止平移,允许限制的旋转。
Unity提供了不同类型的关节比如:
- Character Joint 模拟球窝关节,如胳膊与肩膀的连接,胳膊可以绕着肩膀旋转。禁止所有方向位移,允许所有轴上的旋转。
- Fixed Joint 固定关节,限制自身移动,只能跟随attached到的刚体运动。常用于2个连接的刚体可以被断开,或者跟随另一个刚体运动,但是不想设置父子关系。
- Configurable Joint 可以用来模拟任意骨骼关节,例如布娃娃。可以配置关节在任何自由度上的运动。
- Hingle Joint 铰链关节,适用于房门,允许绕一个轴旋转。
- Sprint Joint 弹簧,让2个物体之间始终保持适当的距离。
1.4 角色控制器 CharacterController
虽然理论上说,游戏内的角色,可以用刚体组件控制,因为人物由质量,会被周围环境阻挡,受重力影响。但是实际上角色与环境的交互比普通刚体要复杂,会有各种不可预料的问题,比如卡墙角,被其它对象挤开,以至于满天飞等。因此Unity提供了角色控制器来专门模拟角色的物理行为。
2. 刚体
刚体组件配置界面如下:
2.1 组件属性
- Mass 物体的质量,默认是千克。
- Drag 物体移动式受到的空气阻力。0表示没有阻力,设置为无穷大物体会立即停止移动。
- Angular Drag 物体选装时受到的空气阻力。0表示没有阻力,设置为无穷大物体并不会立即停止旋转。
- Use Gravity 物体是否受重力影响。
- Is Kinematic 是否时动力学物体。勾选后不再收物理系统控制,而是在脚本中通过Transform来移动物体。
- Interpolate 差值平滑方式。当刚体出现抖动时,可以尝试一下选项来解决:
- None 不执行差值平滑算法
- Interpolate 利用上一帧的位置进行平滑处理
- Extrapolate 估算未来一帧的位置,并进行平滑处理
- Collision Detection 碰撞检测方式。当物体告诉运动时,由于帧数率有限,会穿过物体。根据物体运动速度选择一下碰撞检测方式。
- Discrete 非连续,每帧之间的检测是独立的。该方式效率最高,是默认方式。
- Continuous 连续检测方式。
- 与静态碰撞体的碰撞,采用sweep-based的continuous collidion dectection(CCD)。什么是CCD?
- 与其它设置了 Continuous Dynamic 的刚体的碰撞,采用CCD碰撞。
- 与其它刚体的碰撞,采用非连续方式。
- Continuous Dynamic 连续动态检测方式。
- 与静态碰撞体的碰撞,采用sweep-based的continuous collidion dectection(CCD)。
- 与其它设置了 Continuous 或 Continuous Dynamic 的刚体的碰撞,采用CCD碰撞。
- 与其它刚体的碰撞,采用非连续方式。
- Constraints 运动约束
- Freeze Position 位移时,锁定x,y,z中的一个或多个轴。该限制是在世界坐标系内。
- Freeze Rotation 限制刚体沿x,y,z轴的旋转。该限制是在局部坐标系内。
2.2 父子关系
当一个物体被物理系统控制时,其和父物体之间的关系时半独立的:一方面,移动父物体,子物体也会被移动。另一方面,子物体会由于重力等原因独立运动。
2.3 脚本
要控制刚体,要在脚本重用代码为刚体施加力或扭矩,调用Rigidbody.AddForce 和 Rigidbody.AddTorque 方法。一定要避免通过transform来移动物体。
2.4 动画
某些情况下,尤其时布偶系统,需要让物体在物理系统控制和动画控制之间切换,可以通过Is Kinematic 开关实现。当勾选 Is Kinematic 时,物体将不再受碰撞,重力等影响了,将直接由动画驱动,并且刚体依然会对其它刚体产生物理影响,比如角色会把其它刚体撞飞。
2.5 刚体和碰撞体
碰撞体定义了物体的物理形状,需要和刚体一起使用,来模拟碰撞。如果没有碰撞体,2个刚体不会碰撞,会重叠,穿过。
2.6 连续碰撞见
连续碰撞,用来防止快速移动的物体互相穿过而错过了碰撞时机。穿过的原因是,当前帧2个物体未碰撞,下一帧,由于移动过快,2个物体已经错过。解决该问题,将碰撞体的 Collision Dectectin 设置为连续的。
实际上,连续碰撞是一种备用措施,防止物体之间互相穿过,但是不保证碰撞后产生的结果的精确性。如果需要精确的碰撞,在ProjectSettings的TimeManager中,将Fixed Timestep改的更小,但是效率也更低。Fixed Timestep是物理更新的时间间隔,当逻辑帧速率低于物理模拟帧速率时,没逻辑帧,物理模拟将插帧,已完成模拟。在手机上这回造成严重的发热,所以手机游戏不建议复杂的物理计算,或者进行一些替代算法。
2.7 比例和单位
物体外形的大小,比质量更重要。如果发现刚体的表现与预计不同(移动太慢,有漂浮感或碰撞后速度有问题),可以检查物体大小,比例。Unity默认一个transform的单位为1米,整个物理系统的计算是按照物理学的标砖量纲来进行的,所以物体尺寸正确很重要。比如,一幢大楼的倒塌和积木搭的建筑倒塌效果是不同的。
Unity中的人物模型,高度尽量在2米左右。
如果游戏已经确定了特殊的比例,则只能修改Unity的默认坐标空间轴的比例,但这回给物理模拟带来额外的运算量。
2.8 其它问题和建议
- 2个刚体质量的比值,决定了碰撞后,它们如何运动。
- 质量大的物体不会下落的更快,调整下落速度,可以调整Drag空气阻尼参数。阻尼越小显得越重,越大显得越轻,比如0.001是金属,10则是羽毛。
- 如果想要直接修改transform来移动,但依然需要碰撞检测,则使用刚体,并勾选 Is Kinematic。
- 如果物体脚本需要碰撞或触发器事件,则需要为它们挂载刚体组件。
- 位移阻尼无穷大,位移会立即停止;角位移阻尼无穷大,旋转不会立即停止。
3.碰撞体
3.1 BoxCollider
立方体形状的碰撞体,是最常用的碰撞体,很多物体都可以用该类型碰撞体来表示,如石头,墙,平整的地面或斜坡。
主要参数:
- Is Trigger 是否是触发器,勾选后变为触发器,会触发触发器事件,不再与刚体碰撞。
- Material 碰撞体物理材质。物理材质决定了摩擦力,弹性等。
- Center 中心点坐标(局部坐标系)。
该三个参数也是所有碰撞体的基本参数,后面的碰撞体将不再介绍这三个参数。
3.2 CapsuleCollider
形状像药物胶囊,由2个半球和一个圆柱组成。最常用的是表示角色。
主要参数:
- Radius 半球和圆柱体的半径
- Height 胶囊的高度
- Direction 胶囊方向,默认是Y轴,即向上。
3.3 MeshCollider
网格碰撞体,由物体的渲染网格生成物理碰撞体。由于精确的覆盖了模型,所以非常精确,但是效率也非常低。所以,Unity添加了限制,只有都是Covex(凸体)才可以添加Rigidbody来模拟碰撞行为,非Convex如果添加Rigidbody会在运行时报错。
主要参数:
- Convex 是否使用凸体,勾选后会有Mesh生成凸体,并且最多有255个三角面。
- Cookinbg Options 打开或关闭物理引擎处理Mesh的选项,默认都是开启的
- None or Everything
- Cook for Faster Simulation 开启后会执行额外的步骤,以保证生成的Mesh运行效率最优化。
- Enable Mesh Cleaning 清理退化三角形,会让碰撞点更精确。
- Weld Colocated Vertices 删除在相同位置的顶点
- Mesh 进行碰撞或生成凸体的网格。
问题:
- 碰撞精度高,但是性能极低。
- 碰撞表面是单面的(这跟渲染相同),进入网格碰撞体时会发生碰撞,从内向外就会直接穿过,不会碰撞。
- 用作MeshCollider的网格,某些情况下,为了保证正常工作,导入时,要打开【可读/可写】开关:
- GameObject使用了负的缩放值,如(-1,1,1)。
- 发生了切向变形的物体,比如旋转了的网格碰撞体的父节点,被缩放过了。
- 如果网格只是用在碰撞中,则可以只保留顶点信息,把发现,等信息去掉,在导入界面设置。
3.4 SphereCollider
球形碰撞体
3.5 TerrainCollider
地形碰撞体
参数:
- Terrain Data 地形数据
- Enable Tree Colliders 勾选后,地形上的树木也会有碰撞。
3.6 物理材质
物理材质用来定义物体表面的摩擦力,与碰撞时的弹力。物理材质跟渲染材质一样是一种资源,通过Assets>Create>Physics Material 来创建。
参数:
- Dynamic Friction 动摩擦系数。物体之间正在滑动时起作用,通常为0-1。0类似冰面效果,1时滑动会很快停止。
- Static Friction 静摩擦系数。物体需要由静止到滑动切换时起作用,用来阻止启动滑动,通常为0-1。0类似冰面,1则很难滑动。
- Bounciness 弹性。为0,碰撞时不会反弹,为1时碰撞不会损失能量,持续反弹。
- Friction Combine 如何根据碰撞的两个对象各自的摩擦系数计算综合摩擦力。
- Average 相加平均
- Minimum 取最小值
- Maximum 取最大值
- Multiply 两个摩擦系数相乘
- Bounciness Combine 如何计算弹性系数,同摩擦系数。
当2个物体表面接触时,受到的摩擦力和弹力一定是一样的。当2个物体摩擦力,弹力计算方式不同时,根据优先级确定:Average<Minimum<Multiply<Maximum。
目前Unity使用的是PhysX物理引擎,是以效率和稳定性优化的,有时效果可能不太符合物理定律,需要我们调整相关参数来使其符合我们的预期。
新的DTOS物理引擎同时兼容PhysX和Hovok,不过现在正个DTOS的物理系统都不稳定,期待吧。
4.关节 Joints
4.1 固定关节 FixedJoint
固定关节限制了一个物体相对于另一个物体的运动,不需要是父子关系。适合制作2个可以被外力断开的物体,或者需要一起运动又不是父子关系的物体。
例如黏性炸弹,附着到任务身上后,创建固定关节连接,则炸弹跟随任务移动。
必须要添加刚体组件。
参数
- Connected Body 与哪个物体相连,不指定则连接到场景上。
- Break Force 断开连接需要的力
- Break Torque 断开需要的扭矩
- Enable Collision 被连接的2个对象之间,是否还进行碰撞检测
- Enable Preprocessing 预处理选项,当关节模拟不稳定时,可以尝试关掉。
4.2 铰链关节 HingeJoint
铰链关节连接2个刚体,限制它们的相对运动,像门合页,如房门,链条,钟摆等。铰链有一个轴,2个刚体绕着这个轴旋转。
一个GameObject仅能添加一个铰链关节。铰链的位置和方向由Anchor和Axis参数指定。如果铰链本身还随着另一个物体运动,则组要指定Connected Body,否则不指定,直接连载场景上。
例如实现门的效果,轴的方向是垂直向上,教练组件挂在门上,位置在门和墙之间。不需要将铰链连接到墙上,默认连接到场景就可以了。如果要在门上再开一个狗门,则狗门沿着X轴旋转,而且整体跟随大门运动,所以要指定铰链连接到大门上。
再比如要实现锁链,可以用一连串的铰链连接多个环。
参数
- Connected Body 连接到的刚体,跟随该刚体运动。不指定默认连接到场景。
- Anchor 铰链轴的位置,是本物体的局部坐标。
- Axis 铰链旋转轴,用局部坐标表示。
- Auto Configure Connected Anchor 勾选时,Connected Anchor 会根据Anchor参数自动计算。不勾选,则需要自己设置Connected Anchor。
- Connected Anchor 铰链相对于被连接物体的位置。使用被连接物体的局部坐标。
- Use Spring 是否启用弹簧力。弹簧力可以在物体旋转离开指定角度时,施加一个回弹力。
- Spring 到达指定角度时的弹力大小
- Damper 回弹阻力,越大,回弹速度越慢
- Target Position 目标角度,弹簧力会尽量维持在这个角度
- Use Motor 启用马达会让铰链关节像马达一样自动旋转。
- Target Velocity 目标速度,马达会尽量达到该旋转速度
- Force 为马达施加的力,该力会慢慢将马达旋转速度提升到指定速度。
- Free Spin 如果勾选,则旋转速度超过指定速度,也不会刹车,而是一直加速。
- Limits 启用时,会限制旋转角度
- Min 允许的最小旋转角度
- Max 允许的最大旋转角度
- Bounciness 当旋转到达最大最小角度时,会有多大的弹性将它弹回
- Contact Distance 接触距离,可以理解为误差范围。合理的误差范围防止物体发生抖动
- **Break Force, Break Torque, Enable Collision, Enable Preprocessing ** 这些参数含义同其它关节。
4.3 弹簧关节 SpingJoint
弹簧关节将两个物体连接在一起,又保持一定的距离,就像用弹簧连接它们。它会试图俩东锚点(Anchor)让2个锚点维持在指定距离。弹簧的拉力与两个物体离开稳定位置的幅度有关,还受到弹簧强度的影响。为防止弹簧无限反复伸缩,还需要设置阻尼,让弹簧逐渐稳定下来,更大的阻尼,会更快地稳定下来。
可以手动设置另一个物体上地锚点地位置,也可以勾选自动计算,Unity会以两个物体初始地相对位置,计算出弹簧的稳定长度。
最大最小距离,允许设置弹簧的稳定距离范围。例如可以指定1-2米,过远或过近都会让弹簧拉近或拉远两个物体。
参数
- Connected Body 连接到的另一个刚体
- Anchor 关节在自己身上的锚点
- Auto Configure Connected Anchor 自动计算连接物体的锚点
- Connected Anchor 连接到的物体的锚点(在连接到物体的局部坐标空间)
- Spring 弹簧强度
- Damper 回弹阻尼
- Min Distance 最小距离,2个物体距离小于该距离时,会推开。注意:默认最大最小距离为0,表示以2个物体的初始距离为准。
- Max Distance 最大距离,大于该距离则拉近两个物体。
- Tolerance 弹簧长度允许的误差,允许弹簧稳定时长度不同,可以防止抖动。
- **Break Force, Break Torque, Enable Collision, Enable Preprocessing ** 这些参数含义同其它关节。
4.4 角色关节 CharacterJoint
球窝关节主要用来实现布偶效果,或者说骨骼关节效果,是从球窝关节派生,并添加了在轴上的限制。
如果想要配置布娃娃系统,则可以直接参考Ragdoll Wizard
参数(仅介绍不同)
- Axis 旋转轴。可以理解为关节旋转轴
- Swing Axis 摆动轴。先旋转,在摆动,则可以事项任意方向的摆动。
- Twist Limit Spring 当旋转超出范围时的回弹力
- Low Twist Limit 旋转最小角度
- High Twist Liomit 旋转最大角度
- Swing Limit Sprint 摆动超出范围时的回弹力
- Swing 1 Limit 摆动限制1
- Swing 2 Limit 摆动限制2
角色关节像通用关节一样提供了各种可能的运动限制。
可在在旋转轴上(Axis)定义最大最小旋转角度来控制旋转(相对于初始的旋转),设置Low Twist Limit>Limit = -30,High Twist Limit>Limit = 60,则允许关节围绕 Axis 轴在-30到60度之间旋转。
Swing 1 Limit 定义在 Swing Axis 轴上摆动的限制,限制角度时对称的,比如设置成30,表示允许在-30到30度之间摆动。
Swing 2 Limit 也定义了摆动限制,其轴为 Axis 与 Sing Axis 轴的正交轴,限制角度也是对称的。
限制的参数含义都是相同的,所以下面一起说明:
- Bounciness 可以理解为自身运动到达限制后的弹性力。
- Spring 超出限制后,添加的回弹力的大小。挂接到的对象的移动,会导致添加该力,或者代码中调用Rigidbody.AddForce/Torque接口。
- Damper 回弹阻尼,Spring力的反作用力。
- Contact Distance 接触距离,产生力的距离误差,用来避免抖动。
该组件的参数组合比较复杂,一些理解是做实验得出的,实际应用中根据效果不断调整来获得我们期望的效果。
限制参数是相对与挂接到的物体计算的,所以不要直接旋转或拖动本体,否则模拟效果跟参数匹配不上。
4.5 ConfigurableJoint
ConfigurableJoint包含了关节的所有功能,理论上,上面的所有关节类型,都可以通过该类型配置得到。因为多数情况下,上面的扩展关节类型就够用了,该类型就不说了(太复杂,不常用,翻不动了 _ )。
5. 角色控制器 CharacterController
如果我们开发的是由玩家控制角色的游戏,则角色控制器是必须要用到的,该类控制器模拟了控制人形角色移动的大多数物理行为。
参数
- Slope Limit 限制角色能移动的斜坡的最大角度
- Step Offset 限制角色能爬上的台阶的最大高度。该高度不能高于角色自身高度,否则会报错
- Skin Width 表面厚度,指的是角色碰撞体和场景碰撞体之间允许穿透的厚度。较大的值可以减少抖动的发生,太小角色容易卡在场景某些角落或边无法移动。建议设置为半径的10%。
- Min Move Distance 最小移动距离。如果控制角色移动的距离小于该距离,则角色不会移动,也是为了减少抖动的发生。可以设置为0。
- Center 胶囊中心位置。文档中说是基于世界位置,个人觉得应该解释为角色局部坐标位置。
- Radius 胶囊体的半径
- Height 胶囊体的高度
角色控制器由动画,或者脚本控制移动,可以不符合物理定律。
控制器不会对外力产生反应,也不会对碰撞的物体施加力。如果需要在碰撞时将其它物体推开,则需要在OnControllerColliderHit 方法中对碰到的物体施加力。
5.1 参数设置建议
修改胶囊体,尽量与角色外形一致,并调整Center与角色的中心点尽量一致。
台阶高度,对2米的人来说,建议Step Offset 设置为0.1-0.4米
坡度限制不要太小,90度可以很好的工作,而且也不会爬墙:)。
5.2 角色卡住了?
皮肤厚度是重要的属性。如果角色被卡住了,最有可能的情况是皮肤厚度太小。适当的厚度让角色可以部分穿透其它物体,可以避免角色别卡住,以及抖动。
通常设置为半径的10%,大于0.01米(角色基准高度2米)
将最小移动距离设置为0。
根据实际运行效果不断调整参数,达到期望效果。
5.3 技巧
- 如果角色卡住,调整皮肤厚度
- 添加自定义脚本,让角色对其它物理对象产生影响
- 角色控制器不受外力影响
- 修改控制器参数时,引擎会重新生成控制器,所以之前的碰撞信息会丢失,而且在参数调整时发生的碰撞不会产生OnTriggerEnter事件,但是再移动一次就能碰撞了。
CharacterController编程参考。
6. ConstantForce 常量力组件
常量力组件可以方便地为物体添加一个持续地力,即该组件会为持续地为物体施加固定大小,方向地力。可以用在发射物上,比如火箭,可以表现出加速度效果。
要让火箭持续前进,可以再Relative Force 上加一个Z方向移动地力,然后设置刚体地阻尼参数来限制最大速度,记得去掉重力影响,使火箭运行再轨道上。
参数
- Force 力的大小及方向,世界坐标系
- Relative Force 局部坐标系的力大小及方向
- Torque 扭矩的大小及方向,世界坐标系。扭矩越大,旋转越快
- Relative Torque 局部坐标系下的扭矩。
技巧:
- 要让物体上浮,可以添加世界坐标系下Y轴上的常量力来对抗重力
- 要让物体前进,添加局部坐标系下Z轴上的常量力
7. WheelCollider 轮子组件
7.1 组件说明
WheelCollider 设计用于地面上滚动的轮子,最典型的用法就是各种载具的轮子,当然也可以用再其它情况下。
轮子组件内置了碰撞检测,轮子物理模拟,以及基于滑动摩擦力的驱动模型。
有专门的关于使用轮子的教程,参考Unity Wheel Collider tutorial。
参数
- Mass 车轮的质量
- Radius 车轮的半径
- Wheel Damping Rate 车轮的阻尼
- Suspension Distance 减震器的行程,即减震器的最大偏移距离,以局部坐标表示。避震器总是沿着轮子坐标系的Y轴方向运动
- Force App Point Distance 力施加到轮子上的位置,基于轮子静止时的位置,在减震行程方向上的偏移量。该值为0时,施加在轮子底部。通常在车辆重心略下方效果比较好。
- Center 轮子的中心位置,在局部坐标
- Suspension Spring 减震器设置,提供弹力(弹簧),阻尼(减震筒),来为车辆提供支撑力和稳定性
- Spring 弹力,弹力越大,车身回到初始位置的速度越快
- Damper 阻尼,使避震器运动更慢
- Target Position 弹簧中位位置(在车辆自重下弹簧的稳定点),取值0-1,1表示弹簧完全弹开,0表示完全压缩。通常0.5可以工作的很好。
- Forward Friction 前向摩擦力,阻尼曲线。这部分比较复杂,【车轮阻尼曲线】部分详细解释。
- Sideways Friction 侧向摩擦力,阻尼曲线
7.2 工作原理
车轮的碰撞检测,是从车轮中心位置,沿着局部坐标Y向下发射射线进行。车轮具有半径,可以在减震方向上向下位移,该模拟受参数 Suspension Distance 影响。在脚本中,通过引擎扭矩(motorTorque),刹车扭矩(breakTorque),转向角度(steerAngle)属性来控制。
车轮摩擦力,没有使用统一Unity物理模型引擎计算,而是使用专门的基于滑动摩擦力的物理模型,该模型可以实现真实的物理效果。但是因为使用的不是同一套物理模型,所以常规的物理材质不起作用。
7.3 WheelCollider 的设置
只要将 WheelCollider 创建到车辆上,即可以通过脚本驱动运动。但是物理并不会驱动轮子的3D模型旋转,它们之间相对是独立的,所以物理模型和显示模型可以分开创建:
注意,运行时,车轮的Gizmos绘制不会实时更新,所以运行中暂停,会发现物理模型和渲染模型位置不一致,这是正常的。
7.4 碰撞体问题
由于车轮运动速度非常快,所以跑道的碰撞设置非常重要。基本要求就是,跑道环境要尽量平滑和简单,不要跟显示模型一样由小的凹凸结构。碰撞模型是基于场景外形创建的,但是要简化,并作出必要的改动,以尽量平滑。此外,碰撞不能太薄,以避免发生穿透,比如护栏虽然看起来很薄,但是碰撞体要尽量加厚,可以在开发中进行测试,得到合适的结果。物理开发过程中,很多时候是基于经验来调整。
如图,相对于渲染场景,物理碰撞场景要简单的多。
7.5 车轮阻尼曲线
轮胎的阻尼可以用车轮阻尼曲线(Wheel Friction Curve)来表示。车轮的转动阻尼和侧滑阻尼是独立的。从物理学原理来说,滑动摩擦系数和静摩擦系数的值是不同的,但在实际模拟中,轮胎和路面的相对滑动速度和当时产生的摩擦力很复杂,可以用曲线来描述。
这个曲线描述了相对滑动和摩擦力的关系。很关键的是,将曲线分为3段。第一段是从原点到Extremum Slip/Value(Extremum极值),第二段是从Extremum Slip/Value 到 Asymptote Slip/Value(平滑阶段开始,Asymptote渐进线),第二段后,曲线会保持为平行于X轴不再变化:
对真正的轮胎,在轮胎和地面相对滑动较小时(转动速度较慢),橡胶材质会受到挤压并提供较大的摩擦力,抵消了滑动力,且速度越快,压力越大,则摩擦力也越大;之后随着滑动继续增大,到一定程度后轮胎开始打滑,随着滑动加剧,摩擦力开始下降;最终随着滑动力增大,摩擦力不再变化。
个人理解,Slip是轮胎的转动力,Force是地面摩擦力,转动力相对于地面摩擦力产生向前的动力。转速越快,相对摩擦力越大,前进动力越大,直到轮胎转动过快,转动力大于地面摩擦力,开始打滑。
参数
- Extremum Slip/Value 开始打滑的点,以及在该点的力的大小
- Asymptote Slip/Value 继续增大滑动力,摩擦力不再变化的,以及摩擦力
- Stiffness 摩擦力大小的因数,默认为1,修改该值整体调整摩擦力,游戏中可以通过修改该值来模拟不同的路面,比如柏油路和冰雪路面。
7.6 实践技巧
- 赛车游戏中,车辆速度会很快,要减少物理计算帧的间隔时间。在时间管理器中调节,默认是0.02秒,可以降低该值提高物理帧率,使模拟更稳定,但负载也更高。
- 为防止太容易翻车,可以在脚本中降低刚体的重心,还可以根据车辆速度施加向下的力,向赛车的尾翼,整流罩。
8. Cloth 布料模拟
Cloth 组件利用蒙皮网格,提供基于物理的布料模拟,尤其是用来实现角色的衣服。该组件只能使用Skined Mesh,如果未一个非Skined Mesh添加了布料组件,非Skined Mesh组件会被删除,并添加Skined Mesh。
8.1 组件
参数
- Stretching Stiffness 布料的弹性
- Bending Stiffness 布料的硬度,或柔软性
- Use Tethers 当布料搜到太大的拉伸力时,施加对布料粒子移动的约束力,避免过度拉扯。
- Use Gravity 布料受重力影响
- Damping 运动阻尼(空气阻力)
- External Acceleration 常量,应用到布料的外部加速度
- Random Acceleration 随机值,应用到布料的外部加速度
- World Velocity Scale 角色在世界空间的移动速度对布料影响的因数
- World Acceleration Scale 角色在世界空间的加速度对布料影响的因数
- Friction 与角色碰撞时的摩擦力,其实是与下面的Capsule Colliders和Sphere Colliders指定的碰撞的摩擦力
- Collision Mass Scale 碰撞粒子质量增加系数
- Use Continuouse Collision 开启连续碰撞检测
- Use Virtual Particles 为每个三角形添加碰撞粒子来提高碰撞稳定性
- Solver Frequency 每秒迭代次数,120已经很高了,可以适当调低点
- Sleep Threshold 布料休眠阈值
- Capsule Colliders 跟该布料可能发生碰撞的胶囊碰撞体数组
- Sphere Colliders 跟该布料可能发生碰撞的ClothSphereColliderPair数组,可以理解为他是按照一组来的, 一组中可以只有一个SphereCollider, 也可以有两个, 当有两个的时候, 那么这两个SphereCollider会在布料的碰撞系统中被”焊接”起来. 这样就允许通过两个大小不同的SphereCollider来组合成一个圆锥形状的碰撞体了
8.2 基本概念
布料不能跟场景中的所有碰撞体进行碰撞,也不会对场景中其它对象施加力,除非将碰撞体手动添加给布料,而且即使布料这些碰撞体发生碰撞,也不会有反馈的力,即布料只受力。
另外,布料只跟3中碰撞体检测碰撞,球,胶囊,锥形胶囊(有两个球体构成)。这些限制都是为了更快的模拟。
8.3 约束编辑工具
在布料编辑面板上部,点击 Edit cloth constraints 按钮,则进入约束编辑模式,在Scene场景中,将锁定该布料。布料上的顶点将会根据视图模式以彩色点的形式渲染出来,来表示其值的不同。可以以画刷的方式编辑约束。
8.3.1 工具界面功能
- Visualization 切换显示模式,即当前要显示的数据是 Max Distance 还是 Surface Penetration。下拉列表还提供了Manipulate Backfaces开关(某些角度,控制点不可见,选中该选项则始终可见)。
- Max Distance 布料顶点的在其位置上的最大位移
- Surface Penetration 布料粒子穿进布料的深度(程度)
- Brush Radius 画刷的半径
- Constraint Size 约束体的显示尺寸
8.3.2 有2种方式来编辑顶点的约束
- Select 点选或框选一组顶点,然后开启Max Distance 或(和)Surface Penetration,并设置值。
- Paint 画刷模式,通过点选一个或多个顶点(由画刷大小决定),直接赋予 Max Distance 或(和)Surface Penetration 面板上的当前的值。
图为paint工作模式
无论哪种模式,图中顶点的颜色会在编辑改变后马上更新。颜色映射规则,在Visualization下方的颜色条,从红到绿过度,红色是0,绿色是已经设置过的最大的值。黑色顶点表示未设置约束。
8.4 Self collision(粒子-网格) and intercollision(粒子-粒子)
布料的碰撞让角色衣物及其它织物的运动模拟更真实。Unity中,布料利用一系列的**粒子(碰撞粒子)**来处理碰撞。可以为一下两种目的设置布料粒子:
- Self-collision 防止布料自己穿过自己。
- Intercollision 允许布料的粒子之间互相碰撞。
在布料面板上方点击第二个按钮(Edit cloth self/inter-collision)进入编辑模式。(2019.3的图标换了,但还是第二个。)
Cloth Self Collision And Intercollision 在Scene视图显示的编辑窗口
初始,布料粒子未设置碰撞,显示为黑色:
为应用碰撞,需要选择一组粒子,点击 【Select】按钮
框选要应用碰撞的粒子:
选中的粒子将会编程蓝色:
勾选 Cloth Self Collision And Intercollision 来应用碰撞:
应用了碰撞的粒子,显示为绿色:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HbcvmlET-1580878256414)(https://docs.unity3d.com/uploads/Main/SelectedParticlesGreen.jpg)]
这时回到布料面板,上方会刷新出碰撞参数,填上非0值:
- Distance 粒子半径。Unity会确保在模拟时,这些粒子不会重叠到一起。Distance应该比配置中两个粒子的最小距离要小,否则粒子之间可能会穿插抖动。最小距离在ProjectSettings>Physics最下方定义。
- Stiffness 分离粒子的推动力。布料迭代使用该值计算力,以保证粒子分离开。
上面两个参数,在ProjectSettings>Physics最下方定义了全局参考值(需要勾选Cloth inter-collision)。
布料内自身碰撞相关运算量很大,可以通过降低碰撞距离(碰撞粒子尺寸)来降低碰撞发生的概率来提升性能。
Self Collision 是使用顶点而不是面进行碰撞检测,所以当网格三角形尺寸相对与布料厚度太大时,可能无法工作的非常完美。
【Paint】和【Erase】操作模式,允许通过点击的方式编辑碰撞开关属性。在该模式下,设置碰撞的是绿色的,未设置的是黑色的,刷子范围内的是蓝色的:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-rMP2k0gR-1580878256415)(https://docs.unity3d.com/uploads/Main/PaintBrushSelfCollision.jpg)]
8.5 Cloth intercollision
设置 intercollision 的方式与self-collision相同,设置需要碰撞的碰撞粒子。
要开启 intercollision,在PorjectSettings>Physics的底部,打开选项,并设置Distance和Stiffness为非0值:
这两个参数的功能,与布料组件中对应的参数功能一致。
8.6 碰撞体碰撞
布料不能跟任意场景中的碰撞体进行碰撞,只能与在Capsule Colliders和Sphere Colliders 数组中指定的碰撞体碰撞。对角色衣物模拟来说,是角色身体的碰撞体。
9. 布娃娃向导
Unity 有一个简单的向导来帮助我们快速地创建布娃娃系统。只需要在向导中分别拖动肢体,然后选择创建,则Unity会自动生成Collider,Rigigbody,Joint来构建布娃娃。
布娃娃需要创建在蒙皮网格上(骨骼动画模型)。
我们不可能修改源文件来添加布娃娃功能,而是创建角色预制,并为该预制添加布娃娃系统。
将模型资源从Project拖动到场景的Hierarchy视图中,选中该模型
创建布娃娃,GameObject>3D Object>Ragdoll…,打开布娃娃创建向导:
将模型的骨骼分别拖到布娃娃向导的对应骨骼节点上,点击【Create】,创建布娃娃,然后在Hierarchy视图选择相关骨骼节点,会发现已经自动帮我们创建了BoxCollider,Rightbody,CharacterJoint组件。
最后将这个模型创建成预制,就可以了。
在代码中,
- 获取这些骨骼的Rigidbody和Collider,将Rigidbody.isKinematci=true,Collider.isTrigger=true;
- 切布娃娃时,
- 将Rigidbody.isKinematic=false,Collider.isTrigger=false
- 禁用Animator组件。
常见问题
- 使用Ragdoll去做死亡的效果时,可能会偶然发现,蒙皮会被无限拉伸撕裂的情况,解决办法是在勾选关节的【Enable Projection】选项。
- 第一帧蒙皮撕裂
- 检查pelvis和middle spine是否混淆,root节点设置错误(root节点是有刚体,有碰撞器没有Character Joint)
- 检查两个部位的collider是否产生重叠
- 布娃娃系统未生效,检查Animator组件是否已经禁用。
10. 物理系统调试
Unity提供了可视化的调试方法,检查场景中的碰撞体,还可以检查场景中和物理相关的性能指标。可以展示出场景中的哪些物体可以和哪些物体碰撞,不能和哪些物体碰撞。当场景中碰撞体很多,或场景的渲染效果与物理状态不一致时(比如空气墙)是十分有用的。
当需要优化物理性能时,可以打开Physcis Profiler。
Window>Physics Debug 可以打开物理调试窗口。
在该窗口中,可以自定义可视化选项,为游戏中的物体定义不同的类型,显示或隐藏不同类型的物体。
使用思路:
- 不同类型的碰撞体用不同的颜色表示,比如刚体,Kinematic刚体,触发器,静态碰撞体等。
- 可以隐藏或显示具有某些属性的碰撞体
- 休眠的刚体或者非刚体这类静止的物体和未停下来的刚体之间用明显的显示效果区分。可以判断刚体休眠状态。
11. Continuous Collision Detection
CCD连续碰撞检测,用来避免高速移动的物体穿过其它物体。Unity提供了2种CCD实现:
- Sweep-based CCD Inspector中设置Rigidbody.Collistion Dectection = Continuous 或 Continuous Dynamic
- Speculative CCD Inspector中设置Rigidbody.Collision Detection = Continuous Speculative
11.1 Sweep-based CCD
Sweep-based CCD 使用 Time Of Impact (TOI)算法,在根据物体当前运动方向速度定义的前进轨迹上扫过,计算检测可能的碰撞。如果发生碰撞,算法会计算碰撞时间,并将物体移动到该时间对应的位置。算法通过在扫描体碰撞发生后,在该时间上,计算一个新的速度(更慢)分段扫描,直到发生碰撞,然后再次在该时间范围内执行分段扫描,确定碰撞点。这个过程会消耗大量CPU时间。
然而,因为算法时基于线性的扫描体,所以会忽略物体的旋转,因此当物体旋转时,可能还是会发生穿过的情况。例如下面的例子,乒乓球板因为是旋转的,所以Sweep-based CCD 并没有生效,依然穿过了乒乓球:
另一个问题就是效率问题了。如果由大量的高速运动物体,则会执行大量的扫描迭代,CPU负载会很高。所以尽量少用或用替代方案,比如子弹,直接用射线进行检测,从子弹的上帧位置到该帧位置的射线,执行射线碰撞,并根据碰撞点进行处理。
11.2 Speculative CCD
Speculative CCD,会根据运动方向速度,创建包围运动轨迹的AABB。算法是预测的,是因为与该AABB发生的碰撞,并不见得真的与物体碰撞,还要根据一些限制条件,进行筛选,条件全部满足的,才判定真正碰撞。
下图展示了没有障碍时,小球从t0移动到t1位置。通过放大的AABB,预测算法检测到2个碰撞,碰撞法线分别为n1和n2,然后算法处理这两个可能的碰撞,来避免穿越。
基于当前的运动方向和速度创建的AABB在运动轨迹上检测可能的碰撞,来避免穿越。
该算法比Sweep-based CCD要廉价,因为仅在碰撞接触的检测阶段执行,而不是碰撞处理模拟阶段。而且,因为考虑了旋转和位移,检测到Sweep-based CCD 失败的检测。
(研究DOTS的物理系统时,该新的物理封装更加底层,将碰撞分成了BroadPhase,NarrowPhase等几个阶段,从粗略检测是否碰撞,到计算碰撞点信息的详细运算)。
然而,预测碰撞会发生幽灵碰撞,导致不应该存在的碰撞效果。因为该算法基于最近点收集所有可能的接触,因此接触点法线不精确,这回导致高速运动的物体,在由多个碰撞体镶嵌起来的平面上发生不应该有的跳跃。例如下面的图,球水平向右移动,应该可以移动到t1位置。扩展后的AABB与2个碰撞体b0,b1发生了碰撞,根据最近点算法(从可能碰撞的顶点中选择最近的点生成碰撞法线),算法生成了2个接触点,c0,c1。根据c0点的法线,系统推测在该处有一个斜坡。
该推测的斜坡,在插值模拟时,会导致小球跳起。
预测CCD也可能发生穿越,因为该检测只在碰撞检测阶段执行。在处理接触时(检测到碰撞接触后的进一步处理),如果在该阶段物体受到了很大的力,并进行模拟处理,则物体实际上会通过插值运动到扩展的AABB之外,如果AABB外有其它物体,则会穿越。
例如,下图的小球从t0点向左运动时,小棍沿着顺时针旋转,如果小球因为撞击受到了更大的力,则最终的运动可能会超出t0-t1的AABB,到达t1’位置,穿越了蓝色的墙。这时因为接触检测仅在碰撞检测阶段,在模拟插值阶段不执行。