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

閱讀時間約 11 分鐘

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

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

For迴圈(loop)

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


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

raw-image

蛤~迭代?

#迭代(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);

}

raw-image

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

宣告一個字串變數

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

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

void Start()

{

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

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

{

print(fruits[i]);

}

}

raw-image

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

打開新腳本後

宣告三個公開變數:

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

public int totalBowlingBalls = 5;

public GameObject bowlingBall;

public Transform initialBallPosition;

raw-image


再來試著用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);

}

}

raw-image

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

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

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

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

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

來囉~


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

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

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

ball.AddComponent<PinCollider>();

raw-image

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

PinCollider腳本這樣寫

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

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


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

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

raw-image
 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; // 設置已經計算過碰撞
}
}
}
    4會員
    20內容數
    Gavin Hsieh ㄉ基地
    留言0
    查看全部
    發表第一個留言支持創作者!
    你可能也想看
    Google News 追蹤
    Thumbnail
    這個秋,Chill 嗨嗨!穿搭美美去賞楓,裝備款款去露營⋯⋯你的秋天怎麼過?秋日 To Do List 等你分享! 秋季全站徵文,我們準備了五個創作主題,參賽還有機會獲得「火烤兩用鍋」,一起來看看如何參加吧~
    Thumbnail
    美國總統大選只剩下三天, 我們觀察一整週民調與金融市場的變化(包含賭局), 到本週五下午3:00前為止, 誰是美國總統幾乎大概可以猜到60-70%的機率, 本篇文章就是以大選結局為主軸來討論近期甚至到未來四年美股可能的改變
    Thumbnail
    這個秋,Chill 嗨嗨!穿搭美美去賞楓,裝備款款去露營⋯⋯你的秋天怎麼過?秋日 To Do List 等你分享! 秋季全站徵文,我們準備了五個創作主題,參賽還有機會獲得「火烤兩用鍋」,一起來看看如何參加吧~
    Thumbnail
    美國總統大選只剩下三天, 我們觀察一整週民調與金融市場的變化(包含賭局), 到本週五下午3:00前為止, 誰是美國總統幾乎大概可以猜到60-70%的機率, 本篇文章就是以大選結局為主軸來討論近期甚至到未來四年美股可能的改變