1. 程式人生 > >unity官方案例精講(第三章)--星際航行遊戲Space Shooter

unity官方案例精講(第三章)--星際航行遊戲Space Shooter

案例中實現的功能包括:

(1)鍵盤控制飛船的移動;

(2)發射子彈射擊目標

(3)隨機生成大量障礙物

(4)計分

(5)實現遊戲物件的生命週期管理

匯入的工程包中,包含著一個完整的 _scene---Main場景,建立一個全新場景,會在其中實現大部分功能

素材:
連結:https://pan.baidu.com/s/1-qFUYMjrvhfeOWThawJ-Hw 提取碼:bhr8

 

一、場景準備

1、建立飛船物件:

(1)從project面板中Assets/models/vechicle_playerShip到Hierarchy檢視,重新命名player。reset它的Transform元件。

(2)新增Rigidbody元件:用途是通過指令碼來為飛船新增作用力,此外不希望飛船受重力影響而下墜,取消Use Gravity選項。

(3)新增Mesh Collider元件:目的是使飛船能夠和隨機出現的障礙物發生隨機碰撞,並在碰撞後觸發銷燬飛船和障礙物的事件。此時Mesh Collider元件的Mesh屬性為模型vehicle_playerShip的網格,選中該網格模型,你可以看到在網格模型中包含了很多非常小的細小的三角面片。

由於上面的網格模型過於複雜,在進行碰撞檢測時可能需要消耗大量的計算資源,降低遊戲的執行效率,因此,沒有必要進行這麼精確的碰撞檢測,可以通過建模建立一個簡化的模型,減少不必要的碰撞計算。

為此選中同目錄下的vehicle_playerShip_colloder,展開後選擇對應的網格模型,將它拖動到Mesh Collider元件的Mesh屬性上。還需要勾選Convex和Is Trigger選項框,設定為觸發器。(Convex勾選複選框以啟用凸面。如果啟用,此網格碰撞器將與其他網格碰撞器碰撞。凸網格碰撞器限制為255個三角形)

其中勾選Convex(凸面)是unity新要求,否則執行會出現:Non-convex MeshCollider with non-kinematic Rigidbody is no longer supported since Unity 5.在前面新增剛體的時候,沒有勾選Is Kinematic選項,unity5中不再支援非Kinematic剛體的非Convex網格碰撞體)

(4)新增飛船尾部火焰粒子效果:在project面板中,Assets/Perfabs/VFX/Engines目錄下,將預製體engines_player拖動到Hierarchy檢視的Player物件上,成為player的子物件。

2、設定攝像機的引數

攝像機的投影方式(projection)為Orthography(正交投影),size為10,Clear Flags為Solid Color,background為黑色,其他設定為保留值。

