Unity3d 官方资源Car的主控脚本CarController翻译与详解

一.综述

在Unity3D官方资源中Standard Assets –>Vehicles–>Car是赛车的相关资源,包括赛车模型、赛车控制脚本等。虽然用起来很方便,但是由于对有些脚本理解不彻底,就用不好。尤其是主要控制脚本CarController,我此次就对这个脚本进行了全面解析

二.CarController

1.主要函数流程图

整个CarController脚本主要是通过共有函数Move对赛车进行控制
CarController


2.代码类型定义部分

    //汽车驱动类型
    internal enum CarDriveType
    {
        //四驱
        FrontWheelDrive,
        //后驱
        RearWheelDrive,
        //前驱
        FourWheelDrive
    }
    //速度类型
    internal enum SpeedType
    {
        //英里每小时
        MPH,
        //千米每小时
        KPH
    }

3.代码定义部分

        [SerializeField] private CarDriveType m_CarDriveType = CarDriveType.FourWheelDrive;
        [SerializeField] private WheelCollider[] m_WheelColliders = new WheelCollider[4];
        [SerializeField] private GameObject[] m_WheelMeshes = new GameObject[4];
        [SerializeField] private WheelEffects[] m_WheelEffects = new WheelEffects[4];
        //重心位置
        [SerializeField] private Vector3 m_CentreOfMassOffset;
        //最大可转角度
        [SerializeField] private float m_MaximumSteerAngle;
        [Range(0, 1)] [SerializeField] private float m_SteerHelper; // 0 is raw physics , 1 the car will grip in the direction it is facing
        [Range(0, 1)] [SerializeField] private float m_TractionControl; // 0 is no traction control, 1 is full interference
        //所有轮胎的扭矩
        [SerializeField] private float m_FullTorqueOverAllWheels;
        //反向扭矩
        [SerializeField] private float m_ReverseTorque;
        //最大刹车扭矩
        [SerializeField] private float m_MaxHandbrakeTorque;
        //最大下压力
        [SerializeField] private float m_Downforce = 100f;
        //速度单位
        [SerializeField] private SpeedType m_SpeedType;
        //最高速度
        [SerializeField] private float m_Topspeed = 200;
        //档位总数
        [SerializeField] private static int NoOfGears = 5;
        //
        [SerializeField] private float m_RevRangeBoundary = 1f;
        //最大滑动距离
        [SerializeField] private float m_SlipLimit;
        //刹车扭矩
        [SerializeField] private float m_BrakeTorque;

4.函数Move

//外部调用的汽车移动控制函数
        public void Move(float steering, float accel, float footbrake, float handbrake)
        {
            Debug.Log ("***************************: " + footbrake + " " + handbrake);
            //保持当前的轮胎网格跟随WheelCollider转动
            for (int i = 0; i < 4; i++)
            {
                Quaternion quat;
                Vector3 position;
                m_WheelColliders[i].GetWorldPose(out position, out quat);
                m_WheelMeshes[i].transform.position = position;
                m_WheelMeshes[i].transform.rotation = quat;
            }
            //clamp input values
            //限定输入值范围
            steering = Mathf.Clamp(steering, -1, 1);
            AccelInput = accel = Mathf.Clamp(accel, 0, 1);
            BrakeInput = footbrake = -1*Mathf.Clamp(footbrake, -1, 0);
            handbrake = Mathf.Clamp(handbrake, 0, 1);

            //Set the steer on the front wheels.
            //设置前轮转角
            //Assuming that wheels 0 and 1 are the front wheels.
            //wheels下标为0、1的就是前轮
            m_SteerAngle = steering*m_MaximumSteerAngle;
            m_WheelColliders[0].steerAngle = m_SteerAngle;
            m_WheelColliders[1].steerAngle = m_SteerAngle;
            //调用角度辅助助手,
            SteerHelper();
            //设置加速/刹车信息到WheelCollider
            ApplyDrive(accel, footbrake);
            //检查速度范围
            CapSpeed();

            //Set the handbrake.
            //设置手刹
            //Assuming that wheels 2 and 3 are the rear wheels.
            //Wheel下标是2、3就是后轮
            if (handbrake > 0f)
            {
                //设置手刹值到后轮,达到减速目的
                var hbTorque = handbrake*m_MaxHandbrakeTorque;
                m_WheelColliders[2].brakeTorque = hbTorque;
                m_WheelColliders[3].brakeTorque = hbTorque;
            }

            //计算转速,用来供外部调用转速属性Revs来播放引擎声音等
            CalculateRevs();
            //改变档位
            GearChanging();
            //施加下压力
            AddDownForce();
            //检查轮胎
            CheckForWheelSpin();
            //牵引力控制系统
            TractionControl();
        }

