1. 程式人生 > >Unity官方案例進階--Roll a ball

Unity官方案例進階--Roll a ball

找到 mage 倒計時 nta orm 改變 water tag col

技術分享圖片

經過上一次的學習,我又想改進一下這款 Roll a ball 遊戲,首先這款遊戲只有勝利沒有失敗,所以我想定義一個條件來控制它的輸贏,因此我想到了如下的方案: **使我們的玩家控制 Player 在規定時間內達到一定的分數就勝利,反之則失敗**

任務目標

這裏只是大概的目標內容,還有一些細節會在後面完成的時候體現出來。

  1. 完成 PickUp 的隨機刷新出現,每隔3秒刷新一個
  2. 做一個倒計時器用於顯示遊戲剩余時間
  3. 控制分數和時間,若玩家在規定時間內達到分數則顯示 You Win!字樣,Player 禁止移動;若失敗,則小球爆炸並顯示 Game Over! 字樣。

環境搭建

主體還是 Roll a ball 的內容,其中有一些小改動,我們在此完成一下。

刪除場景中的所有 PickUp

因為我們要完成的是隨機刷新 PickUp 物體,所以我們不需要在場景中添加 PickUp 物體,到時候會用到一個新的知識來完成我們的隨機顯示 PickUp 物體的操作。

添加倒計時文本

因為我們需要讓玩家知道遊戲的剩余時間,所以我們需要添加一個用來顯示時間的文本,位置我選擇在了中間頂部的位置,其他設置隨意。

細節修改

因為最終我們顯示的文本不再是只有勝利,所以將我們之前創建的 WinText 改名為 ResultText。

遺漏補充

在官方的案例中,PickUp 是有一個黃色的材質,而我在之前做的項目中忘記添加了,所以我現在添加了一個黃色材質球給我們的 PickUp。

技術分享圖片

以上就是我們的環境搭建相關的操作,下面就要開始我們的腳本編寫,來實現遊戲的運行了。

遊戲運行

修改以及準備

我將 PlayerController 中除了將 Player 運動的腳本其余的都刪除了。

腳本文件名:PlayerController

代碼如下:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class PlayerController : MonoBehaviour {
public float speed;

private float moveHorizontal;
private float moveVertical;
private Vector3 movement;
private void FixedUpdate()
{
    moveHorizontal = Input.GetAxis("Horizontal");
    moveVertical = Input.GetAxis("Vertical");
    movement = new Vector3(moveHorizontal, 0.0f, moveVertical);

    GetComponent<Rigidbody>().AddForce(movement * speed * Time.deltaTime);
}
}

我將剩下的代碼貼在上方了,有不清楚的或者項目刪除了的可以直接復制過去。

創建 GameController 腳本

還是在我們的 Player 物體上添加腳本取名為 GameController ,我定義它用來控制整個遊戲的運行腳本,其實我們遊戲的改變主要就是體現在此腳本的編寫,所以當我們完成此腳本的編寫也就意味著我們遊戲的完成,廢話不多說了下面我們就來完成它。

刪除補回

因為我們將原來 PlayerController 腳本中除了控制小球運動的代碼都刪除了,所以需要先補充一下 Planyer 與 PickUp 的碰撞以及分數控制的相關代碼,此時只是補回後面還會有更改。

腳本文件名:GameController

代碼如下:

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

public class GameController : MonoBehaviour {

    public Text countText;

    private int count;

    private void Awake()
    {
        count = 0;
    }

    private void Start()
    {
        SetCount();
    }

    private void OnTriggerEnter(Collider other)
    {
        if (other.gameObject.tag == "PickUp")
        {
            Destroy(other.gameObject);
            count++;
            SetCount();
        }
    }

    void SetCount()
    {
        countText.text = "Count:" + count;
    }
}

目標一:刷新 PickUp 物體

在此我們要完成的是讓我們的 PickUp 物體在地面上的隨機位置每3秒刷新一個

這其中我們會用到幾個新的知識,有需要的話最好還是去網上找找資料了解一下或者翻閱官網的API。

  1. Clone 克隆物體
  2. Randam 隨機類

