更新於 2024/06/05閱讀時間約 11 分鐘

2024 虛擬實境 x 人因設計 13Unity 保齡球遊戲拆解&製作

    我們完成了保齡球發射,再來來處理球瓶囉!!

    在這之前,我們在學一個程式語言中也算蠻重要的語法....,然後看看怎麼運用到遊戲裡面 :)

    For迴圈(loop)

    (好像會很麻煩,但我們還是痛苦一下才會進化><)


    這看的出來是操場嘛,迴圈的概念就是重複執行,像是繞著操場跑步一直做一樣的事情,通常用於對集合(例如清單、字串、元組等)中的每個元素進行迭代操作。

    蛤~迭代?

    #迭代(iteration)是重複回饋過程的活動,其目的通常是為了接近並且到達所需的目標或結果。每一次對過程的重複被稱為一次「迭代」,而每一次迭代得到的結果會被用來作為下一次迭代的初始值。


    常常看到的迴圈有兩種 While & For,這兩個差在哪邊?

    觀察兩種迴圈的結構如下:

    While迴圈:

    while (執行條件) {
    重複執行的程式們}

    For迴圈:

    for (宣告索引變數; 執行條件; 每次迭代索引變數的變化) {
    重複執行的程式們}

    可以發現最大的差異就在於有沒有宣告索引變數,而for loop因為宣告了索引變數,所以可以記錄目前跑了幾次迴圈,迴圈次數到了就可以跳出迴圈了。相對的While不須宣告索引變數,只要執行條件仍然滿足,就可以一直迴圈下去。因此,當迭代次數已知,也就是當你知道要跑幾次迴圈時,可使用for迴圈;而當只知道迴圈執行條件,而不清楚總共需跑幾次迴圈時,就只能使用While迴圈

    就好像狗狗繞圈圈 :

    如果要讓小狗繞10圈,可用for迴圈;如果要讓小狗一直跑到主人回到家,但不知道會跑幾圈時,就只能用while迴圈。

    程式碼示意如下(在這裡暫且以文字代替真正的程式碼,以著重在兩種迴圈的比較):

    • 跑十圈:
    for (let i = 1; i <= 10; i++) 
    {
    小狗繞一圈
    }
    • 一直跑到主人回到家:
    while (主人還沒到家)
    {
    小狗繞一圈
    }


    While迴圈也可完成for迴圈的任務

    但其實while迴圈也可達成for迴圈的任務,只要自行在「迴圈外」宣告索引變數」,並在「迴圈內」寫入每次迭代時索引變數的變化,就可達到同樣的效果,如下:

    let i = 1
    while ( i <= 10)
    {
    小狗繞一圈
    i++
    }

    兩種寫法主要的差異在於:

    1. 索引變數的有效範圍: for迴圈的索引變數只在迴圈內有效,而while的則在圈外也有效。
    2. 索引變數變化的位置: for迴圈一律在進入迴圈前;而while則可任意選擇其在迴圈內的位置。

    在舉個例:

    for 迴圈適合以下情境:

      • 當你知道需要迭代的確切次數時,for 迴圈是最適合的選擇。
      • 例如:瀏覽列表、字典、元組或範圍內的數值。
    fruits = ['apple', 'banana', 'cherry']
    for fruit in fruits:
    print(fruit)


    while 迴圈適合以下情境:

    迭代次數未知但需要根據條件迭代的情況

    • 當迭代次數不確定,並且需要根據某個條件來決定迭代是否繼續時,while 迴圈是最佳選擇。
    • 例如:等待用戶輸入正確的數據或處理數據直到特定條件滿足。
    user_input = ''
    while user_input.lower() != 'exit':
    user_input = input('Enter something (type "exit" to quit): ')
    print('You entered:', user_input)


    這樣看來,while迴圈好像有更多的彈性,但相對的也可考量: 索引變數在迴圈外還會需要使用嗎? 若在迴圈外不再需要使用,但在迴圈外仍有效的話,是否只是佔用了多餘的儲存空間,並讓變數的管理更為複雜呢 ?



    來練習一下

    for (初始變數; 判斷式; 遞增式)

    for (i = 0; i<j; i++)

    新增一個程式碼,可命名ForLoopMath ,先把他賦予在某物件上,這樣按Play鍵他才會執行。

    宣告兩個變數-開始整數 & 結束整數

    public int StartNumber = 0;

    public int EndNumber = 0;

    我們只執行一次,先把它寫在Void Start 裡面

    for (StartNumber = 0; StartNumber < EndNumber; StartNumber++)

    {

    print(StartNumber);

    }

    這是數列的練習,再試個字串(String)的試試~~

    宣告一個字串變數

    public string[] fruits = { "apple", "orange", "banana" };

    一樣只須執行一次所以寫在Start

    void Start()

    {

    // 使用 for 迴圈輸出字串列表的每個元素

    for (int i = 0; i < fruits.Length; i++)

    {

    print(fruits[i]);

    }

    }


    好,準備一個GameObject(保齡球球瓶),一個空物件(定位用),還有一個新腳本BowlingPinCreate

    打開新腳本後

    宣告三個公開變數:

    一個是球瓶數量 , 一個是創建球瓶的, 另一個是創建球瓶的起始位置

    public int totalBowlingBalls = 5;

    public GameObject bowlingBall;

    public Transform initialBallPosition;


    再來試著用for迴圈創建N個球瓶
    for (int i = 0; i < totalBowlingBalls; i++)

    {

    Vector3 ballPosition = Vector3.zero;

    // 如果有設置初始位置,則使用初始位置,否則使用計算的位置

    if (initialBallPosition != null)

    {

    ballPosition = initialBallPosition.position + new Vector3(i * 6, 0.5f, 0);

    }

    else

    {

    ballPosition = new Vector3(i * 6, 0.5f, 0);

    }

    Instantiate(bowlingBall, ballPosition, Quaternion.identity);

    }

    }

    之後這個腳本附加在地板或其他GameObject上,就是不要附在創建球瓶的上面

    (不然會很恐怖,Unity 會直接當掉)

    生完球瓶,那就來遊戲的重點啦,讓球瓶倒掉~~

    那我希望全部的球瓶倒掉我就贏了,那換句話說就是

    「我擊倒的球瓶數達到原本數量就贏了!」

    來囉~


    先為每個保齡球物件添加了一個名為 PinCollider 的腳本,用於追蹤保齡球的狀態。

    GameObject ball = Instantiate(bowlingBall, ballPosition, Quaternion.identity);

    // 添加 PinCollider 腳本以追蹤保齡球狀態

    ball.AddComponent<PinCollider>();

    不對阿,我們沒有PinCollider的腳本,所以要再建立一個,這個的內容是我們要看球瓶有沒有碰到地板。

    PinCollider腳本這樣寫

    private void OnCollisionEnter(Collision collision)
    {
    // 碰撞到地板時通知 Finish類別已擊倒了
    if (collision.gameObject.CompareTag("Finish"))
    {
    BowlingPinCreate game = FindObjectOfType<BowlingPinCreate>();
    if (game != null)
    {
    game.UpdateKnockedDownBalls();
    }
    }
    }

    找場景中的第一個 BowlingPinCreate 類別實例


    之後回去BowlingPinCreate腳本 新建 ​UpdateKnockedDownBalls()的方法

    新增一個私有變數 private int knockedDownBalls = 0; // 已擊倒的保齡球數量

     public void UpdateKnockedDownBalls()
    {
    knockedDownBalls++;

    // 檢查是否達到贏得遊戲的條件
    if (knockedDownBalls >= totalBowlingBalls)
    {
    Debug.Log("Congratulations! You win the game!");
    }
    else
    {
    Debug.Log("Knocked down balls: " + knockedDownBalls);
    }
    }

    就可以試著射球瓶看看~~


    ㄟ等等,他A球瓶如果碰地板兩次,會+2,這不是我們要的結果吧!正常是一球瓶倒只會+1,所以要回去改球瓶碰撞器的腳本PinCollider,讓他碰到地板一次後就不會再偵測了


    private bool hasCollided = false; // 標記是否已經計算過碰撞

    public class PinCollider : MonoBehaviour
    {
    private bool hasCollided = false; // 標記是否已經計算過碰撞

    private void OnCollisionEnter(Collision collision)
    {
    if (!hasCollided && collision.gameObject.CompareTag("Finish"))
    {
    BowlingPinCreate game = FindObjectOfType<BowlingPinCreate>();
    if (game != null)
    {
    game.UpdateKnockedDownBalls();
    }
    hasCollided = true; // 設置已經計算過碰撞
    }
    }
    }
    分享至
    成為作者繼續創作的動力吧!
    從 Google News 追蹤更多 vocus 的最新精選內容從 Google News 追蹤更多 vocus 的最新精選內容

    你可能也想看

    發表回應

    成為會員 後即可發表留言
    © 2024 vocus All rights reserved.