我們完成了保齡球發射,再來來處理球瓶囉!!
在這之前,我們在學一個程式語言中也算蠻重要的語法....,然後看看怎麼運用到遊戲裡面 :)
這看的出來是操場嘛,迴圈的概念就是重複執行,像是繞著操場跑步一直做一樣的事情,通常用於對集合(例如清單、字串、元組等)中的每個元素進行迭代操作。
蛤~迭代?
#迭代(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迴圈的任務,只要自行在「迴圈外」宣告索引變數」,並在「迴圈內」寫入每次迭代時索引變數的變化,就可達到同樣的效果,如下:
let i = 1
while ( i <= 10)
{
小狗繞一圈
i++
}
兩種寫法主要的差異在於:
在舉個例:
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迴圈好像有更多的彈性,但相對的也可考量: 索引變數在迴圈外還會需要使用嗎? 若在迴圈外不再需要使用,但在迴圈外仍有效的話,是否只是佔用了多餘的儲存空間,並讓變數的管理更為複雜呢 ?
來練習一下
新增一個程式碼,可命名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; // 設置已經計算過碰撞
}
}
}