場景中可能會有一條基準線或是目標線,遊戲的目的就是要讓所有的物件都到達目標線,另外有一種是並非所有物件都用同一條目標線,而是每個物件需求的高度都不同。
製作使用的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