腳本文件名:GameController

代碼如下:

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

public class GameController : MonoBehaviour {

    public Text countText;
    //用於添加 PickUp 物體,之後克隆要用
    public GameObject pickUp;

    private int count;
    //定義一個三維向量的值用於存儲克隆物體的位置
    private Vector3 newPickUpPt;
    //定義一個需要等待克隆物體的時間,根據目標應該賦值為3
    private float fireRate;
    //定義一個下一次克隆物體的時間
    private float nextFire;

    private void Awake()
    {
        count = 0;
        //給 fireRate 賦值,控制克隆物體的間隔時間
        fireRate = 3f;
    }

    private void Start()
    {
        SetCount();
    }

    private void Update()
    {
        //判斷遊戲時間是否大於我們需要的它克隆的時間
        if (Time.time > nextFire)
        {
            //計算下一次克隆時的時間
            nextFire = Time.time + fireRate;
            //給我們的 PickUp 隨機一個位置
            newPickUpPt = new Vector3(Random.Range(-8, 8), 1f, Random.Range(-8, 8));
            //這就是克隆的操作腳本了
            //Instantiate(需要克隆的物體,克隆物體的 Pothion 值,克隆物體的 Rotation值)
            //此處我們要克隆的物體是 PickUp,他的位置就是我們之前隨機存儲的那個位置,Quaternion.identity 的意思是與原物體的 Rotation 值保持不變
            Instantiate(pickUp, newPickUpPt, Quaternion.identity);
        }
    }

    private void OnTriggerEnter(Collider other)
    {
        if (other.gameObject.tag == "PickUp")
        {
            Destroy(other.gameObject);
            count++;
            SetCount();
        }
    }

    void SetCount()
    {
        countText.text = "Count:" + count;
    }
}

腳本我們現在就寫完了,保存一下腳本,然後來到我們的 Unity 編輯器中,給我們 Player 物體中的 GameController 腳本中的 Player 物體添加一下,然後運行就可以看到 PickUp 開始有規律的創建了。

控制倒計時器

做倒計時器又需要用到一個新的知識--協程,這個知識還是很重要的,最好先去翻閱些資料了解一下它。

腳本文件名:GameController

代碼如下:

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

public class GameController : MonoBehaviour {

    public Text countText;
    //用於添加時間 Text 物體
    public Text timeText;
    //用於添加 PickUp 物體,之後克隆要用
    public GameObject pickUp;

    private int count;
    //定義一個三維向量的值用於存儲克隆物體的位置
    private Vector3 newPickUpPt;
    //定義一個需要等待克隆物體的時間,根據目標應該賦值為3
    private float fireRate;
    //定義一個下一次克隆物體的時間
    private float nextFire;
    private float time;

    private void Awake()
    {
        count = 0;
        //給 fireRate 賦值,控制克隆物體的間隔時間
        fireRate = 3f;
        time = 8;
    }

    private void Start()
    {
        SetCount();
        //開始我們的協程
        StartCoroutine(Timer());
    }

    private IEnumerator Timer()
    {
        //定義一個循環,條件為要time>0
        while (time > 0)
        {
            //間隔1秒繼續執行一下代碼
            yield return new WaitForSeconds(1);
            //time自減1
            time--;
            //更新我們的時間文本
            SetTime();
        }
        //結束我們的協程
        StopCoroutine(Timer());
    }

    private void Update()
    {
        //判斷遊戲時間是否大於我們需要的它克隆的時間
        if (Time.time > nextFire)
        {
            //計算下一次克隆時的時間
            nextFire = Time.time + fireRate;
            //給我們的 PickUp 隨機一個位置
            newPickUpPt = new Vector3(Random.Range(-8, 8), 1f, Random.Range(-8, 8));
            //這就是克隆的操作腳本了
            //Instantiate(需要克隆的物體,克隆物體的 Pothion 值,克隆物體的 Rotation值)
            //此處我們要克隆的物體是 PickUp,他的位置就是我們之前隨機存儲的那個位置,Quaternion.identity 的意思是與原物體的 Rotation 值保持不變
            Instantiate(pickUp, newPickUpPt, Quaternion.identity);
        }
    }