5.函数SteerHelper,重点属性:m_SteerHelper

        private void SteerHelper()
        {
            for (int i = 0; i < 4; i++)
            {
                WheelHit wheelhit;
                m_WheelColliders[i].GetGroundHit(out wheelhit);
                if (wheelhit.normal == Vector3.zero)
                    return; // wheels arent on the ground so dont realign the rigidbody velocity
                //假如轮子离地,就不用调整汽车角度了
            }

            // this if is needed to avoid gimbal lock problems that will make the car suddenly shift direction
            //这个是为了避免万向锁问题的,万向锁问题会导致汽车突然变换方向(我知道万向锁问题,但不理解下面是怎么避免问题的,我只知道四元数的使用就是为了避免万向锁问题)
            //下面这个If函数的效果就是:假如上一次车体Y方向角度比这次小于十度,就根据相差的度数乘以系数m_SteerHelper,得出需要旋转的度数
            //根据这个度数算出四元数,然后将刚体速度直接旋转这个偏移度数,
            //根据代码开头m_SteerHelper的定义,这个做法相当于做了一个角度辅助,不完全凭借WheelCollider物理效果
            //而直接操控速度方向,对车角度进行调整。
            //现在来看,如果m_SteerHelper越小,则调整的角度越小,如果m_SteerHelper为0,则调整的角度为0,。
            if (Mathf.Abs(m_OldRotation - transform.eulerAngles.y) < 10f)
            {
                var turnadjust = (transform.eulerAngles.y - m_OldRotation) * m_SteerHelper;
                Quaternion velRotation = Quaternion.AngleAxis(turnadjust, Vector3.up);
                m_Rigidbody.velocity = velRotation * m_Rigidbody.velocity;
            }
            m_OldRotation = transform.eulerAngles.y;
        }

我通过实践,发现的效果就是如果m_SteerHelper为0,车转角度时就很死,如果为1,车转角度就特别灵活。所以这个属性得看个人的情况进行调整

6.函数TranctionControl,重点属性m_TractionControl

// crude traction control that reduces the power to wheel if the car is wheel spinning too much
        //如果汽车轮胎过度滑转,牵引力系统可以控制减少轮胎动力
        private void TractionControl()
        {
            WheelHit wheelHit;
            switch (m_CarDriveType)
            {
            //四驱
                case CarDriveType.FourWheelDrive:
                    // loop through all wheels
                    for (int i = 0; i < 4; i++)
                    {
                        m_WheelColliders[i].GetGroundHit(out wheelHit);

                        AdjustTorque(wheelHit.forwardSlip);
                    }
                    break;
            //后驱
                case CarDriveType.RearWheelDrive:
                    m_WheelColliders[2].GetGroundHit(out wheelHit);
                    AdjustTorque(wheelHit.forwardSlip);

                    m_WheelColliders[3].GetGroundHit(out wheelHit);
                    AdjustTorque(wheelHit.forwardSlip);
                    break;
            //前驱
                case CarDriveType.FrontWheelDrive:
                    m_WheelColliders[0].GetGroundHit(out wheelHit);
                    AdjustTorque(wheelHit.forwardSlip);

                    m_WheelColliders[1].GetGroundHit(out wheelHit);
                    AdjustTorque(wheelHit.forwardSlip);
                    break;
            }
        }


        private void AdjustTorque(float forwardSlip)
        {
            //当向前滑动距离超过阈值后,就说明轮胎过度滑转,则减少牵引力,以降低转速。
            if (forwardSlip >= m_SlipLimit && m_CurrentTorque >= 0)
            {
                m_CurrentTorque -= 10 * m_TractionControl;
            }
            else
            {
                m_CurrentTorque += 10 * m_TractionControl;
                if (m_CurrentTorque > m_FullTorqueOverAllWheels)
                {
                    m_CurrentTorque = m_FullTorqueOverAllWheels;
                }
            }
        }

