前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Unity基础(6)--资源管理(3)小赛车竞技

Unity基础(6)--资源管理(3)小赛车竞技

作者头像
孙寅
发布2020-06-02 17:16:32
7280
发布2020-06-02 17:16:32
举报
文章被收录于专栏:宜达数字宜达数字

Paste_Image.png

引言:今天突发奇想,想搞一个车的项目,话不多说,直接下载,但是之前对它一无所知。好在通过简单的测试,已经解决。运行效果还不错。上图是运行效果

1、导入游戏资源
1-1 导入标准资源包:

最后一个就是拥有车模型的资源包,而且导入Unity中会自动出现一个Standard Assets:

Paste_Image.png

Paste_Image.png

标准资源包里面就有车的模型:

Paste_Image.png

其他资源是我自己下载的:可以加群(Q群:134688909)获取相关资源

Paste_Image.png

1-2让车能够移动

因为系统自带的车模型,拥有移动功能,而且还不错。所以直接拿来当轮子使用

Paste_Image.png

再搞一些跟随车辆:显得有竞技效果,此处也是使用系统资源

Paste_Image.png

2、搭建简单地形:
2-1创建地形

Paste_Image.png

Paste_Image.png

2-2添加地形:

Paste_Image.png

Paste_Image.png

2-3最终效果图:

Paste_Image.png

3、添加游戏对象 - 车

Paste_Image.png

4、脚本一栏:
代码语言:javascript
复制
namespace UnityStandardAssets.Vehicles.Car
{
    //汽车驱动类型
    internal enum CarDriveType
    {
        //四驱
        FrontWheelDrive,
        //后驱
        RearWheelDrive,
        //前驱
        FourWheelDrive
    }
    //速度类型
    internal enum SpeedType
    {
        //英里每小时
        MPH,
        //千米每小时
        KPH
    }

    public class CarController : MonoBehaviour
    {
        [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;

        private Quaternion[] m_WheelMeshLocalRotations;
        private Vector3 m_Prevpos, m_Pos;
        private float m_SteerAngle;
        //当前档位
        private int m_GearNum;
        //档位因子
        private float m_GearFactor;
        //上一帧汽车方向
        private float m_OldRotation;
        //当前扭矩
        private float m_CurrentTorque;
        private Rigidbody m_Rigidbody;
        private const float k_ReversingThreshold = 0.01f;

        public bool Skidding { get; private set; }
        public float BrakeInput { get; private set; }
        public float CurrentSteerAngle{ get { return m_SteerAngle; }}
        public float CurrentSpeed{ get { if(m_Rigidbody == null) return 0;return m_Rigidbody.velocity.magnitude*2.23693629f; }}
        public float MaxSpeed{get { return m_Topspeed; }}
        public float Revs { get; private set; }
        public float AccelInput { get; private set; }
        public Rigidbody CarRigidbody { get{return m_Rigidbody;}}
        // Use this for initialization
        private void Start()
        {
            //四个轮胎的自转方向
            m_WheelMeshLocalRotations = new Quaternion[4];
            for (int i = 0; i < 4; i++)
            {
                m_WheelMeshLocalRotations[i] = m_WheelMeshes[i].transform.localRotation;
            }
            //设置重心
            m_WheelColliders[0].attachedRigidbody.centerOfMass = m_CentreOfMassOffset;
            //设置最大手刹扭矩
            m_MaxHandbrakeTorque = float.MaxValue;

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

        //档位更新
        private void GearChanging()
        {
            //这里其实相当于将所有速度进行了划分,f就是指的目前速度处于最大速度的几分之几
            //如果NoOfGears是5,那么就把速度分成5段
            //1档就是0/5 ~ 1/5,
            //2档就是1/5 ~ 2/5,
            //3档就是2/5 ~ 3/5,
            //4档就是3/5 ~ 4/5,
            //5档就是4/5 ~ 5/5,
            //每次先看当前档位是几档,然后看当前速度是否匹配当前档位,如果匹配当前档位没啥事,如果不匹配则看当前速度如果小于当前档位
            //最小速度则减档,如果大于当前档位最大速度则,加档;
            float f = Mathf.Abs(CurrentSpeed/MaxSpeed);
            //计算当前档位的速度上限
            float upgearlimit = (1/(float) NoOfGears)*(m_GearNum + 1);
            //计算当前档位的速度下限
            float downgearlimit = (1/(float) NoOfGears)*m_GearNum;
            //如果当前档位大于0, 则看当前速度是否小于当前档位,如果小于,则果断降档
            if (m_GearNum > 0 && f < downgearlimit)
            {
                m_GearNum--;
            }
            //如果当前档位小于最大档位-1档,且当前速度大于当前档位上限,则果断加档
            //这里我好奇的是,为什么条件不是"if(f>upgearlimit&&(m_GearNum < NoOfGears))",干嘛要减一?
            //我觉得原因可能是因为这个5档,只是理论速度,故意不达到。。。就像一般汽车为了汽车性能稳定,速度设定可以上200码一样,但是没有人上过。。
            if (f > upgearlimit && (m_GearNum < (NoOfGears - 1)))
            {
                m_GearNum++;
            }
        }


        // simple function to add a curved bias towards 1 for a value in the 0-1 range
        //
        private static float CurveFactor(float factor)
        {
            return 1 - (1 - factor)*(1 - factor);
        }


        // unclamped version of Lerp, to allow value to exceed the from-to range
        //非限制版本的插值函数,我刚开始没发现他有啥特别之处,“非限制”怎么体现?直到我发现他的value没有限制0~1的范围才瞬间明白。
        private static float ULerp(float from, float to, float value)
        {
            return (1.0f - value)*from + value*to;
        }

        //计算档位因子
        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);
        }