    private void OnTriggerEnter(Collider other)
    {
        if (other.gameObject.tag == "PickUp")
        {
            Destroy(other.gameObject);
            count++;
            SetCount();
        }
    }

    void SetCount()
    {
        countText.text = "Count:" + count;
    }

    void SetTime()
    {
        timeText.text = time.ToString();
    }
}

控制結果

方法挺多的,可以自己想想怎麽去完成。

腳本文件名:GameController

代碼如下:

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

public class GameController : MonoBehaviour {

    public Text countText;
    //用於添加時間 Text 物體
    public Text timeText;
    public Text resultText;
    //用於添加 PickUp 物體,之後克隆要用
    public GameObject pickUp;

    private int count;
    //定義一個三維向量的值用於存儲克隆物體的位置
    private Vector3 newPickUpPt;
    //定義一個需要等待克隆物體的時間,根據目標應該賦值為3
    private float fireRate;
    //定義一個下一次克隆物體的時間
    private float nextFire;
    private float time;

    private void Awake()
    {
        count = 0;
        //給 fireRate 賦值,控制克隆物體的間隔時間
        fireRate = 3f;
        time = 8;
    }

    private void Start()
    {
        SetCount();
        //開始我們的協程
        StartCoroutine(Timer());
    }

    private IEnumerator Timer()
    {
        //定義一個循環,條件為要time>0
        while (time > 0)
        {
            //間隔1秒繼續執行一下代碼
            yield return new WaitForSeconds(1);
            //time自減1
            time--;
            //更新我們的時間文本
            SetTime();
            //調用我們創建的控制結果函數
            if (time <= 0 && count < 2)
            {
                resultText.text = "Game Over!";
            }
            else if (count >= 2)
            {
                resultText.text = "You Win!";
            }
        }
        //結束我們的協程
        StopCoroutine(Timer());
    }

    private void Update()
    {
        //判斷遊戲時間是否大於我們需要的它克隆的時間
        if (Time.time > nextFire && time > 0)
        {
            //計算下一次克隆時的時間
            nextFire = Time.time + fireRate;
            //給我們的 PickUp 隨機一個位置
            newPickUpPt = new Vector3(Random.Range(-8, 8), 1f, Random.Range(-8, 8));
            //這就是克隆的操作腳本了
            //Instantiate(需要克隆的物體,克隆物體的 Pothion 值,克隆物體的 Rotation值)
            //此處我們要克隆的物體是 PickUp,他的位置就是我們之前隨機存儲的那個位置,Quaternion.identity 的意思是與原物體的 Rotation 值保持不變
            Instantiate(pickUp, newPickUpPt, Quaternion.identity);
        }
    }

    private void OnTriggerEnter(Collider other)
    {
        if (other.gameObject.tag == "PickUp")
        {
            Destroy(other.gameObject);
            count++;
            SetCount();
        }
    }

    void SetCount()
    {
        countText.text = "Count:" + count;
    }

    void SetTime()
    {
        timeText.text = time.ToString();
    }
}

遊戲的完善

這是我們此遊戲的最後部分,用於完善一些遊戲中缺乏的地方,也添加了一些比較有意思的東西,來使遊戲更加具有趣味性。

完善物體的克隆創建

當遊戲結束時,我們的克隆還在不停操作,這是我們不願意看到的,所以接下來我們來完善它。

其實完善很簡單,只需在我們克隆創建操作的 if 判斷中添加條件就可以了。

腳本文件名:GameController

代碼如下:

private void Update()
    {
        //判斷遊戲時間是否大於我們需要的它克隆的時間
        if (Time.time > nextFire && time > 0)
        {
            //計算下一次克隆時的時間
            nextFire = Time.time + fireRate;
            //給我們的 PickUp 隨機一個位置
            newPickUpPt = new Vector3(Random.Range(-8, 8), 1f, Random.Range(-8, 8));
            //這就是克隆的操作腳本了
            //Instantiate(需要克隆的物體,克隆物體的 Pothion 值,克隆物體的 Rotation值)
            //此處我們要克隆的物體是 PickUp,他的位置就是我們之前隨機存儲的那個位置,Quaternion.identity 的意思是與原物體的 Rotation 值保持不變
            Instantiate(pickUp, newPickUpPt, Quaternion.identity);
        }
    }

完善倒計時器

當我們遊戲勝利後會發現我們的倒計時器依舊還在運行,所以這是有問題的,因此接下來我們就來完善它。

來到我們打代碼,會發現我們的遊戲勝利放在了倒計時器中的循環中,因此只要當我們勝利之後添加一個 個 Break 語句跳出循環就可以完成我們的倒計時停止的操作了。

腳本文件名:GmaeController

代買如下:

private IEnumerator Timer()
    {
        //定義一個循環,條件為要time>0
        while (time > 0)
        {
            //間隔1秒繼續執行一下代碼
            yield return new WaitForSeconds(1);
            //time自減1
            time--;
            //更新我們的時間文本
            SetTime();
            //調用我們創建的控制結果函數
            if (time <= 0 && count < 2)
            {
                resultText.text = "Game Over!";
            }
            else if (count >= 2)
            {
                resultText.text = "You Win!";
                break;
            }
        }
        //結束我們的協程
        StopCoroutine(Timer());
    }

寫完代碼後運行,然後完成我們的兩個小球的條件後,會發現我們的倒計時停止了,耶!完成啦!但是我們又會驚喜的發現剛完成的遊戲結束後停止創建物體的任務並沒有完善,我們只考慮到了遊戲失敗時停止創建,若當遊戲勝利結束時並沒有考慮到,所以我們又要回過頭來重新完善我們之前的任務。

補:完善物體的克隆創建

要完善其實也很簡單,只需要在我們創建的條件中再添加一個 Count 要小於我們的目標值就可以了。

腳本文件名: GameController

代碼如下:

private void Update()
    {
        //判斷遊戲時間是否大於我們需要的它克隆的時間
        if (Time.time > nextFire && time > 0 && count < 2)
        {
            //計算下一次克隆時的時間
            nextFire = Time.time + fireRate;
            //給我們的 PickUp 隨機一個位置
            newPickUpPt = new Vector3(Random.Range(-8, 8), 1f, Random.Range(-8, 8));
            //這就是克隆的操作腳本了
            //Instantiate(需要克隆的物體,克隆物體的 Pothion 值,克隆物體的 Rotation值)
            //此處我們要克隆的物體是 PickUp,他的位置就是我們之前隨機存儲的那個位置,Quaternion.identity 的意思是與原物體的 Rotation 值保持不變
            Instantiate(pickUp, newPickUpPt, Quaternion.identity);
        }
    }

趣味添加:遊戲失敗小球爆炸效果

爆炸效果是從網上找的,在此感謝TPMer博主提供,爆炸效果需要用到的素材請到他的CSDN博客最底處下載。

TPMerd的CSDN博客

素材下載完成後記住他的下載位置當我們導入時需要用到,再打開我們的 Unity 編輯器在菜單欄中的 Assets 中找到 Import Package 再選擇 Custom Package 選項來添加我們的素材,找到我們之前下載的素材包打開,在 Import Unity Package 界面中,我們勾去第一個 000.unity 場景文件後選擇右下角的 Import 選項添加我們的素材。

技術分享圖片

然後編輯我們的腳本文件來完成我們的小球爆炸效果。

腳本文件名:GameController

代碼如下:

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

public class GameController : MonoBehaviour {

    public Text countText;
    //用於添加時間 Text 物體
    public Text timeText;
    public Text resultText;
    //用於添加 PickUp 物體,之後克隆要用
    public GameObject pickUp;
    //用於添加我們的爆炸物體
    public GameObject Bang;

