DevLog_9

本節主要內容:修改子彈預製體,優化動畫播放,修復奔跑bug,添加子彈槍名文本信息,導入新的資源包和人物模型。

2020/4/2


這些天一直在忙着家裏的事情,所以沒有更新,今天繼續倒騰我的畢業設計。
1 將子彈運動這一實際函數放到Bullet腳本當中,在Bullet中設置一個子彈速度m_BulletSpeed,在Inspector界面賦值200即可。同時,凍結子彈剛體的X、Y軸上的旋轉以免子彈在擊中物體後改變方向。
2 在AimIn和Idle之間加一個Transition,設置條件爲Aim爲false時轉換,動畫播放80%時切換爲Idle。這樣做的目的是如果只是一瞬間按下右鍵開鏡,那麼不需要播放完整的AimIn動畫就回撤到Idle,要不然會顯得很奇怪。
在這裏插入圖片描述
在這裏插入圖片描述
3 還有一個問題就是快速的連續按下右鍵,視野會出現抽搐的現象,所以需要控制一下。我的解決辦法是,取消之前的自動放大視野的協程,改爲右鍵瞄準狀態下按住左Shift屏息時放大視野。

protected override void Aim()
{
   m_Animator.SetBool("Aim", m_IsAiming);

   // Player is holding breath to zoom in the field of view.
   if (m_IsAiming && Input.GetKey(KeyCode.LeftShift))
   {
       FOVZoom(m_TargetFOV);
   }
   else
   {
       FOVZoom(m_OriginFOV);
   }
}

// Zooms the view of field of the camera if player aims.
private void FOVZoom(float _targetFOV)
{
    float _currentFOVVelocity = 0;
    m_Camera.fieldOfView = Mathf.SmoothDamp(m_Camera.fieldOfView, 
        _targetFOV, ref _currentFOVVelocity, Time.deltaTime);
}

4 修復了奔跑會打斷開槍過程的bug,實際上應爲開槍可以打斷奔跑的狀態。
5 在Player下添加Canvas畫布,Canvas下添加Image,source image改爲crosshair_medium,並將寬高設置爲15,沿着Y軸的偏移設爲-5,如圖所示。
在這裏插入圖片描述
在這裏插入圖片描述
6 添加槍械後坐力函數。由於在X軸和Y上都會產生旋轉,Z軸上也會產生小範圍的旋轉偏移。所以要獲取子彈射出後的Z軸上的偏移,然後修正爲0。該函數在每次執行DoAttack()函數後執行。
在這裏插入圖片描述

private void RecoilMuzzle()
{
    // Produce a small range of shake on the X and Y axes.
    float _randomRecoilAngleX = Random.Range(m_RecoilAngleX.x, m_RecoilAngleX.y);
    float _randomRecoilAngleY = Random.Range(m_RecoilAngleY.x, m_RecoilAngleY.y);
    m_RecoilPart.transform.rotation *= Quaternion.Euler(-_randomRecoilAngleX, _randomRecoilAngleY, 0f);

    // Fixed rotation around the Z axis.
    float _offestZ = m_RecoilPart.transform.rotation.eulerAngles.z;
    m_RecoilPart.transform.rotation *= Quaternion.Euler(0f, 0f, -_offestZ);
}

7 添加武器名稱、子彈文本。
在這裏插入圖片描述
在這裏插入圖片描述
8 導入新的Low Poly Pack,將模型用Assault_Rifle_01_FPSController替換,然後更換腳本組件調整位置又是一晚上。。。
更新一下今天重做的腳本吧。

AssaultRifle

using UnityEngine;
using UnityEngine.UI;
using System.Collections;

namespace Scripts.Weapon
{
    public class AssaultRifle : FireArms
    {
        // Fields.
        private IEnumerator m_ReloadCheckCoroutine;

        protected override void Start()
        {
            base.Start();

            m_ReloadCheckCoroutine = CheckIfReloadOver();
        }

        private void Update()
        {
            ChangeStatue();

            // Presses left mouse button to shoot. 
            if (Input.GetMouseButton(0) && !m_IsReloading)
            {
                m_IsFiring = true;
                DoAttack();
                RecoilMuzzle();
            }
            else
            {
                m_IsFiring = false;
            }

            // Enters the aiming state when the player holds down the right mouse button.
            if (Input.GetMouseButton(1))
            {
                m_IsAiming = true;
                m_Crosshair.color = Color.clear;
                m_Crosshair.sprite = null;
            }
            else // Exits the aiming state when the player releases the right mouse button.
            {
                m_IsAiming = false;
                m_Crosshair.color = Color.red;
                m_Crosshair.sprite = m_OriginSprite;
            }
            Aim();

            // Presses R button to reload magazine.
            if (Input.GetKeyDown(KeyCode.R))
            {
                // Cann't shoot when the magazine if full.
                if (m_MagazineBulletsLeft == m_MagazineCapacity)
                {
                    return;
                }

                m_IsReloading = true;

                Reload();
            }

            m_AmmoStatueText.text = m_MagazineBulletsLeft + " / " + m_CartridgeBagBulletsLeft;
        }

