Unity武器系統的優化


Unity武器系統的優化

 

射擊游戲子彈是最基本的游戲對象,當然使用unity開發的話,做一個子彈並不是很難的事,從發射到子彈的飛行,到銷毀,基本上入門的程序員都能寫出來。

  然而這個看似簡單的東西,有着很大的優化空間。這個優化分兩部分,一是,子彈的發射優化,另一個是子彈的碰撞檢測優化。

     對於發射時的優化,主要考慮到子彈這個游戲對象使用頻率比較高,一個關卡里,要產生和銷毀大量的游戲對象,從內存加載到實例化,然后執行銷毀動作,速度,性能太差,而且因為unity的內存管理並不好,因此為產生大量的內存垃圾,如果游戲時間一長,或者在安卓等低端平台運行時就會造成卡頓現象。關於解決這個問題,網上有很多方案,也有現成的插件,PoolManager等,管理對象的實例化和銷毀。當然可以自己寫一個簡單點的東西來優化這一過程。怎么做呢?

原理就是重復利用子彈。用一個數組來保存子彈的引用,初始化的時候全部隱藏,射擊的時候按照數組下標依次激活。一個子彈打出去后,到了回收的時候,不要銷毀,而是將其“隱藏”,並做初始化處理。這個過程還可以優化,例如一個彈夾的子彈數量是30,實際上你只需要預加載15發子彈就可以了。依次激活,重復利用。

代碼中有詳細的注釋,我就不多說了。

 

using UnityEngine;
using System.Collections;
using System.Collections.Generic;
public class GunShot : MonoBehaviour
{
    public string weaponName;//武器的名稱,使用Resource加載
    private List<BullectOfWeapon> weapons;//用List集合保存子彈對象數組
    public int shellCount = 20;//一個彈夾的子彈數量
    public int maxShell=10;//預加載子彈數量,因為可以循環利用,可以小於等於彈夾的子彈數量
    private int currentShell = 0;//當前激活的子彈
    
    public float fireRote=0.2f;//開火時間間隔,一次射擊到下一次的時間
    private float currentFireTime;//當前時間

    public float loadTime = 1.5f;//上子彈的時間。
    private float currentLoadTime = 0;
    private int currentCount;//一個彈夾打出去的子彈數


