1. 程式人生 > >Extreme Drift賽車遊戲C#源碼詳解(2)

Extreme Drift賽車遊戲C#源碼詳解(2)

時間 blank 出現 進行 rest clam .text static cost

Extreme Drift賽車遊戲C#源碼詳解(1)

接著上次的源碼分析:

MainMenu場景的UGUI部分:

Canvas中的EnoughMoney:

作用:當買車後金幣不足彈出的頁面

內部一個Text和一個Button,讓我有點疑惑的是,Button綁定的點擊事件是BuyVehicle函數:

    public void BuyVehicle()
    {
        //金幣足夠或者車輛未買才會執行
        if ((gameScore >= vehicleSetting[currentVehicleNumber].price) && !vehicleSetting[currentVehicleNumber].Bought)
        {
            
//數據持久化 PlayerPrefs.SetInt("BoughtVehicle" + currentVehicleNumber.ToString(), 1); //減錢 gameScore -= vehicleSetting[currentVehicleNumber].price; //防止負數 if (gameScore <= 0) { gameScore = 1; } //保存金幣數量 PlayerPrefs.SetInt("GameScore
", gameScore); //保存車輛購買狀態 vehicleSetting[currentVehicleNumber].Bought = true; } else { menuPanels.EnoughMoney.SetActive(true); } }

下一個對象LevelChooser:

內含三個對象,Top,Bottom,Levels:

Top:帶有OutlineLogo的簡單的Text

Bottom:內含Back和Start,返回上一個場景,原理是傳入一個索引值,函數如下

    public void CurrentPanel(int current)
    {
        //這裏為什麽要傳一個INT呢?因為開頭有定義
        //public enum Panels { MainMenu = 0, SelectVehicle = 1, SelectLevel = 2, Settings = 3 }
        //activePanel默認為0,這裏傳入的是1,就是選車
        activePanel = (Panels)current;

        //PlayerPrefs是數據持久化,從存檔取出數據驗證
        if (currentVehicleNumber != PlayerPrefs.GetInt("CurrentVehicle"))
        {
            currentVehicleNumber = PlayerPrefs.GetInt("CurrentVehicle");
            //循環所有的車輛
            foreach (VehicleSetting VSetting in vehicleSetting)
            {
                //當前車激活狀態,否則不激活
                if (VSetting == vehicleSetting[currentVehicleNumber])
                {
                    VSetting.vehicle.SetActive(true);
                    currentVehicle = VSetting;
                }
                else
                {
                    VSetting.vehicle.SetActive(false);
                }
            }
        }
        //根據傳入值做一些操作
        switch (activePanel)
        {

            case Panels.MainMenu:
                menuPanels.MainMenu.SetActive(true);
                menuPanels.SelectVehicle.SetActive(false);
                menuPanels.SelectLevel.SetActive(false);
                if (menuGUI.wheelColor) menuGUI.wheelColor.gameObject.SetActive(true);

                break;
            case Panels.SelectVehicle:
                menuPanels.MainMenu.gameObject.SetActive(false);
                menuPanels.SelectVehicle.SetActive(true);
                menuPanels.SelectLevel.SetActive(false);
                break;
            case Panels.SelectLevel:
                menuPanels.MainMenu.SetActive(false);
                menuPanels.SelectVehicle.SetActive(false);
                menuPanels.SelectLevel.SetActive(true);
                break;
            case Panels.Settings:
                menuPanels.MainMenu.SetActive(false);
                menuPanels.SelectVehicle.SetActive(false);            menuPanels.EnoughMoney.SetActive(true);
                menuPanels.SelectLevel.SetActive(false);
                break;
        }
    }

Start對應的函數如下:調用StartGame,然後調用下面的兩個函數

    public void StartGame()
    {
        //防止出錯
        if (startingGame) return;
        //設置背景
        FadeBackGround.SetBool("FadeOut", true);
        //調用協程加載(由於不能直接調用C#的多線程,這個函數實現多線程)
        StartCoroutine(LoadLevelGame(1.5f));
        startingGame = true;
    }


    IEnumerator LoadLevelGame(float waitTime)
    {
        yield return new WaitForSeconds(waitTime);
        //激活加載頁面
        menuGUI.loading.SetActive(true);
        //加載
        StartCoroutine(LoadLevelAsync());

    }

    IEnumerator LoadLevelAsync()
    {

        yield return new WaitForSeconds(0.4f);
        sceneLoadingOperation = Application.LoadLevelAsync(currentLevelNumber + 1);
        sceneLoadingOperation.allowSceneActivation = false;

        while (!sceneLoadingOperation.isDone || sceneLoadingOperation.progress < 0.9f)
        {
            menuLoadTime += Time.deltaTime;

            yield return 0;
        }
    }

Levels是一個帶有Scroll Rect的Image,內含Group,下有五張地圖對象

每一個地圖下有star1,star2,star3,簡單的Image表實獲取到的成績,以及一個locked表示是否解鎖,本質是一個簡單的Image

還有一個CurrentLevel對象,簡單的Image背景,裏面有兩個簡單的Text表示曾經獲取的成績

每一張地圖對象只是一個圖片,並不實際含有地圖,並且綁定一個函數,根據傳參進行下一步:

public void currentLevel(int current)
    {

        for (int i = 0; i < levelSetting.Length; i++)
        {
            if (i == current)
            {
                currentLevelNumber = current;
                levelSetting[i].panel.image.color = Color.white;
                levelSetting[i].panel.enabled = true;
                levelSetting[i].lockImage.gameObject.SetActive(false);
                PlayerPrefs.SetInt("CurrentLevelNumber", currentLevelNumber);
            }
            else if (levelSetting[i].locked == false)
            {
                levelSetting[i].panel.image.color = new Color(0.3f, 0.3f, 0.3f);
                levelSetting[i].panel.enabled = true;
                levelSetting[i].lockImage.gameObject.SetActive(false);
            }
            else
            {
                levelSetting[i].panel.image.color = new Color(1.0f, 0.5f, 0.5f);
                levelSetting[i].panel.enabled = false;
                levelSetting[i].lockImage.gameObject.SetActive(true);
            }
            //根據保存過的信息進行顯示
            if (levelSetting[i].bestTime)
            {
                if (PlayerPrefs.GetFloat("BestTime" + (i + 1).ToString()) != 0.0f)
                {
                    if (PlayerPrefs.GetInt("LevelStar" + (i + 1)) == 1)
                    {
                        levelSetting[i].stars.Star1.color = Color.white;
                    }
                    else if (PlayerPrefs.GetInt("LevelStar" + (i + 1)) == 2)
                    {
                        levelSetting[i].stars.Star1.color = Color.white;
                        levelSetting[i].stars.Star2.color = Color.white;
                    }
                    else if (PlayerPrefs.GetInt("LevelStar" + (i + 1)) == 3)
                    {
                        levelSetting[i].stars.Star1.color = Color.white;
                        levelSetting[i].stars.Star2.color = Color.white;
                        levelSetting[i].stars.Star3.color = Color.white;
                    }

                    levelSetting[i].bestTime.text = "BEST : " + GetComponent<FormatSecondsScript>().FormatSeconds(PlayerPrefs.GetFloat("BestTime" + (i + 1))).ToString();
                }
            }
        }
    }

下一個選項,Settings:設置界面

一個深色的BackGround

SettingsPanel:下面有七個對象

close是一個簡單的Button,綁定MainMenu的函數:

    public void SettingActive(bool activePanel)
    {
        menuPanels.Settings.gameObject.SetActive(activePanel);
    }

Quality下有四個Button,用於設置畫質,一個line用來分割,綁定的函數如下:

直接調用系統畫質API,然後持久化保存數據

    public void QualitySetting(int quality)
    {
        QualitySettings.SetQualityLevel(quality - 1, true);
        PlayerPrefs.SetInt("QualitySettings", quality);
    }

Sound音量設置下是兩個Toggle:選項框綁定函數:設置後保存

    public void DisableAudioButton(Toggle toggle)
    {
        if (toggle.isOn)
        {
            AudioListener.volume = 1;
            PlayerPrefs.SetInt("AudioActive", 0);
        }
        else
        {
            AudioListener.volume = 0;
            PlayerPrefs.SetInt("AudioActive", 1);
        }
    }


    public void DisableMusicButton(Toggle toggle)
    {
        if (toggle.isOn)
        {
            menuMusic.GetComponent<AudioSource>().mute = false;
            PlayerPrefs.SetInt("MusicActive", 0);
        }
        else
        {
            menuMusic.GetComponent<AudioSource>().mute = true;
            PlayerPrefs.SetInt("MusicActive", 1);
        }
    }

Control下四個對象,用於設置操作相關

兩個選項框用於設置操作模式

    public void ControlModeButtons(Toggle value)
    {
        if (value.isOn)
            PlayerPrefs.SetString("ControlMode", "Buttons");
    }
    public void ControlModeAccel(Toggle value)
    {
        if (value.isOn)
            PlayerPrefs.SetString("ControlMode", "Accel");
    }

下面是音量設置:都是一些簡單的函數

    public void DisableAudioButton(Toggle toggle)
    {
        if (toggle.isOn)
        {
            AudioListener.volume = 1;
            PlayerPrefs.SetInt("AudioActive", 0);
        }
        else
        {
            AudioListener.volume = 0;
            PlayerPrefs.SetInt("AudioActive", 1);
        }
    }


    public void DisableMusicButton(Toggle toggle)
    {
        if (toggle.isOn)
        {
            menuMusic.GetComponent<AudioSource>().mute = false;
            PlayerPrefs.SetInt("MusicActive", 0);
        }
        else
        {
            menuMusic.GetComponent<AudioSource>().mute = true;
            PlayerPrefs.SetInt("MusicActive", 1);
        }
    }

保存敏感系數函數:

    public void EditSensitivity()
    {
        PlayerPrefs.SetFloat("Sensitivity", menuGUI.sensitivity.value);
    }

最後一個Erase All Data是激活下面的Reset Panel,進行數據清除

Reset Panel是一個紅色背景和選擇YES或者NO

    public void EraseSave()
    {
        //清除所有數據後重新加載
        PlayerPrefs.DeleteAll();
        currentVehicleNumber = 0;
        Application.LoadLevel(0);

        foreach (Material mat in allRestMaterials)
            mat.SetColor("_Color", new Color(0.7f, 0.7f, 0.7f));
    }

Fade Background是一個隱藏對象,如果激活後,會顯示車輪樣的加載圖片,帶有一個綁定了動畫的Animator

到這裏,UGUI的Canvas就分析完了

下一個,先看簡單的Vehicle Root對象

裏面裝有12輛車和一個Shadow陰影

這12輛車就是汽車的3D模型,沒什麽好說的,Shadow是汽車的陰影

Shadow實現是利用Projector投影,用到一些陰影資源,這裏就不深究了

下一個Garage對象,也很簡單,就是一個車庫3D模型

最後來看Main Camera:

Main Camera下有兩個Audio Source,分別是背景音樂和點擊音效

Main Camera首先綁定的是MainMenu:

這兩篇文章都是圍繞MainMenu講的,現在從頭來看:

先看一些全局變量:這裏大部分的意思我都看懂了,不過有一部分我無法理解

    //金幣
    private int gameScore { get; set; }
    //視角移動速度
    public float cameraRotateSpeed = 5;
    //加載背景
    public Animator FadeBackGround;
    //背景音樂
    public AudioSource menuMusic;
    //簡單的GO
    public Transform vehicleRoot;
    //所有材質
    public Material[] allRestMaterials;
    //主界面:含有五個場景對象
    public MenuPanels menuPanels;
    //界面GUI,含有多個對象
    public MenuGUI menuGUI;
    //車輛數組,保存了汽車的各種信息
    public VehicleSetting[] vehicleSetting;
    //關卡數組,保存了關卡的信息
    public LevelSetting[] levelSetting;
    //默認以第一個界面開啟
    private Panels activePanel = Panels.MainMenu;
    //含義暫定
    private bool vertical, horizontal;
    private Vector2 startPos;
    private Vector2 touchDeltaPosition;

    //鼠標按下位置記錄
    private float x, y = 0;

    //當前車輛以及各種信息
    private VehicleSetting currentVehicle;

    private int currentVehicleNumber = 0;
    private int currentLevelNumber = 0;

    private Color mainColor;
    private bool randomColorActive = false;
    private bool startingGame = false;

    private float menuLoadTime = 0.0f;
    private AsyncOperation sceneLoadingOperation = null;

自定義的幾個類:

    //遊戲的GUI
    [System.Serializable]
    public class MenuGUI
    {
        public Text GameScore;
        public Text VehicleName;
        public Text VehiclePrice;

        public Slider VehicleSpeed;
        public Slider VehicleBraking;
        public Slider VehicleNitro;

        public Slider sensitivity;

        public Toggle audio;
        public Toggle music;
        public Toggle vibrateToggle;
        public Toggle ButtonMode, AccelMode;

        public Image wheelColor, smokeColor;
        public Image loadingBar;

        public GameObject loading;
        public GameObject customizeVehicle;
        public GameObject buyNewVehicle;
    }
    //五個場景的Panel
    [System.Serializable]
    public class MenuPanels
    {
        public GameObject MainMenu;
        public GameObject SelectVehicle;
        public GameObject SelectLevel;
        public GameObject EnoughMoney;
        public GameObject Settings;
    }
    //車輛信息
    [System.Serializable]
    public class VehicleSetting
    {
        public string name = "Vehicle 1";

        public int price = 20000;

        public GameObject vehicle;
        public GameObject wheelSmokes;

        public Material ringMat, smokeMat;
        public Transform rearWheels;

        public VehiclePower vehiclePower;
        //是否購買,隱藏
        [HideInInspector]
        public bool Bought = false;
        //車輛性能參數
        [System.Serializable]
        public class VehiclePower
        {
            public float speed = 80;
            public float braking = 1000;
            public float nitro = 10;
        }
    }
    //可以使用的幾張地圖信息
    [System.Serializable]
    public class LevelSetting
    {
        public bool locked = true;
        public Button panel;
        public Text bestTime;
        public Image lockImage;
        public StarClass stars;
        //該地圖獲得了幾顆星
        [System.Serializable]
        public class StarClass
        {
            public Image Star1, Star2, Star3;
        }
    }

這個腳本其他所有的函數都有說過了,除了Awake和Update函數:

先來看看Awake函數:

    //初始化
    void Awake()
    {

        AudioListener.pause = false;
        Time.timeScale = 1.0f;

        //震動選項,這裏沒用
        menuGUI.vibrateToggle.isOn = (PlayerPrefs.GetInt("VibrationActive") == 0) ? true : false;

        //默認金幣999999
        gameScore = 999999;

        //加載第一個場景
        CurrentPanel(0);

        //設置畫質
        if (PlayerPrefs.GetInt("QualitySettings") == 0)
        {
            PlayerPrefs.SetInt("QualitySettings", 4);
            QualitySettings.SetQualityLevel(3, true);
        }
        else
        {
            QualitySettings.SetQualityLevel(PlayerPrefs.GetInt("QualitySettings") - 1, true);
        }
        //設置車輛敏感度
        if (PlayerPrefs.GetFloat("Sensitivity") == 0.0f)
        {
            menuGUI.sensitivity.value = 1.0f;
            PlayerPrefs.SetFloat("Sensitivity", 1.0f);
        }
        else
        {
            menuGUI.sensitivity.value = PlayerPrefs.GetFloat("Sensitivity");
        }

        //設置操作模式
        switch (PlayerPrefs.GetString("ControlMode"))
        {
            case "":
                menuGUI.ButtonMode.isOn = true;
                break;
            case "Buttons":
                menuGUI.ButtonMode.isOn = true;
                break;
            case "Accel":
                menuGUI.AccelMode.isOn = true;
                break;
        }

        //當前車輛
        currentLevelNumber = PlayerPrefs.GetInt("CurrentLevelNumber");
        //地圖解鎖情況處理
        for (int lvls = 0; lvls < levelSetting.Length; lvls++)
        {
            if (lvls <= PlayerPrefs.GetInt("CurrentLevelUnlocked"))
                levelSetting[lvls].locked = false;

        }

        //設置地圖
        currentLevel(currentLevelNumber);

        //讀取默認控制信息設置
        switch (PlayerPrefs.GetString("ControlMode"))
        {
            case "":
                PlayerPrefs.SetString("ControlMode", "Buttons");
                menuGUI.ButtonMode.isOn = true;
                break;
            case "Buttons":
                menuGUI.ButtonMode.isOn = true;
                break;
            case "Accel":
                menuGUI.AccelMode.isOn = true;
                break;
        }

        //默認只買了第一輛車
        PlayerPrefs.SetInt("BoughtVehicle0", 1);


        //背景音樂和音量設置
        menuGUI.audio.isOn = (PlayerPrefs.GetInt("AudioActive") == 0) ? true : false;
        AudioListener.volume = (PlayerPrefs.GetInt("AudioActive") == 0) ? 1.0f : 0.0f;

        menuGUI.music.isOn = (PlayerPrefs.GetInt("MusicActive") == 0) ? true : false;
        menuMusic.mute = (PlayerPrefs.GetInt("MusicActive") == 0) ? false : true;

        currentVehicleNumber = PlayerPrefs.GetInt("CurrentVehicle");
        currentVehicle = vehicleSetting[currentVehicleNumber];


        int i = 0;

        //遍歷所有車輛,進行輪胎顏色煙霧設置
        foreach (VehicleSetting VSetting in vehicleSetting)
        {

            if (PlayerPrefsX.GetColor("VehicleWheelsColor" + i) == Color.clear)
            {
                vehicleSetting[i].ringMat.SetColor("_DiffuseColor", Color.white);
            }
            else
            {
                vehicleSetting[i].ringMat.SetColor("_DiffuseColor", PlayerPrefsX.GetColor("VehicleWheelsColor" + i));
            }



            if (PlayerPrefsX.GetColor("VehicleSmokeColor" + i) == Color.clear)
            {
                vehicleSetting[i].smokeMat.SetColor("_TintColor", new Color(0.8f, 0.8f, 0.8f, 0.2f));
            }
            else
            {
                vehicleSetting[i].smokeMat.SetColor("_TintColor", PlayerPrefsX.GetColor("VehicleSmokeColor" + i));
            }


            //Bought=true說明車沒有買
            if (PlayerPrefs.GetInt("BoughtVehicle" + i.ToString()) == 1)
            {
                VSetting.Bought = true;

                if (PlayerPrefs.GetInt("GameScore") == 0)
                {
                    PlayerPrefs.SetInt("GameScore", gameScore);
                }
                else
                {
                    gameScore = PlayerPrefs.GetInt("GameScore");
                }
            }

            //顯示當前車輛
            if (VSetting == vehicleSetting[currentVehicleNumber])
            {
                VSetting.vehicle.SetActive(true);
                currentVehicle = VSetting;
            }
            else
            {
                VSetting.vehicle.SetActive(false);
            }

            i++;
        }
    }

再看看Update方法:

    void Update()
    {

        //如果加載了下一個Scene
        if (sceneLoadingOperation != null)
        {
            //根據時間顯示加載輪胎動畫(圓形逐漸圓滿)
            /*public static Vector3 MoveTowards(Vector3 current, Vector3 target, float maxDistanceDelta);
            作用是將當前值current移向目標target。(對Vector3是沿兩點間直線)
            maxDistanceDelta就是每次移動的最大長度。
            返回值是當current值加上maxDistanceDelta的值,如果這個值超過了target,返回的就是target的值*/
            menuGUI.loadingBar.fillAmount = Mathf.MoveTowards(menuGUI.loadingBar.fillAmount, sceneLoadingOperation.progress + 0.2f, Time.deltaTime * 0.5f);
            //加載完成
            if (menuGUI.loadingBar.fillAmount > sceneLoadingOperation.progress)
                sceneLoadingOperation.allowSceneActivation = true;
        }


        if (menuGUI.smokeColor.gameObject.activeSelf || randomColorActive)
        {
            //車輛的後輪旋轉
            vehicleSetting[currentVehicleNumber].rearWheels.Rotate(1000 * Time.deltaTime, 0, 0);
            //開啟車輛輪胎煙霧
            vehicleSetting[currentVehicleNumber].wheelSmokes.SetActive(true);
        }
        else
        {
            vehicleSetting[currentVehicleNumber].wheelSmokes.SetActive(false);
        }


        //車輛參數的顯示
        menuGUI.VehicleSpeed.value = vehicleSetting[currentVehicleNumber].vehiclePower.speed / 100.0f;
        menuGUI.VehicleBraking.value = vehicleSetting[currentVehicleNumber].vehiclePower.braking / 100.0f;
        menuGUI.VehicleNitro.value = vehicleSetting[currentVehicleNumber].vehiclePower.nitro / 100.0f;
        menuGUI.GameScore.text = gameScore.ToString();

        //如果車已購買
        if (vehicleSetting[currentVehicleNumber].Bought)
        {
            //一些簡單的設置
            menuGUI.customizeVehicle.SetActive(true);
            menuGUI.buyNewVehicle.SetActive(false);

            menuGUI.VehicleName.text = vehicleSetting[currentVehicleNumber].name;
            menuGUI.VehiclePrice.text = "BOUGHT";
            PlayerPrefs.SetInt("CurrentVehicle", currentVehicleNumber);
        }
        else
        {
            //車沒買的話一些簡單的設置
            menuGUI.customizeVehicle.SetActive(false);
            menuGUI.buyNewVehicle.SetActive(true);

            menuGUI.VehicleName.text = vehicleSetting[currentVehicleNumber].name;
            menuGUI.VehiclePrice.text = "COST: " + vehicleSetting[currentVehicleNumber].price.ToString();
        }

        //我改了下源碼,因為暫時用不到安卓系統
        if (Input.GetMouseButton(0) && activePanel != Panels.SelectLevel)
        {
            //這些數學函數我看不懂,但明白意思,就是鼠標點擊屏幕出現是視覺效果和旋轉效果
            x = Mathf.Lerp(x, Mathf.Clamp(Input.GetAxis("Mouse X"), -2, 2) * cameraRotateSpeed, Time.deltaTime * 5.0f);
            Camera.main.fieldOfView = Mathf.Clamp(Camera.main.fieldOfView, 50, 60);
            Camera.main.fieldOfView = Mathf.Lerp(Camera.main.fieldOfView, 50, Time.deltaTime);
        }
        else {
            x = Mathf.Lerp(x, cameraRotateSpeed * 0.01f, Time.deltaTime * 5.0f);
            Camera.main.fieldOfView = Mathf.Lerp(Camera.main.fieldOfView, 60, Time.deltaTime);
        }
        //慢慢旋轉視角
        transform.RotateAround(vehicleRoot.position, Vector3.up, x);


    }

Extreme Drift賽車遊戲C#源碼詳解(2)