        protected override void Shooting()
        {
            // No shooting until the interval is reached, or magazine if full.
            if (m_MagazineBulletsLeft <= 0 || !AllowToFire())
            {
                return;
            }

            m_LastFireTime = Time.time; // Note last fire time.

            m_Animator.Play("Fire", m_IsAiming ? 1 : 0, 0); // Play fire animation.

            // Only if player can shoot plays the sound.
            m_ShootingAudioSource.clip = m_FirearmsAudioClips.m_ShootSound;
            m_ShootingAudioSource.Play();

            CreatBulletPrefab();
            CreatCasingPrefab();
        }

        // Spawn bullet from bullet spawnpoint.
        private void CreatBulletPrefab()
        {
            // Play the muzzle effect.
            m_MuzzleEffect.Play();

            Instantiate(m_BulletPrefab, m_BulletSpawnPoint.position, m_BulletSpawnPoint.rotation);

            m_MagazineBulletsLeft -= 1;
        }

        //Spawn casing prefab at spawnpoint.
        private void CreatCasingPrefab()
        {
            Instantiate(m_CasingPrefab, m_CasingSpawnPoint.position, m_CasingSpawnPoint.rotation);
        }

        // This method recoil muzzle to simluate real shooting scene.
        private void RecoilMuzzle()
        {
            // Produce a small range of shake on the X and Y axes.
            float _randomRecoilAngleX = Random.Range(m_RecoilAngleX.x, m_RecoilAngleX.y);
            float _randomRecoilAngleY = Random.Range(m_RecoilAngleY.x, m_RecoilAngleY.y);
            m_RecoilPart.transform.rotation *= Quaternion.Euler(-_randomRecoilAngleX, _randomRecoilAngleY, 0f);

            // Fixed rotation around the Z axis.
            float _offestZ = m_RecoilPart.transform.rotation.eulerAngles.z;
            m_RecoilPart.transform.rotation *= Quaternion.Euler(0f, 0f, -_offestZ);
        }

        protected override void Aim()
        {
            m_Animator.SetBool("Aim", m_IsAiming);

            // Player is holding breath to zoom in the field of view.
            if (m_IsAiming && Input.GetKey(KeyCode.LeftShift))
            {
                FOVZoom(m_TargetFOV);
            }
            else
            {
                FOVZoom(m_OriginFOV);
            }
        }

        // Zooms the view of field of the camera if player aims.
        private void FOVZoom(float _targetFOV)
        {
            float _currentFOVVelocity = 0;
            m_Camera.fieldOfView = Mathf.SmoothDamp(m_Camera.fieldOfView, 
                _targetFOV, ref _currentFOVVelocity, Time.deltaTime);
        }

        protected override void Reload()
        {
            // Runs out of ammunition and player can not fire.
            if (m_CartridgeBagBulletsLeft <= 0)
            {
                return;
            }

            m_IsAiming = false; // The FOV should be the origin value if player start reloading.

            // Set the reload layer's weight to 1 to play the reload animation.
            m_Animator.SetLayerWeight(2, 1);

            m_Animator.SetTrigger(m_MagazineBulletsLeft > 0 ? "ReloadLeft" : "ReloadOut");

            m_ReloadAudioSource.clip = m_MagazineBulletsLeft > 0 ?
                m_FirearmsAudioClips.m_ReloadAmmoLeftSound : m_FirearmsAudioClips.m_ReloadOutOfAmmoSound;
            m_ReloadAudioSource.Play();

            // Fills in the magazine until the animation is nearly over.
            if (m_ReloadCheckCoroutine == null)
            {
                m_ReloadCheckCoroutine = CheckIfReloadOver();
                StartCoroutine(CheckIfReloadOver());
            }
            else
            {
                StopCoroutine(CheckIfReloadOver());
                m_ReloadCheckCoroutine = null;
                m_ReloadCheckCoroutine = CheckIfReloadOver();
                StartCoroutine(CheckIfReloadOver());
            }
        }

        private IEnumerator CheckIfReloadOver()
        {
            while (true)
            {
                yield return null;

                // Get the playing animation's info in specified layer.
                m_AnimatorStateInfo = m_Animator.GetCurrentAnimatorStateInfo(2);
                if (m_AnimatorStateInfo.IsTag("Reload"))
                {
                    if (m_AnimatorStateInfo.normalizedTime >= 0.95f)
                    {
                        // Bullets left int the cartridge bag can fill in a magazine.
                        if (m_CartridgeBagBulletsLeft > m_MagazineCapacity)
                        {
                            int _reloadAmount = m_MagazineCapacity - m_MagazineBulletsLeft; // Amount of reload.
                            m_MagazineBulletsLeft = m_MagazineCapacity; // Reload the arms.
                            m_CartridgeBagBulletsLeft -= _reloadAmount; // Reduce the bullets in cartridge bag.
                        }
                        else // Bullets left int the cartridge bag can not fill in a magazine.
                        {
                            m_MagazineBulletsLeft = m_CartridgeBagBulletsLeft;
                            m_CartridgeBagBulletsLeft = 0;
                        }

                        // Set the reload layer's weight back.
                        m_Animator.SetLayerWeight(2, 0);

                        m_IsReloading = false;

                        yield break;
                    }
                }
            }
        }
    }
}