    public int chargerCount=5;//彈夾的數量
    void Start() {
        InitGun();//初始化武器
    }
    void Update() {
        if (!isLoading()&&checkFire()&&  Input.GetButton("Fire1")) 
{//注意條件順序,寫反就不對了

Shot();//開火代碼 } } public void InitGun()//初始化武器 { //武器的名稱,使用Resource加載 BullectOfWeapon _weaponModel = Resources.Load<BullectOfWeapon>(weaponName); if (_weaponModel) { weapons = new List<BullectOfWeapon>(); for (int i = 0; i < maxShell; i++) { //實例化子彈對象 BullectOfWeapon _weapon = Instantiate(_weaponModel); //將其隱藏 _weapon.gameObject.SetActive(false); //初始化子彈,將子彈放在“彈夾里”,將生成的子彈作為子對象 //這么做可以方便管理 _weapon.InitWeapon(transform); //將子彈對象的引用添加至集合 weapons.Add(_weapon); } } _weaponModel = null;//釋放子彈對象,這個可以讓系統能更好多份對內存進行回收 } public bool checkFire() {//根據上一次開火的時間判斷是否可以開火 currentFireTime += Time.deltaTime; if (currentFireTime >= fireRote) { currentFireTime = 0; return true; } return false; } public bool isLoading() { if (chargerCount == 0) return true;//彈夾使用完畢 if (currentCount < shellCount) { //判斷是否已經 return false; } currentLoadTime += Time.deltaTime; if (currentLoadTime >= loadTime) { currentLoadTime = 0; chargerCount--;//減少彈夾的數量 currentCount = 0;//下一個彈夾從零計數 return false; } return true; } public void Shot() { if (weapons!=null && weapons.Count > 0) {//先判斷子彈數組防止為空 if (!weapons[currentShell].gameObject.activeSelf)//檢查當前子彈是否正在使用 { weapons[currentShell].ActiveWeapon();//將子彈激活 currentShell = (currentShell + 1) % maxShell;//移動下標 currentCount++; } } } }
public class BullectOfWeapon : MonoBehaviour
{
    private Transform charger;//彈夾,用來回收時將子彈對象放在父物體里,方便資源的管理
    
    public float lifeTime = 3;//子彈的生存時間
    public float maxSpeed;//子彈移動的最大速度


    private Vector3 velocity;//子彈的實時速度
    private Vector3 currentPos;//子彈的當前位置
    private Vector3 nextPos;//子彈的上一個位置
    private bool isHit;//是否碰撞
    private float distence;
    private RaycastHit hit;
    public void InitWeapon(Transform parent) {//初始化子彈
        if (!charger) this.charger = parent;//將子彈作為子對象放在“彈夾里”
        transform.parent = charger;
        //位置,角度歸零
        transform.localPosition = Vector3.zero;
        transform.localRotation = Quaternion.identity;
        
    }
    public void ActiveWeapon() {//激活子彈
       
        currentPos = nextPos = transform.position;//初始化當前位置和下一個位置
        isHit = false;//
        velocity = maxSpeed * transform.forward.normalized;//初始化速度
        transform.parent = null;//將子彈從彈夾分離出去
        gameObject.SetActive(true);
        //開啟協程,lifeTime之后將物體再次隱藏
        StartCoroutine(DestroyWeapon(gameObject,lifeTime));
    }
    public IEnumerator DestroyWeapon(GameObject desObj, float desTime) {
        yield return new WaitForSeconds(desTime>0?desTime:0);
        if (desObj.activeSelf)
        {
            desObj.SetActive(false);
            //隱藏子彈后重新初始化,為下一次使用做准備
            InitWeapon(charger);
        }
        
    }
    void Update() {
        Fire();
    }
    public void Fire() { //這個是子彈的被射出后的飛行與碰撞檢測代碼
        if (!isHit)//沒有發生碰撞保持飛行
        {
            Flying();

        }
       
    }
    private void Flying() {//子彈的飛行
      
    }
   
}

 

 

 

以上就是槍和子彈的部分代碼。關於特效,力什么的我就省略了。接下來來探討一下子彈的飛行與碰撞的檢測。

一般新手做子彈,使用transform+碰撞器+剛體,這樣的子彈性能不高,而且極容易發生穿牆事件。Unity官方的士兵射擊樣例就是這么搞的,為了防止穿牆,子彈的飛行速度很慢,看起來很假。

學過射線的,估計會使用射線的碰撞檢測,判斷子彈是否擊中目標。然后在碰撞點生成火花特效等。這樣的子彈不會穿牆,但是少了子彈飛行的效果。然后就有人用發射一個粒子來展示子彈的飛行。

對於上面的兩種方案,第一種是要果斷放棄的,性能差。第二種性能比較好,我來將其優化一下,讓子彈看起來更像子彈,就是射線檢測+子彈飛行效果。通過修改子彈的位移,來模擬飛行效果,使用射線檢測碰撞。這樣性能和效果都有了。好不多說看實現代碼。

private void Flying() {//子彈的飛行
        nextPos += velocity * Time.deltaTime;//使用平滑過渡不用擔心穿牆問題
        
        distence = (nextPos-currentPos).magnitude;
        if (distence > 0) {
            if (Physics.Raycast(currentPos,transform.forward,out hit,distence))
            {
                
                isHit = true;

                nextPos = hit.point;//將下一個目標點設置為碰撞點
            }
        }
        currentPos = transform.position;//獲取當前位置
        transform.position = nextPos;//將當前位置置於下一個點
        if (isHit)//碰撞后將子彈隱藏回收
        {
            StartCoroutine(DestroyWeapon(gameObject,0));
        }
    }

 

關於特效我就不多說了。使用TrailRenderer可以模擬子彈的軌跡,簡單實用。力效果什么的大家自行加上。

上面就是優化后的武器系統,設計原理盡可能的節省內存,並提升自身性能,同時保證系統的正常使用。上面的方法可以同樣使用在特效上,用數組保存火花粒子,彈殼,或者子彈貼圖,這樣在不失真的情況下還能保證系統性能。例如規定彈孔貼圖的數量,然后就是動態的,每生成一個彈孔后,不是銷毀,而。當彈孔數量達到最大值時,將以前的彈孔貼圖放在新的碰撞點即可。而不是重復的執行加載實例和銷毀,那樣對系統的內存和效率影響更大。因為一個游戲對象的初始化過程和對應腳本的加載是很耗時的,盡管這對CPU來說是小事。

 本文的鏈接:http://www.cnblogs.com/jqg-aliang/p/4714750.html。轉載請申明出處,謝謝!


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM