Unity3D网络游戏实战
上QQ阅读APP看书,第一时间看更新

2.5 车辆行驶

到现在为止,已编写了两套坦克控制模式,然而,此时的坦克还不能紧贴起伏的地面,只要地面不平坦,坦克就会穿插在山体里或悬浮在半空中(如图2-41所示);其次坦克转弯不太自然,坦克的旋转半径一般不为0,不会原地旋转。 Unity3D内置的一种碰撞器WheelCollider,可实现对车辆的操控。

图2-41 坦克穿插在山体里

2.5.1 Unity3D的物理系统

在实现车辆控制之前,有必要先了解Unity3D的物理系统。物理系统中最常用的组件是Rigidbody和Collider。

Rigidbody(刚体)使物体能在物理规律下运动,它是物体系统的基础组件。可以从力和碰撞两方面来理解物理系统。

1.力(重力)

例如让一个带有Collider和Rigidbody的物体从高空落下,因为受到力的作用,在物体碰到地面后会倒下。读者可以新建一个工程试一试物理系统,在场景中添加一个圆柱体和立方体(它们默认附带有Collider组件),然后给圆柱体添加Rigidbody组件(Component→PhysicsRigidbody),再让圆柱体倾斜一定的角度(为了更明显地看出效果),如图2-42所示。

图2-42 将带有Rigidbody组件和一定初始角度的圆柱体放到场景中

运行游戏,可以看到圆柱体受到重力的影响掉到地面上,然后倒下,如图2-43所示。

图2-43 圆柱体受到重力影响掉到地面上,然后倒下

2.力(附加力)

通过程序给物体施加力,即可改变物体的运动轨迹。例如编写如下的代码,把它附加到上述例子的圆柱体上。如果按下空格键,则是通过Rigidbody的AddForce方法给物体施加一个力(这里施加的是一个方向向上,大小为50N的力)。

运行游戏后按下空格键,由于物体受到附加力和重力的共同影响(如图2-44所示),如果附加力大于重力(长按空格键),则物体将会向上飞起。

图2-44 物体受到附加力和重力的共同影响

3.碰撞

当“带有Rigidbody和Collider的游戏对象”碰撞到场景中“带有Collider的游戏对象”时,OnCollisionEnter()方法将被调用,后面会利用这一特性判断炮弹是否击中。相关的方法有6个,具体见表2-3。

表2-3 碰撞器的相关方法

例如,在上面圆柱体掉落例子的TestForce类中,添加如下的语句。

    //当碰撞到物体
    void OnCollisionEnter(Collision collisionInfo)
    {

        Debug.Log("碰撞到 " + collisionInfo.gameObject.name);
    }

运行游戏,在圆柱体碰到地面的时候,OnCollisionEnter方法会被调用,如图2-45所示。

图2-45 碰撞检测

坦克行驶符合物理规律,可以使用Unity3D内置的车轮碰撞器来实现。

2.5.2 车轮碰撞器

WheelCollider(车轮碰撞器)是一种特殊的地面车辆碰撞器,它具有内置的碰撞检测、车轮物理引擎和一个基于滑移的轮胎摩擦模型。WheelCollider是专门为有轮子的车辆所做的设计,也适用于坦克。

在添加轮子碰撞器之前,先给坦克车身添加碰撞器。由于Rigidbody是物理系统的基础组件,因此需要添加该组件后碰撞器才会生效。先给坦克模型添加Rigidbody组件,如图2-46所示。由于坦克一般都比较重,因此需要在Rigidbody中调整坦克的质量(Mass属性,这里调整为300)。

图2-46 给坦克模型添加Rigidbody组件

暂且把坦克当成一辆拥有四个轮子的汽车。在坦克模型下建立名为PhysicalBody的空物体(Empty Object),用于存放坦克的碰撞器组件。再在PhysicalBody里添加wheelL1、wheelR1、wheelL2和wheelR2这4个空物体,4个wheel将代表车辆的4个轮子。在PhysicalBody里添加两个名为collider的空物体,用于给车身添加碰撞器(BoxCollider)。此时坦克模型的层次结构如图2-47所示。

图2-47 Tank的层次结构

给两个collider添加BoxCollider组件(Component→Physics→Box Collider),然后点击Box Collider属性面板的调整碰撞体的大小和位置(点击后,碰撞体会出现控制点,拖动它们即可调整),调整后的碰撞体如图2-48所示。添加车身碰撞体可以让程序检测坦克是否碰撞了障碍物,或者是否被子弹打中(将在第3章中实现)。

图2-48 车身碰撞体

接下来给坦克添加车轮碰撞体,如图2-49所示。给4个wheel分别添加Wheel Collider(Component→ Physics→WheelCollider),调整它们的位置和大小(通过Wheel-Collider组件的Radius属性调整大小),使wheelL1代表前方左轮,wheelR1代表前方右轮,另外两个轮子代表后方的轮子。轮子位置和角度的细微差别对物理性能的影响很大,切记,看着Transform的数值进行调整,使它们对称。

图2-49 坦克的碰撞体

WheelCollider是用Unity3D制作汽车类型游戏的关键所在,它不仅可以模拟轮子的碰撞过程,还模拟了汽车的悬挂系统、引擎系统、轮胎摩擦等汽车的关键物理特性。WheelCollider的一些属性如图2-50和表2-4所示。

图2-50 WheelCollider的属性面板

表2-4 WheelCollider的属性及说明

车轮是由motorTorque、brakeTorque和steerAngle属性控制的,它们的含义见表2-5。

表2-5 车轮控制属性

一般来说,重心、悬挂系统和轮胎(摩擦力属性)对汽车的性能有着很大的影响。

❑重心:大家都知道重心低的车辆不容易翻倒,所有被应用到汽车刚体上的力都会作用到质心,默认情况下,Unity3D会将所有附加到刚体上的碰撞器(不管是该游戏对象还是它的子对象)的重心设为刚体重心。由于车辆的重心通常不是车的中心位置,若要得到更真实的效果,可以通过代码设置Rigidbody.centerOfMass来调整重心位置。

❑悬挂系统:另一个可以影响汽车行为的因素是悬挂系统(如图2-51所示)。汽车的悬挂系统可以增强轮胎和路面的摩擦,设想一辆没有安装悬挂系统的汽车行驶在颠簸的路面时,车轮肯定会时不时被抬起,轮胎的摩擦便无从谈起。

图2-51 汽车的悬挂系统

与悬挂系统的相关的参数如表2-6所示。

表2-6 与悬挂系统相关的参数及说明

❑轮胎摩擦力:Forward Friction是前后方向的摩擦力属性,影响motorTorque和brakeTorque的效果。Sideways Friction是左右方向的摩擦力属性,影响steerAngle的效果。车轮摩擦力受滑动摩擦力和滚动摩擦力的综合影响。图2-52是车轮摩擦力曲线图,假设要拉动车辆,一开始车轮静止,需要较大的力去克服惯性(从原点到Extremum),接着只需要一个与摩擦力相同的拉力便可以使车辆做匀速运动(Asymptote),这样就是图2-52所展示的曲线。

图2-52 车轮摩擦力曲线图

Forward Friction和Sideways Friction中每一项的5个参数的含义见表2-7。

表2-7 摩擦力相关的参数及说明

给坦克添加碰撞体、了解车轮碰撞器的属性后,接下来便要控制坦克的行进了。

2.5.3 控制车辆

汽车的前轮和后轮分别悬挂在两条轴上,每条轴上两个轮子的步调是一致的。新建名为AxleInfo.cs的文件,添加代表车轴信息的AxleInfo类,代码如下所示。

在上述代码中,leftWheel和rightWheel代表在某一条轴上的两个车轮碰撞器,例如左前轮和右前轮就是同一轴上的leftWheel和rightWheel。Motor指明是否将发动机的马力传送给轴上的轮子,就像汽车有前驱、后驱和四驱一样,对于前驱的汽车,马力传送给前轮而不传送给后轮。Steering指明轮子是否转向,汽车都是前轮转向,因此前轴的steering为true,后轴的steering为false。

接着在Tank类中编写控制车轴的方法(之前编写的两种操作模式已然无用,可以删去)。定义如图2-53下面的代码所示的变量,其中axleInfos是上面定义的AxleInfo类型的列表,代表坦克中的各个车轴,普通汽车只有前轮和后轮两个车轴,axleInfos的长度应该设置为2。其他机构变量分别代表马力、制动(刹车)和转向角。

图2-53 列表

axleInfos为List(列表)类型,List类型“System.Collections.Generic”命名空间中,需要在Tank类代码的最前面添加using语句“using System.Collections.Generic; ”。List<AxleInfo>即定义了AxleInfo类型的列表,也可以简单地把它理解为一个数组(如图2-53所示)。列表对象的常用方法有:Add (添加项)、Clear(清空列表)、Remove(删除元素)、RemoveAt(删除指定索引的元素),并可以通过“[索引]”获取指定索引的内容(如axleInfos[0])。

代码如下所示。

    //轮轴
    public List<AxleInfo> axleInfos;
    //马力/最大马力
    private float motor = 0;
    public float maxMotorTorque;
    //制动/最大制动
    private float brakeTorque = 0;
    public float maxBrakeTorque = 100;
    //转向角/最大转向角
    private float steering = 0;
    public float maxSteeringAngle;

接着在Tank类中定义PlayerCtrl方法,用来处理用户的输入。当玩家按键盘的“上”或“下”键时,motor等于maxMotorTorque或它的负值;按“左”或“右”键时, steering等于maxSteeringAngle或它的负值。若玩家使用摇杆或方向盘等设备,motor会在-maxMotorTorque到maxMotorTorque之间取值,steering也会在-maxSteeringAngle到maxSteeringAngle之间取值,代码如下所示。

然后在Update()中调用PlayerCtrl,把玩家操作的语句放到PlayerCtrl中而不是直接放到Update中,是因为后面还要实现电脑控制的功能,这样处理后,后续只需要添加类似PlayerCtrl的方法,而不必修改代码结构。在Update方法中通过foreach遍历每个车轴,依照车轴的steering和motor属性,给轮子施加转向、马力和制动,代码如下所示。

至此,我们已经编写完控制坦克的代码,最后只需要再做一些设置,便能让坦克行动起来。在属性面板中将坦克模型的AxleInfos的Size设置为2(前后两条车轴),并将4个WheelCollider拖入其中,设置为后驱(如图2-54所示)。

图2-54 Tank的属性设置

选择坦克的4个WheelCollider,调整参数(具体参数的含义请参照2.5.2节),以获得更好的驾驶体验,如图2-55所示。

图2-55 WheelCollider的属性

这时再运行游戏,便可以控制坦克行进了,此时履带会紧贴起伏的地表,如图2-56所示。如果容易翻倒,可以调整Rigidbody的质量、坦克重心和马力等数值,以达到良好的驾驶体验。因为WheelCollider的详细参数涉及不少物理知识,这里不再展开,读者可以查阅更多资料,以便更好地调整数值。

图2-56 坦克行驶在陡坡上

如果读者觉得把坦克当成四轮汽车,还不能很真实地模拟履带行驶,可以尝试添加更多的轮子,以求真实模拟(如图2-57所示)。

图2-57 添加更多轮子的坦克

2.5.4 制动(刹车)

本小节将处理坦克的刹车功能,玩家按下后退键,又分为以下两种情况。

1)坦克向前行进时,应当刹车。

2)坦克静止时,应当后退。

第一个问题便是如何判断坦克是在向前行进还是静止,车轮碰撞器有一个rpm(转速)属性,可以通过它判断坦克的状态。如果转速大于某个值(这里取5),可以粗略地视为坦克在前进;如果小于某个值(这里取-5),可以视为坦克在后退;如果转速接近0,则视为静止。

修改PlayerCtrl,通过轮子的rpm属性(转速)判断坦克的运行状态。如果坦克在移动,则设置brakeTorque的值,使坦克刹车,代码如下所示。

    public void PlayerCtrl()
    {
        //马力和转向角
        motor = maxMotorTorque * Input.GetAxis("Vertical");
        steering = maxSteeringAngle * Input.GetAxis("Horizontal");
       //制动
        brakeTorque=0;
        foreach(Axlelnfo axlenlnfo in axlenfos)
        {
            if (axleInfo.leftWheel.rpm > 5 && motor < 0)  //前进时,按下"下"键
                brakeTorque = maxBrakeTorque;
            else if (axleInfo.leftWheel.rpm < -5 && motor > 0) //后退时,按下"上"键
                brakeTorque = maxBrakeTorque;
            continue;
        }
        //炮塔炮管角度
        turretRotTarget = Camera.main.transform.eulerAngles.y;
        turretRollTarget = Camera.main.transform.eulerAngles.x;
    }

运行游戏,即可体验到刹车的效果。