        //计算转速
        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);
        }

        //外部调用的汽车移动控制函数
        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;
            }

            //计算转速
            CalculateRevs();
            //改变档位
            GearChanging();
            //施加下压力
            AddDownForce();
            //检查轮胎
            CheckForWheelSpin();
            //牵引力控制系统
            TractionControl();
        }

        //控制车速
        private void CapSpeed()
        {
            float speed = m_Rigidbody.velocity.magnitude;
            switch (m_SpeedType)
            {
                case SpeedType.MPH:
                    //将速度m/s转换为mile/h,以便比较
                    speed *= 2.23693629f;
                    if (speed > m_Topspeed)
                        m_Rigidbody.velocity = (m_Topspeed/2.23693629f) * m_Rigidbody.velocity.normalized;
                    break;

                case SpeedType.KPH:
                //将速度m/s转换为km/h,以便比较
                    speed *= 3.6f;
                    if (speed > m_Topspeed)
                        m_Rigidbody.velocity = (m_Topspeed/3.6f) * m_Rigidbody.velocity.normalized;
                    break;
            }
        }


        private void ApplyDrive(float accel, float footbrake)
        {

            float thrustTorque;
            switch (m_CarDriveType)
            {
                case CarDriveType.FourWheelDrive:
                    thrustTorque = accel * (m_CurrentTorque / 4f);
                    for (int i = 0; i < 4; i++)
                    {
                        m_WheelColliders[i].motorTorque = thrustTorque;
                    }
                    break;

                case CarDriveType.FrontWheelDrive:
                    thrustTorque = accel * (m_CurrentTorque / 2f);
                    m_WheelColliders[0].motorTorque = m_WheelColliders[1].motorTorque = thrustTorque;
                    break;

                case CarDriveType.RearWheelDrive:
                    thrustTorque = accel * (m_CurrentTorque / 2f);
                    m_WheelColliders[2].motorTorque = m_WheelColliders[3].motorTorque = thrustTorque;
                    break;

            }

            for (int i = 0; i < 4; i++)
            {
                if (CurrentSpeed > 5 && Vector3.Angle(transform.forward, m_Rigidbody.velocity) < 50f)
                {
                    m_WheelColliders[i].brakeTorque = m_BrakeTorque*footbrake;
                }
                else if (footbrake > 0)
                {
                    m_WheelColliders[i].brakeTorque = 0f;
                    m_WheelColliders[i].motorTorque = -m_ReverseTorque*footbrake;
                }
            }
        }


        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物理效果
            //而直接操控速度方向,对车角度进行调整。
            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;
        }


        // this is used to add more grip in relation to speed
        private void AddDownForce()
        {
            m_WheelColliders[0].attachedRigidbody.AddForce(-transform.up*m_Downforce*
                                                         m_WheelColliders[0].attachedRigidbody.velocity.magnitude);
        }


        // checks if the wheels are spinning and is so does three things
        //检查轮胎是否旋转
        // 1) emits particles
        //检查是否发射尾气粒子
        // 2) plays tiure skidding sounds
        //播放滑行音
        // 3) leaves skidmarks on the ground
        //去掉刹车印
        // these effects are controlled through the WheelEffects class
        //这些特效都是通过WheelEffects类控制的
        private void CheckForWheelSpin()
        {
            // loop through all wheels
            for (int i = 0; i < 4; i++)
            {
                WheelHit wheelHit;
                m_WheelColliders[i].GetGroundHit(out wheelHit);

                // is the tire slipping above the given threshhold
                //轮胎滑行距离是否超出阈值
                if (Mathf.Abs(wheelHit.forwardSlip) >= m_SlipLimit || Mathf.Abs(wheelHit.sidewaysSlip) >= m_SlipLimit)
                {
                    //超出则发射烟雾粒子
                    m_WheelEffects[i].EmitTyreSmoke();

                    // avoiding all four tires screeching at the same time
                    //避免四个轮胎都同时播放滑行声音
                    // if they do it can lead to some strange audio artefacts
                    //如果那样的话会导致某些奇怪的音效
                    if (!AnySkidSoundPlaying())
                    {
                        m_WheelEffects[i].PlayAudio();
                    }
                    continue;
                }

                // if it wasnt slipping stop all the audio
                //假如没有超出阈值,还没有停止音效,则停止音效
                if (m_WheelEffects[i].PlayingAudio)
                {
                    m_WheelEffects[i].StopAudio();
                }
                // end the trail generation
                //停止烟雾生成
                m_WheelEffects[i].EndSkidTrail();
            }
        }

        // 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;
                }
            }
        }


        private bool AnySkidSoundPlaying()
        {
            for (int i = 0; i < 4; i++)
            {
                if (m_WheelEffects[i].PlayingAudio)
                {
                    return true;
                }
            }
            return false;
        }

        void Update()
        {
            //这两段If分别用来控制汽车X轴/Z轴上汽车不会过度翻转,导致汽车游戏性差。
            if (transform.rotation.eulerAngles.x > 60 && transform.rotation.eulerAngles.x < 300) {
                Quaternion temp = transform.rotation;
                if (transform.rotation.eulerAngles.x < 180) {
                    temp.eulerAngles = new Vector3(60f, transform.rotation.eulerAngles.y, transform.rotation.eulerAngles.z);
                }
                else {
                    temp.eulerAngles = new Vector3(300f, transform.rotation.eulerAngles.y, transform.rotation.eulerAngles.z);
                }
                transform.rotation = temp;
            }
            if (transform.rotation.eulerAngles.z > 25 && transform.rotation.eulerAngles.z < 335) {
                Quaternion temp = transform.rotation;
                if (transform.rotation.eulerAngles.z < 180) {
                    temp.eulerAngles = new Vector3(transform.rotation.eulerAngles.x, transform.rotation.eulerAngles.y, 25);

                }
                else
                {
                    temp.eulerAngles = new Vector3(transform.rotation.eulerAngles.x, transform.rotation.eulerAngles.y, 335);

                }
                transform.rotation = temp;
            }
        }
    }
}
本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1、导入游戏资源
    • 1-1 导入标准资源包:
    • 其他资源是我自己下载的:可以加群(Q群:134688909)获取相关资源
      • 1-2让车能够移动
        • 2、搭建简单地形:
          • 2-1创建地形
          • 2-2添加地形:
          • 2-3最终效果图:
        • 3、添加游戏对象 - 车
          • 4、脚本一栏:
          领券
          问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档