FireArms

using UnityEngine;
using UnityEngine.UI;

namespace Scripts.Weapon
{
    public abstract class FireArms : MonoBehaviour, IWeapon
    {
        [Header("Ammunition Settings")]
        public int m_MagazineCapacity = 35; // Amount of bullets in a magazine
        public int m_CartridgeBagCapacity = 210; // Maximum number of bullets player can carry.
        [SerializeField] protected int m_MagazineBulletsLeft; // Current number of bullets left in the magazine
        [SerializeField] protected int m_CartridgeBagBulletsLeft; // Current number of bullets left in cartridge bag;

        [Header("Fire Settings")]
        public float m_FireRate; // Amount of bullets the gun can fire per second.
        public float m_FireInterval; // Interval between two shoots.
        public Vector2 m_RecoilAngleX; // Angle at which the muzzle is raised around X after each shoot.
        public Vector2 m_RecoilAngleY; // Angle at which the muzzle is raised around Y after each shoot.

        [Header("Spawn Position")]
        public Transform m_BulletSpawnPoint; // The position where the bullet shoot.
        public Transform m_CasingSpawnPoint; // The position where the casings are thrown.
        public Transform m_RecoilPart; // The part affected by th recoil.

        [Header("Prefabs")]
        public ParticleSystem m_MuzzleEffect;
        // public ParticleSystem m_CasingEffect;
        public GameObject m_BulletPrefab;
        public GameObject m_CasingPrefab;

        [Header("Audio")]
        public AudioSource m_ShootingAudioSource;
        public AudioSource m_ReloadAudioSource;
        public AssaultRifleAudioClipsData m_FirearmsAudioClips;

        [Header("Status")]
        public bool m_IsFiring = false;
        public bool m_IsReloading = false; // Player can't shot while he is reloading.
        public bool m_IsAiming = false;
        public bool m_IsWalking = false;
        public bool m_IsRunning = false;
        public bool m_IsHoldingBreath = false;

        [Header("Camera")]
        public Camera m_Camera;
        public float m_TargetFOV;

        [Header("UI")]
        public string m_WeaponName;
        public Image m_Crosshair;
        public Text m_WeaponNameText;
        public Text m_AmmoStatueText;
        

        // Fields.
        protected float m_LastFireTime;
        protected float m_OriginFOV;
        protected float m_CurrentBreathHoldingTime; // Current time player has held the breath.

        // References.
        protected Animator m_Animator;
        protected AnimatorStateInfo m_AnimatorStateInfo;
        protected Sprite m_OriginSprite;

        protected virtual void Start()
        {
            m_Animator = GetComponent<Animator>();

            SetUp();
        }

        private void SetUp()
        {
            // Initialize variables.
            m_MagazineBulletsLeft = m_MagazineCapacity;
            m_CartridgeBagBulletsLeft = m_CartridgeBagCapacity;
            m_FireInterval = 1 / m_FireRate;
            m_OriginFOV = m_Camera.fieldOfView;

            m_WeaponNameText.text = m_WeaponName;
            m_OriginSprite = m_Crosshair.sprite;
        }

        protected void ChangeStatue()
        {
            // Play animations according to the key pressed by the player.
            if (Input.GetKey(KeyCode.W) || Input.GetKey(KeyCode.S) || Input.GetKey(KeyCode.A) || Input.GetKey(KeyCode.D))
            {
                // Player cannot run while he is aiming or firing.
                if (Input.GetKey(KeyCode.LeftShift) && !m_IsAiming && !m_IsFiring)
                {
                    m_IsWalking = false;
                    m_IsRunning = true;
                }
                else
                {
                    m_IsRunning = false;
                    m_IsWalking = true;
                }
            }
            else
            {
                m_IsWalking = false;
                m_IsRunning = false;
            }
            m_Animator.SetBool("Walk", m_IsWalking);
            m_Animator.SetBool("Run", m_IsRunning);
        }

        public void DoAttack()
        {
            Shooting();
        }

        // Control the interval of player shooting.
        // This method will return true if player can shoot again.
        protected bool AllowToFire()
        {
            return Time.time - m_LastFireTime > m_FireInterval;
        }

        protected abstract void Shooting();
        protected abstract void Reload();
        protected abstract void Aim();
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章