(Clear Flags: 每個攝影機在渲染其檢視時儲存的顏色和深度資訊。螢幕中未繪製的部分為空,預設情況下將顯示skybox。使用多個攝影機時,每個攝影機在緩衝區中儲存自己的顏色和深度資訊,在每個攝影機渲染時累積更多資料。當場景中的任何特定攝影機渲染其檢視時,可以設定清除標誌以清除緩衝區資訊的不同集合。

skybox:這是預設設定。螢幕的任何空白部分都將顯示當前相機的天空盒。如果當前攝影機沒有設定“天空盒”(skybox) 

solid color:螢幕的任何空白部分都將顯示當前相機的背景色。

Depth only:如果要繪製玩家的槍而不讓其在環境中被剪輯,請將一個攝影機設定為深度0以繪製環境,並將另一個攝影機設定為深度1以單獨繪製武器。

Don't clear:此模式不清除顏色或深度緩衝區. 結果是,每個幀都會在下一幀上繪製,從而產生塗抹效果。這通常不用於遊戲,而且更可能與自定義著色器一起使用

注意,在某些GPU(主要是移動GPU)上,如果不清除螢幕,可能會導致下一幀中未定義螢幕內容。在某些系統中,螢幕可以包含前一幀影象、實心黑螢幕或隨機彩色畫素

 3、新增背景圖片

(1)建立一個Quad面片,重新命名為background,移除Mesh Collider元件,在Assets/Textures中選擇tile_nubula_green_dff,將其拖動到background上,(此圖片的尺寸是1024*2048,寬高比為1:2,為了防止圖片被拉伸失真,在放大是需要遵循這個比例。)設定其Transform元件。紋理的shader設定為Unlit/Textures。

4、新增粒子背景效果

在真實的是空中應該是繁星點點,所以要新增粒子背景效果,讓星空背景更貼近逼真

(1)在Assets/Prefabs/VFX/Starfield目錄下,拖動預製體StarField到Hierarchy面板上,保留Transform元件屬性的預設值,由於Y值為-5,高於background的(-10),所以不會被background擋住。

(2)展開StarField可以看到兩個子物件,其中part_StarFied用於生成較大的粒子效果,另外一個生成較小的粒子效果。在子物件中,你會發現一個粒子系統元件(Particle System)

 二、編寫指令碼程式碼

1、鍵盤控制飛船移動的操作

(1)在Assets中建立資料夾Scripts,在Scripts中建立PlayerController.cs指令碼,由於需要處理剛體元件的物體特效,我們在此過載事件函式FixedUpdate,並且在其中新增如下程式碼:

    void FixedUpdate()
    {
        //得到水平和豎直方向的輸入
        float moveHorizontal = Input.GetAxis("Horizontal");
        float moveVertical = Input.GetAxis("Vertical");

        //利用上面得到的水平和豎直方向的輸入建立一個vector3,作為剛體速度
        Vector3 movement = new Vector3(moveHorizontal, 0.0f, moveVertical);
        G

(2)繫結指令碼到player物件,直接選中指令碼,將其拖動到player上

(3)運行遊戲,有三個問題:

  • 飛船的移動速度過慢
  • 沒有對player做範圍限制,飛船可以移動到螢幕外
  • 左右移動飛船的時候,飛船沒有側翻效果

(4)解決上面問題,新增一個控制速度變數,建立一個public型別的變數speed

(5)新增限制物件運動範圍的程式碼:

由於此場景飛機的活動範圍是在xz平面上的,需要限制player的位置在有效的活動範圍內,由background決定其xz的座標值

  • 在指令碼中建立一個Boundary類用於管理飛船活動的範圍,在PlayerController類中新增一個Boundary的例項。訪問許可權是public
public class Boundary
{
    public float xMin, xMax, zMin, zMax;
}
public class Player_Control : MonoBehaviour
{
    public float speed;//速度
    public Boundary1 boundary;
  • 要將一個物體限制在一個範圍內,可以使用unity提供的Mathf.Clamp函式來實現:該函式若value的值小於min,則返回min;若value大於max,則返回max。於是可以在FixedUpdate中限定
static float Clamp(float value,float min,float max);
  •  在player面板上,並沒有看到boundary變量出現,需要為Boundary類新增可序列化的屬性 
[System.Serializable]
public class Boundary1
{
    public float xMin, xMax, zMin, zMax;
}
  • 運行遊戲,尋找臨界值。此時FixedUpdate函式的程式碼
 void FixedUpdate()
    {
        //得到水平和豎直方向的輸入
        float moveHorizontal = Input.GetAxis("Horizontal");
        float moveVertical = Input.GetAxis("Vertical");

        //利用上面得到的水平和豎直方向的輸入建立一個vector3,作為剛體速度
        Vector3 movement = new Vector3(moveHorizontal, 0.0f, moveVertical);
        Rigidbody rb =  GetComponent<Rigidbody>();

        if(rb != null)
        {
            rb.velocity = movement * speed;
            rb.position = new Vector3(Mathf.Clamp(rb.position.x, boundary1.xMin, boundary1.xMax), 0.0f,
                Mathf.Clamp(rb.position.z, boundary1.zMin, boundary1.zMax));
        }
    }

 (6)新增移動時旋轉的效果

  • 要是想飛船左右移動時,以一定的角度傾斜,需要在改變飛船位置的同時更新飛船的Rotation屬性:在PlayerController類中新增一個傾斜係數tilt,設定預設值為4.0f.
  • 在FixedUpdate函式中新增下面的語句
rb.rotation = Quaternion.Euler(0.0f, 0.0f, rb.velocity.x * -tilt);
  • 函式Euler()是Quaternion的一個靜態方法,接收繞XYZ軸的旋轉角度為引數,並返回一個Quaternion物件。若飛船左右傾斜,則需要繞z軸旋轉,往左移動的時候,x軸方向上速度為負值,而此時旋轉角度(逆時針)應該為正值,所以需要乘以一個負數。

 此時完整的PlayerController指令碼程式碼:

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

[System.Serializable]
public class Boundary
{
    public float xMin, xMax, zMin, zMax;
}
public class Player_Control : MonoBehaviour
{
    public float speed;//速度
    public Boundary boundary;
    public float tilt = 4.0f;

    void FixedUpdate()
    {
        //得到水平和豎直方向的輸入
        float moveHorizontal = Input.GetAxis("Horizontal");
        float moveVertical = Input.GetAxis("Vertical");

        //利用上面得到的水平和豎直方向的輸入建立一個vector3,作為剛體速度
        Vector3 movement = new Vector3(moveHorizontal, 0.0f, moveVertical);
        Rigidbody rb =  GetComponent<Rigidbody>();

        if(rb != null)
        {
            rb.velocity = movement * speed;
            rb.position = new Vector3(Mathf.Clamp(rb.position.x, boundary.xMin, boundary.xMax), 0.0f,
                Mathf.Clamp(rb.position.z, boundary.zMin, boundary.zMax));
            rb.rotation = Quaternion.Euler(0.0f, 0.0f, rb.velocity.x * -tilt);
        }
    }
}

 三、實現射擊行為

1、建立電光子彈

(1)新建一個空遊戲物件,命名為Bolt,重置其Transform元件,為了防止Player遮擋Bolt,可暫時將player隱藏,然後為Bolt新增一個Rigidbody元件,並取消勾選Use Gravity。

(2)建立一個Quad,命名為VFX,將其設為Bolt的子物件,重置Transform元件,Rotation的屬性值(90,0,0),移除Mesh collider元件

(3)將Assets/Materials目錄下的fx_bolt_orange拖動到VFX上

(4)為Bolt新增一個Capsule Collider元件,勾選Is Trigger選項框,設定為一個觸發器(注意這裡的Capsule Collider元件只能放到Bolt上,不能放到子物件上,不然無法銷燬Bolt物件,然後設定Capsule Collider的direction屬性值為Y-Aixs,並設定radius為0.04,Height為0.65)

(5)新建一個Mover.cs繫結到Bolt上

 public float speed=20.0f;
    // Start is called before the first frame update
    void Start()
    {
        GetComponent<Rigidbody>().velocity = Vector3.forward * speed;
    }

(6)建立目錄Perfabs,用來儲存預製體,將Blot製作成一個預製體,建好之後,刪除Hierarchy檢視中的Bolt

(7)兩個問題:不能通過鍵盤和滑鼠發射,子彈不會自己消失或者銷燬,數量巨大的子彈必定消耗非常多的系統資源,嚴重影響遊戲的效能

2、用指令碼控制發射子彈

(1)為player建立一個空的子物件shot spawn ,這是發射子彈的位置,position的值為(0,0,0.7),位置可以自己調整

(2)為了實現fire1觸發後即刻例項化Bolt預製體,需要:

  • 儲存傳入的Bolt遊戲物件,作為Instantiate的第一個引數
  • 儲存發射器的位置,作為例項化Bolt的位置
  • 設定一定的發射頻率,只有間隔時間到了之後才能繼續發射

(3)在PlayerController中書寫程式碼

    public float fireRate = 0.5f;//發射的間隔時間,預設是0.5秒
    public GameObject shot; //shot表示的是Bolt預製體
    public Transform shotSpawn;//子彈發射的位置
    private float nextFire = 0.0f;//表示下次可以發射的最早時間(發射時間應該大於此值)從0開始

    private void Update()
    {
        if(Input.GetButton("Fire1") && Time.time > nextFire){
            nextFire = Time.time + fireRate;
            Instantiate(shot, shotSpawn.position,Quaternion.identity);
        }
    }

3、管理子彈的宣告週期

我們想要子彈飛出有效的遊戲區域後自行銷燬,因此可以為遊戲區域增加觸發器,當飛出的時候,在事件響應中呼叫Destroy方法

(1)建立一個Cube,重新命名Boundary,重置Transform元件,設定數值,由於不用顯示移除Mesh Renderer元件,

 (2)建立指令碼DestroyByBoundary.cs在其中新增響應的處理事件,OnTriggerExit,將其拖動到Boundary物件上。

   private void OnTriggerExit(Collider other)
    {
        Destroy(other.gameObject);
    }

四、新增小行星(Asteroid)

接下來可以在場景中新增小行星物件,實現的目標是:

  • 小行星隨機產生,且應該以隨機的角度旋轉
  • 當飛船發射子彈擊中小行星時,小行星會爆照並且銷燬
  • 若飛船碰撞到小行星,則飛船爆炸,遊戲結束

1、建立小行星物件

(1)建立空物件,重新命名為Asteroid,重置其Transform元件,設定position(0,0,10),新增Rigidbody元件,取消Use Gravity選項,將Angular Drag 設定為0;新增capsule collider元件,勾選Is Trigger選項。

(2)從Assets/Models拖動prop_asteroid_01到Asteroid物件上。成為Asteroid的子物件

(3)為了使碰撞體更接近模型的幾何體形狀,選中設定碰撞體的屬性值Radius的值為0.5,Height的值為1.6,Direction為Z軸

2、新增控制小行星隨機旋轉的功能

(1)建立指令碼RandomRotator.cs並且繫結到Asteroid物件上。

    public float tumble = 10.0f;//小行星的旋轉系數
    // Start is called before the first frame update
    void Start()
    {
        //設定剛體的角速度,角速度是描述做圓周運動的物體,單位時間旋轉的角度
        //Random.insideUnitSphere表示單位長度半徑球體內的一個隨機點(向量)
        //記住將剛體的角阻力設定為0,不然會越轉越慢(物體旋轉是所受到的空氣阻力)
        GetComponent<Rigidbody>().angularVelocity = Random.insideUnitSphere * tumble;
    }

3、新增控制射擊小行星的功能

子彈射中小行星,二者會消失;飛船與小行星發生碰撞,二者會消失

(1)新建一個指令碼DestroyByContact.cs,並且繫結的Asteroid物件上

(2)小行星在Boundary中,如果寫直接寫銷燬程式碼,遊戲一開始就會把小行星和Boundary銷燬,所以要進行碰撞體檢測,若是與Boundary碰撞不銷燬,與其他的物件則執行銷燬程式碼,方法之一是比較物件的Tag屬性,設定Boundary的Tag為Boundary。

(3)新增程式碼

public class DestroyBy_Contact : MonoBehaviour
{
    private void OnTriggerEnter(Collider other)
    {
        if(other.tag == "Boundary")
        {
            return;
        }
        Destroy(other.gameObject);
        Destroy(gameObject);
    }
}

4、新增小行星爆炸效果

(1)在指令碼DestroyByContact中新增兩個變數

public GameObject explosion;//小行星的爆炸粒子效果物件
public GameObject playerExplosion;//飛船爆炸的粒子效果物件

(2)在碰撞函式中新增例項化粒子效果的程式碼

//例項化爆炸效果
        Instantiate(explosion, transform.position, transform.rotation);
        if(other.tag == "Player")
        {
            Instantiate(playerExplosion, other.transform.position, other.transform.rotation);
        }

(3)在Assets/prefabs/VFX目錄下拖動explosion_asteroid到變數explosion上,explosion_player到變數playerExplosion上

5、新增小行星移動的功能

(1)將Mover.cs指令碼拖動到Asteroid上,設定Speed的值為-5,使小行星向與子彈運動方向相反的方向執行

6、新增小行星隨機產生的邏輯功能

在新增隨機產生小行星的邏輯功能之前,需要先製作Asteroid預製體

(1)將Asteroid拖動到Prefabs中,然後在hierarchy面板中刪除

(2)建立一個空物件,重新命名為GameController,重置其Transform元件,設定Tag為GameController

(3)建立GameController.cs指令碼,並且拖動到GameController上

    public GameObject hazard;//準備例項化的障礙物物件
    public Vector3 spawnValues;//設定為(6,0,14.5)
    private Vector3 spawnPosition = Vector3.zero;//例項化時的位置
    private Quaternion spawnRotation;//例項化時的旋轉

    //用於在
    void SpawnWaves()
    {
        //x在這個範圍之間
        spawnPosition.x = Random.Range(-spawnValues.x, spawnValues.x);
        spawnPosition.z = spawnValues.z;
        spawnRotation = Quaternion.identity;
        Instantiate(hazard, spawnPosition, spawnRotation);
    }
    // Start is called before the first frame update
    void Start()
    {
        SpawnWaves();
    }

 (4)將小行星預製體拖拽給hazard,spawnValues設定為(6,0,14.5)

(5)執行會發現隨機位置生成

7、新增小行星批量產生的功能

(1)在GameController指令碼中新增變數hazardCount,表示障礙物的數量

(2)修改SpawnWaves中的程式碼

   public int spawnCount;//生成小行星的數量
    //用於生成小行星
    void SpawnWaves()
    {
        for (int i = 0; i < spawnCount; i++)
        {
            //x在這個範圍之間
            spawnPosition.x = Random.Range(-spawnValues.x, spawnValues.x);
            spawnPosition.z = spawnValues.z;
            spawnRotation = Quaternion.identity;
            Instantiate(hazard, spawnPosition, spawnRotation);
        }
        
    }

(3)設定數量為10,這樣的話,,生成的小行星之間會互相碰撞銷燬,為了解決這個問題,可以在每次生成一個小行星後等待一段時間,unity中提供協程類WaitForSeconds可以實現這樣的功能

(4)再新增一個變數spawnWait,使用協程方法,修改函式。並且修改呼叫方法,設定變數的是為0.5

(5)由於不想一開始就生成小行星,可以在設定一個變數startWait,在for迴圈的上面新增一段程式碼,儲存,設定startwait為1

 (6)如果想不斷的產生多波小行星,可以新增一個變數waveWait,表示兩波之間的時間間隔,寫個無限迴圈,將for包進去,並且加上延遲waveWait

    public GameObject hazard;//準備例項化的障礙物物件
    public Vector3 spawnValues;//
    private Vector3 spawnPosition = Vector3.zero;//例項化時的位置
    private Quaternion spawnRotation;//例項化時的旋轉

    public int spawnCount;//生成小行星的數量
    public float spawnWait;//設定產生小行星的時間間隔

    public float startWait;//設定等待時間,之後產生小行星
    public float waveWait;//兩波小行星之間的時間間隔
    //用於生成小行星
    IEnumerator SpawnWaves()
    {
        //等待startWait秒之後生成行星
        yield return new WaitForSeconds(startWait);
        //不斷產生行星
        while (true)
        {
            for (int i = 0; i < spawnCount; i++)
            {
                //x在這個範圍之間
                spawnPosition.x = Random.Range(-spawnValues.x, spawnValues.x);
                spawnPosition.z = spawnValues.z;
                spawnRotation = Quaternion.identity;
                Instantiate(hazard, spawnPosition, spawnRotation);
                //生成每個行星的時間間隔
                yield return new WaitForSeconds(spawnWait);
            }
            //兩波波行星生成的時間間隔
            yield return new WaitForSeconds(waveWait);
        }
              
    }
    // Start is called before the first frame update
    void Start()
    {
        StartCoroutine(SpawnWaves());
        
    }

(7)設定waveWait的值為2,運行遊戲,發現可以不斷的生成小行星,但是發現擊中小行星幾次後,爆炸粒子效果explosion_asteroid沒有自動銷燬,隨著遊戲的進行,嚴重的影響了遊戲的美觀和效率。

(8)新建一個指令碼DestroyByTime.cs並且繫結到粒子效果上面。

public class DestrtroyByTime : MonoBehaviour
{
    //表示的是粒子的宣告週期預設2秒
    public float lifeTime = 2.0f;

    // Start is called before the first frame update
    void Start()
    {
        //在lifeTime秒之後銷燬物體
        Destroy(gameObject, lifeTime);
    }
}

(9)運行遊戲,已經ok了

五、新增遊戲音訊

1、新增碰撞爆炸音訊

(1)將project檢視變成單列布局,兩列的不好弄

(2)將Assets/Audio中將對應的音訊檔案拖動到Assets/VFX/Explosions中預製體物件上。確保Play On Awake選項勾選

2、新增飛船射擊音效

(1)將音訊檔案拖動到player上,取消勾選Play On Awake選項,不然一開始就會響

(2)在PlayerController指令碼中新增以下程式碼,執行發射子彈就可以聽到聲音

 if(Input.GetButton("Fire1") && Time.time > nextFire){
            ...............//呼叫audiosource類中成員函式Play來播放聲音
            GetComponent<AudioSource>().Play();
        }

 3、新增背景音效

理論上,背景音樂可以放到場景中任意一個處於活動狀態的遊戲物件上,這裡選擇的是在GameController上

上面講直接拖動音訊檔案到目標物件的方法新增音訊,簡介高效。但不利於讀者理解unity管理音訊的過程,下面採用另外一種方法來新增音訊。

(1)在GameController上新增一個AudioSource元件,此時Audio Clip屬性為空。

(2)講背景音樂拖動到Audio Clip中,這樣就可以繫結到GameController上了

(3)由於背景音樂從遊戲開始連續不斷的播放,所以Play On Awake和Loop都要勾選上

六、新增計分文字

(1)建立Text,會自動新增一個 Canvas父物件和EventSystem物件,重新命名Text為Score Text,Text元件中的Text屬性輸入:得分

(2)將其放到場景的左上角:

(3)新增計分功能;在GameController中新增兩個變數:之後再建立函式並進行初始化

public Text scoreText;//Text元件
private int score;//分數
void Start()
    {
        //初始化分數和Text元件
        score = 0;
        updateScore();
        StartCoroutine(SpawnWaves());        
    }
    //建立一個增加和更新分數的元件
    public void AddScore(int newScoreValue)
    {
        score += newScoreValue;
        updateScore();
    }
    private void updateScore()
    {
        scoreText.text = "得分:" + score;
    }
}

 (4)在DestroyByContact指令碼中加入變數

public int scoreValue;//設定小行星的分數
private GameController gameController;//建立一個GameController類的變數

 (5)在小行星碰撞事件函式中OnTriggerEnter中新增分值更新語句

//增加分數
gameController.AddScore(scoreValue);

(6)在函式start中初始化變gameController,我們不能直接得到GameController指令碼,需要找到GameController物件,在得到繫結在上面的GameController指令碼

    private void Start()
    {
        GameObject go = GameObject.FindWithTag("GameController");
        if(go != null)
        {
            gameController = go.GetComponent<GameController>();
        }
        else
        {
            Debug.Log("找不到tag為GameController的物件");
        }
        if(gameController == null)
        {
            Debug.Log("找不到為GameController指令碼");
        }
    }

(7)在GameController物件中將Score Text拖進去,在Asteroid預製體中設定分數為10

七、遊戲結束與重新開始

當飛船銷燬後,遊戲應該結束,並且使用者能夠選擇重新開始遊戲

1、設定遊戲結束的文字,建立Text 設定遊戲結束的字型,居中顯示

2、新增遊戲結束的功能

(1)開啟指令碼GameController指令碼,新增變數

public Text gameOverText;//遊戲結束顯示的文字
public bool gameOver;//遊戲是否結束的標誌

(2)在Start中賦值,遊戲開始時應該清除文字

        //遊戲剛開始,文字清除,同時設定gameOver為false
        gameOverText.text = "";
        gameOver = false;    

(3)在指令碼中新增一個GameOver函式,用來表示遊戲的結束

    public void GameOver()
    {
        gameOver = true;
        gameOverText.text = "遊戲結束";   
    }

(4)在SpawnWaves中,當gameOver為true時,應該跳出while 迴圈

     //不斷產生行星
        while (true)
        {
            //如果遊戲結束,跳出迴圈
            if (gameOver)
            {
                break;
            } 

(5)將場景中的遊戲結束的文字,拖拽給gameOverText變數,unity會自動的賦值

(6)開啟指令碼DestroyByContact,當小行星碰撞的是player物件的時候,遊戲結束(注意檢查player的Tag是不是設定成了Player)

if (other.tag == "Player")
        {
        ............. //呼叫遊戲結束的函式 gameController.GameOver(); }

(7)運行遊戲,當飛船與小行星碰撞後,遊戲結束

3、重新開始遊戲

1、建立一個Text,重新命名restartText,拖動選擇好合適的位置,Text屬性寫: 按下【R】鍵重新開始,調整好大小

2、新增重新開始的程式碼

(1)開啟指令碼GameController指令碼,新增變數

    public Text restartText;//重新開始的文字
    private bool restart;//遊戲是否從新開始的標誌

(2)在Start中賦值,遊戲開始時應該清除文字

    //遊戲開始,文字清除,同時設定restart為false
    restartText.text = "";
    restart = false;

(3)在SpawnWaves函式中,當遊戲結束時,新增程式碼

       //如果遊戲結束,跳出迴圈
            if (gameOver)
            {
                restartText.text = "按下【R】鍵重新開始";
                restart = true;
                break;

(4)在Update函式中,新增程式碼

 private void Update()
    {
        if (restart)
        {
            if (Input.GetKeyDown(KeyCode.R))
            {
                //Application.LoadLevel(Application.loadedLevel);已經棄用
                SceneManager.LoadScene("Space_Shooter");//小括號裡可以填寫場景的名字
            }
        }
    }

新手上路可以一起交流哦!

&n