从上面的函数可以看出来,属性m_TractionControl控制着牵引力每次增加或者减少的增量大小。
我们再来看另外一段Start中的代码

            //设置当前扭矩,初始化的扭矩值跟m_TractionControl大小有关,m_TractionControl决定是否有牵引力,如果m_TractionControl
            //值为0,则当前扭矩直接就是最大值,如果该值为1,则初始扭矩为0,然后汽车启动慢慢增加扭矩力。
            m_CurrentTorque = m_FullTorqueOverAllWheels - (m_TractionControl*m_FullTorqueOverAllWheels);

也就是说,如果m_TractionControl为1,那么一开始的汽车扭矩力就是0了,然后通过上面函数TranctionControl中的代码,每次10个力地增加。
我们会发现,每次启动汽车都会非常缓慢,而且如果爬坡时一次没冲上去,停在了半坡,再想冲上去,非常困难,因为这个时候扭矩力很小,动力不足;
如果我们把m_TractionControl设置为0,那么汽车一开始的动力就是满的,也不会再增加减少,但是汽车启动可能会特别快。
如果我们把m_TractionControl设置为0.5,那么汽车一开始就拥有满动力一半的动力,然后另外一半动力会动态地变化。
所以只要把握住了上面几点,就根据自己需要设置该属性就好了

7.函数CalculateRevs(),重点属性Revs

        //计算转速
        private void CalculateRevs()
        {
            // calculate engine revs (for display / sound)
            //计算引擎转速(只用于显示和声音)
            // (this is done in retrospect - revs are not used in force/power calculations)
            //(我的个人理解:)这个计算是回溯的转速,不能用于力的计算。也就是说,这个是根据速度,反算出来的转速,只是为了效果显示
            //计算在当前档位上的转速因子(决定在当前档位上的转速)
            CalculateGearFactor();
            //档位因子:当前档位/总档位数
            var gearNumFactor = m_GearNum/(float) NoOfGears;
            //计算在当前档位下的最小转速
            var revsRangeMin = ULerp(0f, m_RevRangeBoundary, CurveFactor(gearNumFactor));
            //计算在当前档位下的最大转速
            var revsRangeMax = ULerp(m_RevRangeBoundary, 1f, gearNumFactor);
            //根据当前的转速因子,计算当前的转速
            Revs = ULerp(revsRangeMin, revsRangeMax, m_GearFactor);
        }

其中有个比较奇怪的地方是计算RevsRangeMin,对档位因子使用了CurveFactor曲线函数,我的理解是,由于档位与转速的对应关系不是y = x这种简单的关系,所以不能直接使用gearNumFactor当做转速的比例系数,所以需要做一次转换,也就是说,档位或者速度与转速之间的对应关系是转速 = 1- (1-X)(1-X)。请看我根据这个函数做出来的曲线
档位-转速

至于为什么计算最大转速时,又没有使用这个函数,我就还没有搞清楚呢

8.函数CalculateGearFactor

        //计算档位因子
        private void CalculateGearFactor()
        {
            float f = (1/(float) NoOfGears);
            // gear factor is a normalised representation of the current speed within the current gear's range of speeds.
            // We smooth towards the 'target' gear factor, so that revs don't instantly snap up or down when changing gear.
            //我们要让值平滑地想着目标移动,以保证转速不会在变换档位时突然地上高或者降低
            //反向差值,通过当前速度的比例值,找当前速度在当前档位的比例位置,得到的值将是一个0~1范围内的值。
            var targetGearFactor = Mathf.InverseLerp(f*m_GearNum, f*(m_GearNum + 1), Mathf.Abs(CurrentSpeed/MaxSpeed));
            //从当前档位因子向目标档位因子做平滑差值
            m_GearFactor = Mathf.Lerp(m_GearFactor, targetGearFactor, Time.deltaTime*5f);
        }

从上面我们可以看出,至少档位速度之间的对应关系,大致是直线性,不是曲线的。不然上面的那种反插值函数,可能就得使用曲线变换函数了。

三.其他

其他的函数也有些有意思的函数,我就不在这里列举了。我上传了我注释版的CarController,希望能给有用的朋友一些启发。我的好多理解也可能不对,只是自圆其说,我日后再研究有结果会再更新的
我的上传注释版链接:
http://download.csdn.net/detail/narutojzm1/9516648

  • 19
    点赞
  • 59
    收藏
    觉得还不错? 一键收藏
  • 9
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 9
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值