    private int count;
    //定義一個三維向量的值用於存儲克隆物體的位置
    private Vector3 newPickUpPt;
    //定義一個需要等待克隆物體的時間,根據目標應該賦值為3
    private float fireRate;
    //定義一個下一次克隆物體的時間
    private float nextFire;
    private float time;

    private void Awake()
    {
        count = 0;
        //給 fireRate 賦值,控制克隆物體的間隔時間
        fireRate = 3f;
        time = 8;
    }

    private void Start()
    {
        SetCount();
        //開始我們的協程
        StartCoroutine(Timer());
    }

    private IEnumerator Timer()
    {
        //定義一個循環,條件為要time>0
        while (time > 0)
        {
            //間隔1秒繼續執行一下代碼
            yield return new WaitForSeconds(1);
            //time自減1
            time--;
            //更新我們的時間文本
            SetTime();
            //調用我們創建的控制結果函數
            if (time <= 0 && count < 2)
            {
                resultText.text = "Game Over!";
                //將 Player 物體銷毀
                Destroy(this.gameObject);
                //克隆我們的爆炸物體來代替我們的 Player 物體
                Instantiate(Bang, this.transform.position, this.transform.rotation);
            }
            else if (count >= 2)
            {
                resultText.text = "You Win!";
                break;
            }
        }
        //結束我們的協程
        StopCoroutine(Timer());
    }

    private void Update()
    {
        //判斷遊戲時間是否大於我們需要的它克隆的時間
        if (Time.time > nextFire && time > 0 && count < 2)
        {
            //計算下一次克隆時的時間
            nextFire = Time.time + fireRate;
            //給我們的 PickUp 隨機一個位置
            newPickUpPt = new Vector3(Random.Range(-8, 8), 1f, Random.Range(-8, 8));
            //這就是克隆的操作腳本了
            //Instantiate(需要克隆的物體,克隆物體的 Pothion 值,克隆物體的 Rotation值)
            //此處我們要克隆的物體是 PickUp,他的位置就是我們之前隨機存儲的那個位置,Quaternion.identity 的意思是與原物體的 Rotation 值保持不變
            Instantiate(pickUp, newPickUpPt, Quaternion.identity);
        }
    }

    private void OnTriggerEnter(Collider other)
    {
        if (other.gameObject.tag == "PickUp")
        {
            Destroy(other.gameObject);
            count++;
            SetCount();
        }
    }

    void SetCount()
    {
        countText.text = "Count:" + count;
    }

    void SetTime()
    {
        timeText.text = time.ToString();
    }
}

當我們將腳本寫完保存後,打開 Unity 編輯器在我們的 Player 物體中 GameController 腳本下方的 Bang 處添加我們 Prefabs 文件下的爆炸文件後運行,當我們遊戲結束時會發現我們的 Player 物體爆炸了,但是我們下方會有紅色文字報錯。

技術分享圖片

它的意思大體為 物體已經被銷毀了,但是遊戲依舊還在使用它 ,然後在找到下面的提示可以看到問題出現在了我們的 CameraController 文件中的第18行,我們順著找過去看到的是我們用來使我們的相機位置為 Player 位置加上一個固定值的代碼,找到了病因我們就去解決了。

腳本文件名:CameraController

代碼如下:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class CameraController : MonoBehaviour {

    public GameObject player;

    private Vector3 offset;

    private void Awake()
    {
        offset = transform.position - player.transform.position;
    }

    private void Update()
    {
        if (null != player)
        {
            transform.position = player.transform.position + offset;
        }
    }
}

解決方法很簡單就是將我們的相機位置代碼放在一個 if 判斷語句中就行了,判斷的條件就是我們的 Player 物體存在就行了。

總結

以上就是這篇文章中的所有內容了,因為本人還是新手所以在很多內容上理解可能很不準確或者錯誤的,希望各位朋友能像我提出,我一定會悉心學習並改正的,我也會努力讓自己與目標更加靠近的!!!

歡迎大家收藏我的博客,我會努力更新更多的作品的,給自己加個油!

Unity官方案例進階--Roll a ball