作者: 吴亚峰 , 杜化美 , 张月霞 , 索依娜 责编: 张涛

6.1 刚体

Unity 4 3D开发实战详解

6.1.1 刚体特性

在Unity内建物理引擎中,首先要介绍的是刚体(Rigidbody)的概念。包含有该类组件的游戏对象,会遵循万有引力定律,在重力的作用下,使物体垂直下落。刚体组件还会影响物体发生碰撞时的反应,使物体遵循惯性定律,并在其他物体运动冲击作用下产生速度或者形变。

刚体作为物理引擎中的最基本组件,保证了所有物体受到重力的约束。Unity开发平台中,对刚体设置了很多属性和变量,并对应封装了多个相关方法,下面进行分别介绍。

1.刚体属性
为了利于开发者控制物理系统,Unity提供了多个属性接口。开发者可以通过更改这些参数来控制物体的物理状态。实际开发中,这些参数都被详细地罗列在属性查看器中,开发者很容易就可以对其进行更改。接下来对这些属性进行介绍。

(1)质量(Mass)

该属性表示刚体的质量,其数据类型是float,默认值为1。其在属性查看器中的位置如图6-1所示。该属性的大小是有严格要求的,一般来说,大部分物体的质量属性值接近0.1才符合日常生活的感官感受,一旦超过10.0,就会失去仿真所需达到的效果。

(2)平移阻力(Drag)

该属性是物体的平移阻力,其数据类型是float,初始值为0。Drag属性在属性查看器中的位置如图6-2所示。在现实生活中,物体会受到各方面的影响,速度会渐渐衰减,为了模拟这一效果,Unity设定了平移阻力属性。这一属性值越高,物体的速度衰减越严重。

(3)旋转阻力(Angular Drag)

该属性表示物体的旋转阻力,其数据类型是float,初始值为0.05。其在属性查看器中的位置如图6-3所示。当一个物体进行旋转的时候,其旋转的角速度也会受各方面影响衰减,为模拟这一现象,Unity设定了旋转阻力属性。此属性值越高,物体的角速度衰减越严重。

unity伪3d地铁 unity 3d 仿真_ViewUI

(4)使用重力(Use Gravity)

该属性表示的是该物体是否受到重力的影响,其数据类型是boolean,初始值为true。其在属性查看器中的位置如图6-4所示。这一属性被设为false时可以模拟物体在外太空状态下的失重状态。有时候也可以根据需要用于某些特殊的场合。

unity伪3d地铁 unity 3d 仿真_unity伪3d地铁_02

注意

当该值被设定为false的时候,物体将不受重力影响,但刚体的其他特性还是存在的。

(5)是否遵循运动学(Is Kinematic)

该属性表示本游戏对象是否遵循牛顿运动学物理定律,其数据类型是boolean,初始值为false。其在属性查看器中的位置如图6-5所示。如果该属性被置为true,本物体的运动状态将不受外力、碰撞和关节的影响,而只受到动画以及附加在物体上的脚本的影响。

另外,虽然当该属性被置为true时物体将不受物理定律的约束,但是该物体还是会影响其他的物体,改变其他物体的运动状态。在游戏开发中此属性会被经常用到,想象一下在第一人称射击游戏中,敌人被击杀后会倒地不动,因为这个敌人对象Rigidbody的Is Kinematic属性被设置为了true。

(6)插值(Interpolate)

该属性表示的是该物体运动的插值模式。其在属性查看器中的位置如图6-6所示。在默认状态下,插值Interpolate是被禁用的,可以选择Interpolate模式,在此模式下物理引擎会在物体的运动帧之间进行插值,使得运动更加自然。

unity伪3d地铁 unity 3d 仿真_javascript_03

不得不提的是,插值导致了物理模拟和渲染的不同步,进而产生物体轻微抖动的现象。建议在开发中,对主要角色使用插值,而其他的则禁用此功能,以达到折中效果。

(7)冻结旋转(Freeze Rotation)

该属性表示的是该物体的旋转是否受到物理定律的约束。其在属性查看器中的位置如图6-7所示。默认状态下任意轴的旋转是受物理定律控制的。该属性的值是修改在每个轴上的旋转属性来实现的,实际开发中可以灵活地使用。

在第一人称射击游戏中,通过此属性去除物理属性对旋转的控制,可以使玩家完全控制视角旋转。

(8)碰撞检测模式(Collision Detection)

该属性表示的是碰撞检测模式。其在属性查看器中的位置如图6-8所示。默认状态下,碰撞检测模式是Discrete 。在没有碰撞检测的情况下,碰撞物体会穿过对方,产生所谓的“穿透”现象。为避免此类问题的发生,碰撞检测工作是必须做的。

unity伪3d地铁 unity 3d 仿真_游戏_04

关于碰撞模式的选择,可选的有不连续模式(Discrete)、连续模式(Continuous)和动态连续模式(ContinuousDynamic),其中动态连续模式适用于高速运动的物体,但是耗费资源较大。连续模式仅仅可以用于球体、胶囊和盒子碰撞者的刚体,而且会严重影响物体的运动表现。所以大部分的物体采用不连续模式作为碰撞检测模式。

2.刚体变量
为了获取或者更改物体的运动状态,Unity还预留了多个变量接口,这些接口使得运动处理变得相当简单。接下来将具体地介绍一下这些变量。

(1)刚体的速度(velocity)

此变量表示物体的速度值,在Unity中单位1表示现实生活中的1米。在开发过程中,直接更改此变量的值并不是一种明智的做法,因为Unity中物体的运动经过非常复杂的计算使得物体的运动自然平滑,如果有外加干预,会迫使物体的运动模拟失真。

下面的代码可以实现一个物体的速度骤增,以实现瞬移效果,具体代码如下:

1 function FixedUpdate () { 
2   if (Input.GetButtonDown ("GO")) {     //按下按钮“GO”
3     rigidbody.velocity = Vector3(10,0,0);   //给予_x_方向速度
4  }}

(2)是否使用重力(useGravity)

此变量表示当前的物体是否受到重力的约束。在开发过程中,灵活地更改此变量可以达到一些特殊的场景要求,比如在外太空环境下的失重状态。

可以编写代码实现物体的失重状态,具体代码如下:

1 function Start (other : Collider) {     //脚本被激活
2   if (other.attachedRigidbody) {      //此物体包含有刚体组件
3     other.attachedRigidbody.useGravity = false;  //设置物体不受重力约束
4   }}

(3)冻结旋转(freezeRotation)

此变量表示当前的物体的旋转是否受到物理定律的约束。通过此变量,可以更改x、y、z三个方向中的某个方向的旋转约束,以达到开发者想要达到的旋转效果。

该变量使用的具体语法代码如下所示:

1 rigidbody.freezeRotation = true;

(4)刚体的位置(position)

此变量表示了刚体的位置,改变此变量时,引擎会在物理阶段结束后,将物体放置到指定的位置。

下面的脚本实现了将物体放置于原点的功能,具体代码如下:

1 function Start () {       //脚本被激活 
2   rigidbody.position = Vector3.zero;   //将物体放置在(0,0,0)位置
3 }

说明

这种做法不适用于旋转物体,可以使用MovePosition代替。

(5)刚体的旋转(rotation)

此变量表示了物体的旋转状态,改变此变量时,引擎会在物理阶段结束后,将物体旋转到指定的状态。

下面的脚本实现了设置物体无旋转,具体代码如下:

1 function Start () {        //脚本被激活

2   rigidbody.rotation = Quaternion.identity;//将物体的旋转状态设置为原始态(无旋转)
3 }

说明

这种做法不适用于旋转物体。可以使用MoveRotation方法来代替。

3.刚体常用方法
在讲解了刚体的属性与变量后,有必要讲解一下Unity提供的相关方法。

(1)给刚体施加力(AddForce)

此方法被调用时,将会施加给刚体一个瞬时力,然后物体在力的作用下,产生一个初速度,接着物体在初速度的作用下开始运动。

下面的代码实现了对物体施加竖直向上的力,具体代码如下:

1 function FixedUpdate () {
2   rigidbody.AddForce (Vector3.up * 10);    //施加竖直向上的力 
3 }

注意

AddForce方法尽量不要用在Update方法中,因为这样的方法在Update中会被不停地调用,与被调用一次不同,这种是模拟了一种加速度的效果。

(2)移动刚体位置(MovePosition)

此方法被调用时,会使刚体按照参数移动到某个位置。此方法经常用于FixedUpdate方法中。具体用法如下列代码所示:

1  private var speed : Vector3 = Vector3 (3, 0, 0);             //声明速度向量
2  function FixedUpdate () {
3  rigidbody.MovePosition(rigidbody.position + speed * Time.deltaTime);   //按照speed为速度平移刚体
4 }

(3)旋转刚体(MoveRotation)

此方法被调用时,会使刚体按照参数旋转到某个方位。此方法经常用于FixedUpdate方法中。具体用法如下列代码所示:

1 var eulerAngleVelocity : Vector3 = Vector3 (0, 100, 0);     //声明角速度
2 function FixedUpdate () {
3   var deltaRotation : Quaternion = Quaternion.Euler(eulerAngleVelocity * Time. deltaTime);
4   rigidbody.MoveRotation(rigidbody.rotation * deltaRotation);//以eulerAngleVelocity为角速度旋转
5 }

6.1.2 物理管理器

前面的内容简单介绍了刚体的属性、变量和常用方法。接下来这一部分将讲解一下物理管理器(Physics Manager)的相关内容。

Unity作为一个优秀的游戏开发平台,其出色的管理模式是令人称赞的。在Unity中,不仅可以对单个分组进行属性设置,也可以对场景全局进行设置。这一部分讲解Unity中场景的全局物理参数是如何设置的。

1.物理管理器预览
(1)打开Unity开发平台,在菜单栏中选择“Edit”选项,会弹出编辑列表,如图6-9所示。在编辑列表中,选择“Project Settings”选项,弹出全局设置列表,如图6-10所示。在全局设置列表中选择“Physics”,即可进入物理管理器。

unity伪3d地铁 unity 3d 仿真_ViewUI_05

unity伪3d地铁 unity 3d 仿真_ViewUI_06

(2)按照第(1)步进行操作以后,会在属性查看器中呈现物理管理器编辑器,如图6-11所示。

unity伪3d地铁 unity 3d 仿真_游戏_07

2.物理管理器参数
在前面的内容中,讲解了物理管理器的打开操作。接下来介绍的是物理管理器中的相关参数的含义和用法。

(1)重力(Gravity)。

该属性表示的是项目中的重力加速度,该参数将被应用于所有刚体。其在物理管理器中的位置如图6-12所示,该属性3个数值分别指定在x、y、z方向上的重力加速度,一般重力是竖直向下的,所以只有y轴上有一个负值,大小默认为单位是-9.81米/(秒^2)。

(2)默认材质(Default Material)。

该属性表示当物体没有被指定物理材质的时候,该物体的默认材质。其在物理管理器中的位置,如图6-13所示。默认状态下是没有指定值的,因为在Unity中,每个物体的物理材质可能会有很大的不同,有时候指定默认材质并不是有太大的用途。

unity伪3d地铁 unity 3d 仿真_游戏_08

(3)反弹阈值(Bounce Threshold)。

该属性表示的是项目中的反弹阈值,该参数将被应用于所有刚体。其在物理管理器中的位置如图6-14所示,如果两个碰撞体的相对速度低于该值,那么将不会反弹。该值也用于减少抖动,所以不建议设置为很低的值。

(4)休眠速度(Sleep Velocity)。

该属性表示的是项目中刚体自动进入休眠时状态的速度值。其在物理管理器中的位置如图6-15所示。该值是休眠的默认线速度,低于该速度的物体将会进入休眠。如果没有这种机制,项目中的所有物体将永远都在计算中,对于资源的回收利用很不利。

unity伪3d地铁 unity 3d 仿真_unity伪3d地铁_09

(5)休眠角速度(Sleep Angular Velocity)。

该属性表示的是项目中的休眠角速度,该参数将被应用于所有刚体。其在物理管理器中的位置如图6-16所示,如果角速度低于该值,物体将进入休眠状态。

(6)最大角速度(Max Angular Velocity)。

该属性表示的是项目中刚体最大的角速度值。其在物理管理器中的位置如图6-17所示,设定最大值可以避免角速度数值不稳定现象的发生。在脚本中可以用语法Rigidbody.maxAngular Velocity来更改此值。

unity伪3d地铁 unity 3d 仿真_ViewUI_10

(7)最小穿透禁区(Min Penetration For Penalty)。

该属性表示的是项目中的最小穿透禁区,该参数将被应用于所有刚体。其在物理管理器中的位置如图6-18所示,该属性的具体含义是在碰撞检测器将两个物体分开前,可以穿透多少米的距离。较高的值会导致较多的物体穿透,不过可以减少抖动。

(8)求解迭代次数(Solver Iteration Count)。

该属性表示的是项目中关节和连接计算过程中的迭代次数。其在物理管理器中的位置如图6-19所示,决定了关节和连接的计算精度。求解迭代次数越高精度越高,但是次数过高会浪费过多的资源,不利于项目的正常运行,一般来说此值设置为7就可以适用于大多数情况。

(9)射线检测命中触发器(Raycasts Hit Triggers)。

该属性表示的是项目中的射线检测命中触发器,该参数将被应用于所有刚体。其在物理管理器中的位置如图6-20所示。如果启用,那么进行射线检测时命中碰撞体会返回一个命中消息,如果关闭,则不返回命中消息。

(10)层碰撞矩阵(Layer Collision Matrix)。

该属性表示的是项目中的层碰撞矩阵,该参数将被应用于所有刚体。其在物理管理器中的位置如图6-21所示。定义层碰撞检测系统的行为。

unity伪3d地铁 unity 3d 仿真_ViewUI_11


unity伪3d地铁 unity 3d 仿真_游戏_12

说明

层碰撞矩阵在后面的介绍中会有比较详细的解释,此处不再赘述。

6.1.3 碰撞与休眠

1.碰撞的使用
与碰撞相关的内容包括碰撞器(Collision)类、碰撞检测模式选择和最小穿透禁区设置,后两个内容已经在前面的内容中进行了详细的介绍,这里不再赘述。下面重点讲解一下碰撞器类及其相关方法的使用。

(1)碰撞器类的变量如表6-1所示,碰撞器有很多变量供碰撞处理使用,其中除了碰撞接触点(contacts)变量,其他变量都是只读的,读者请务必注意。

unity伪3d地铁 unity 3d 仿真_javascript_13

(2)碰撞器相关方法。

碰撞触发(OnCollisionEnter)
此方法在碰撞发生时被系统自动调用。该方法会传递一个Collision类,该类包含了碰撞点和相对速度等信息。开发者可以利用这些信息对物体进行碰撞处理,下面的代码就是一个简单的例子:

1 var explosionPrefab : Transform;
2 function OnCollisionEnter(collision : Collision) {    //碰撞开始
3 var contact : ContactPoint = collision.contacts[0];   //获取0号碰撞点
4 var rot : Quaternion =
5 Quaternion.FromToRotation(Vector3.up, contact.normal); //旋转以使_y_轴面向接触点法向量
6   Instantiate(explosionPrefab, pos, rot);         //实例化爆炸对象
7   Destroy (gameObject);                     //销毁游戏对象
8 }

说明

该方法只有在两个物体都遵循物理定律的情况下发生,即两者的Is kinematic属性都为false。

  • 碰撞状态(OnCollisionStay)

此方法在碰撞时两物体接触过程中被调用。该方法同样会传递一个Collision类,该类包含了碰撞点和相对速度等信息,下面的代码就是一个简单的例子:

1 function OnCollisionStay(collisionInfo : Collision) {  //碰撞进行中
2  for (var contact : ContactPoint in collisionInfo.contacts) {
3 Debug.DrawRay(contact.point, contact.normal * 10, Color.white); //绘制所有的接触点和其法向量
4  }}
  • 退出碰撞(OnCollisionExit)

此方法在碰撞时停止触碰刚体或者碰撞的时候调用。该方法同样会传递一个Collision类,该类包含了碰撞点和相对速度等信息,下面的代码就是一个简单的例子:

**1 function OnCollisionExit(collisionInfo : Collision) { //碰撞结束
2   print("不再和" + collisionInfo.transform.name+"发生碰撞。"); //打印**碰撞结束信息
3 }
2.睡眠的使用
(1)睡眠(Sleep)。

该方法被调用时,会强制一个刚体进入睡眠状态至少一帧的时间。一般用在Awake方法中,设置脚本开始执行时刚体睡眠。刚体进入睡眠后,物体相当于没有刚体存在,其具体的语法如下:

1 rigidbody.Sleep();JavaScript

(2)强制唤醒(WakeUp)。

该方法被调用时,会强制使一个处于睡眠状态刚体被唤醒,其具体的语法如下:

1 rigidbody.WakeUp();