場景中可能會有一條基準線或是目標線,遊戲的目的就是要讓所有的物件都到達目標線,另外有一種是並非所有物件都用同一條目標線,而是每個物件需求的高度都不同。
製作使用的Unity版本為5.4.1 f1。
實作測試(WebGL build) (滑鼠點擊每個石柱的上下按鈕讓該石柱移動,這個時候有連動的石柱也會跟著移動,當所有石柱的頂端都位在紅色線段的時候,就遊戲結束)
1、開關
先來製作一個開關的圖片,如果只是單純的開關只有兩種切換感覺有點單調,所以這邊改用Slider類型的,然後圖片使用了石柱的圖片來代替滑桿。
把圖片去背匯入到Unity裡面,我這邊大致上排列了一下石柱跟背後的線段,順序的話看自己想怎麼排了,照順序只是比較方便記而已。
所有的石柱都對齊某個基準線,這個線段就是作為玩家完成遊戲的判斷,這邊偷懶就全部都共用同一條線,並且石柱圖片的Y軸LocalPosition都在0的位置,之後也方便計算。
2、板子
開始來製作板子Component,同樣這次也是讓所有的判斷都寫在這裡,UI點擊按鈕後呼叫裡面的移動方法,然後去移動特定的石柱圖片到正確的位置。
public class Board : MonoBehaviour
{
public SpriteRenderer[] pillars; //石柱圖片
public float speed = 2f; //石柱移動的速度
private int[][] linkedPillars; //每個石柱跟其他石柱連動的參數
private int[] pillarPositions; //石柱當前的位置
private bool isMoving = false;
void Start() {
InitGame();
}
public void InitGame()
{
//初始化連動參數 (這邊是亂數決定每個石柱跟其他石柱的連動參數)
//可以的話最好是預先決定好那些按鈕會跟那些連動,比較不會因為隨機造成每次難度不一
int pillarCount = pillars.Length;
pillarPositions = new int[pillarCount];
linkedPillars = new int[pillarCount][];
for(int y = 0; y < pillarCount; y++) {
linkedPillars[y] = new int[pillarCount];
for (int x = 0; x < pillarCount; x++) {
linkedPillars[y][x] = Random.Range(-2, 3); //-2 ~ +2 步數
}
}
//隨機設定版面初始狀態
int randomMoves = Random.Range(5, 11); //隨機移動幾次
for(int i = 0; i < randomMoves; ++i) {
int randomPillarIndex = Random.Range(0, pillarCount); //隨機移動某個石柱
int randomMoveDirection = Random.Range(-2, 3); //隨機移往某個方向
CalculatePillarsPosition(randomPillarIndex, randomMoveDirection); //計算其他石柱的連動後的正確位置
}
//數值設定結束,設定石柱的圖片到正確的位置
for(int i = 0; i < pillars.Length; ++i) {
Vector3 pos = pillars[i].transform.localPosition;
pos.y = pillarPositions[i] * 1.5f;
pillars[i].transform.localPosition = pos;
}
}
//給UI按鈕呼叫,把編號N的石柱往上移動一個單位
public void MovePillarUp(int index)
{
if (isMoving) return;
CalculatePillarsPosition(index, 1);
StartCoroutine(MovingPillarCoroutine());
}
//給UI按鈕呼叫,把編號N的石柱往下移動一個單位
public void MovePillarDown(int index)
{
if (isMoving) return;
CalculatePillarsPosition(index, -1);
StartCoroutine(MovingPillarCoroutine());
}
//移動目標石柱後,依據連動參數來計算所有其他石柱調整後的位置
private void CalculatePillarsPosition(int pillarIndex, int direction)
{
int[] linkedSteps = linkedPillars[pillarIndex]; //取得這個石柱的連動參數
for (int i = 0; i < linkedSteps.Length; ++i)
{
//如果i是主要移動的石柱,就移動direction的方向單位
//如果是其他石柱,則計算連動,連動數值正值移動方向跟目前移動的pillar同方向,負值則是反方向
int move = (i == pillarIndex) ? direction : (linkedSteps[i] > 0) ? Mathf.Abs(linkedSteps[i]) * direction : linkedSteps[i] * direction;
pillarPositions[i] = Mathf.Clamp(pillarPositions[i] + move, -2, 2);
}
}
private IEnumerator MovingPillarCoroutine()
{
//因為我所有pillar的Y軸localPosition都是0,而我每一格線段的距離是1.5個單位
//因此就可以直接使用pillarPositions裡面的數值乘上1.5,就是那個pillar object應該要在的Y軸座標
isMoving = true;
while (true)
{
bool isFinish = true;
for(int i = 0; i < pillars.Length; ++i)
{
//移動圖片到目標位置
Vector3 endPos = new Vector3(pillars[i].transform.localPosition.x, pillarPositions[i] * 1.5f, pillars[i].transform.localPosition.z);
pillars[i].transform.localPosition = Vector3.MoveTowards(pillars[i].transform.localPosition, endPos, 1.5f * speed * Time.deltaTime);
if (Vector3.Distance(pillars[i].transform.localPosition, endPos) < 0.1f)
{
pillars[i].transform.localPosition = endPos;
}
else
{
isFinish = false; //只要有一個圖片還沒移動到定點就繼續跑
}
}
if (isFinish) break;
yield return null;
}
//因為目標紅線位置在pillarPositions裡面的數值是0,所以遊戲是不是結束檢查是否都是0就好了,因為有正負所以全部轉正後加總
if (pillarPositions.Sum(x => Mathf.Abs(x)) == 0)
{
Debug.Log("Game over");
}
isMoving = false;
}
}
3、參數設定
這邊我使用int[][]來記錄每個石柱跟其他石柱的連動參數,因為整個開關群組在畫面中剛好是一整排,用這樣的方式也好記每個石柱在陣列中的位置。
經過初始化後,int[0][]就是第一個石柱的連動參數,x的位置是他在他自己參數陣列中的位置,基本上在移動的時候會略過這個參數,並竟是要去連動其他的石柱的。
接著再順便亂數設定參數linkedPillars[y][x] = Random.Range(-2, 3);,使用-2到+2的整數只是因為我這邊用了五條位置線,中間紅色的是0點,那麼上下就是各兩個單位了。
移動石柱就呼叫MovePillarUp(index)跟MovePillarDown(index),傳送的index當然就是石柱的順序從左到右是0~4了,我這邊偷懶就直接在每個石柱的上下各放一個Button,去呼叫讓那個石柱往上或往下移動而已。
最後把Board隨便放到一個物件上,然後把場景上的石柱圖片拉進到pillars[]裡面,這邊我也是一樣照順序從左到右1~5,到這邊就差不多完成了,接著記得製作一個UI介面並且配置好按鈕來呼叫MovePillarUp(index)跟MovePillarDown(index) ,同樣製作UI就不在這邊說明了。
這種連動型的機關也滿有趣的,之後再來做一個變形的連動開關好了。有時候這種機關在遊戲中可能不會單獨出現,會和其他類型的謎題合併在一起來增加難度,這邊會跟那些連動是亂數設定參數,最好還是固定一種或幾種配置,同時遊戲開場石柱的位置也一樣固定一種或幾種版面,不然會不好掌控難易度。
如果有任何錯誤歡迎提出。




No comments:
Post a Comment