這個題目也不局限於5公升跟3公升的水瓶,不過這邊就用最簡單的這兩個單位來製作,基本原理是一樣的,差別在於達到需求水量所需的步驟數不同。
製作使用的Unity版本為5.2.2f1。
實作測試 (WebGL build,似乎會花點時間載入),滑鼠左鍵點中間兩個燒杯可以拿取,滑鼠右鍵會放下燒杯,拿取燒杯的情況下點右邊的水可以裝滿,點左下角的水槽可以倒掉,點另一個燒杯可以倒過去,左上角的是目標水量燒杯,只有在燒杯水量符合才能倒進去
1、物件Tags
因為這次依靠物件的Tag來作為判斷,所以需要增加幾個Tag來使用。
這邊Infinity用在無窮水源的物件上,Target用在目標水盆,Sink是倒掉水的物件,Beaker是燒杯物件。
2、燒杯
接著從燒杯的部分開始,先做一個燒杯的Component來使用,這個Component紀錄燒杯當前的水量,以及這個燒杯可以裝的最大水量,如此之外就是增減水量的方法、調整水圖片的方法。
public class Beaker : MonoBehaviour { public int maxWater; //燒杯最大水位 public int currentWater; //目前水位 private Vector3 startPosition; //燒杯起始位置 void Start() { //開始時紀錄起始位置同時重設水量圖片 ResetWaterImage(); startPosition = this.transform.position; } //填滿或清空水量 public void AddWater(bool isFill) { currentWater = (isFill) ? maxWater : 0; ResetWaterImage(); } //倒入另一個燒杯水量 public void AddWater(Beaker other) { int waterSpace = maxWater - currentWater; //還可以容納的水量 if (waterSpace == 0) return; //已經滿了 //可以容納的水量大於等於加入的水 if (waterSpace >= other.currentWater) { AddWater(other.currentWater); other.AddWater(false); } else { AddWater(waterSpace); other.AddWater(-waterSpace); } } //倒入水量 public void AddWater(int value) { currentWater = Mathf.Clamp(currentWater + value, 0, maxWater); ResetWaterImage(); } //回到初始位置 public void ReturnStartPosition() { StartCoroutine("ReturnPosition", startPosition); } //隨意製作的移動方法 IEnumerator ReturnPosition(Vector3 pos) { Vector3 target = new Vector3(pos.x, pos.y, this.transform.position.z); while (true) { this.transform.position = Vector3.MoveTowards(this.transform.position, target, 10f * Time.deltaTime); if (this.transform.position == target) break; yield return null; } } //只在水量有變動的時候呼叫,調整水量圖,可以加入自己的介面或物件等公式 public void ResetWaterImage() { Debug.Log("Reset Image"); } }
接著開始在場景中擺放幾個Sprite物件,這邊我總共放個5個Sprite物件,每個都加上一個Box Collider 2D,接著把每個物件的Tag都設定好。
把每個Sprite物件的Tag都設定完畢後,在中間兩個燒瓶物件加上Beaker這個Component,然後設定好最大水量的數值。
最後就是WaterTarget這個物件也加上Beaker這一個Component,單純只是廢物利用而已,使用Beaker的MaxWater來做為目標水量的判斷,所以這邊設定一個想到達成的水量,不過這邊因為我在判斷上是只有完全符合水量才能倒進去,無法一次倒一點累積,所以目標水量要避免超過場上最大的燒杯。
3、板子
最後做一個板子物件,這邊偷懶直接讓所有的動作都在這Component裡面做判斷,這個只要放在場上隨意一個物件就可以。
public class Board : MonoBehaviour { private Beaker dragBeaker; void Update() { //如果有拾取燒杯,就讓燒杯跟著滑鼠 if (dragBeaker != null) { Vector2 pos = Camera.main.ScreenToWorldPoint(Input.mousePosition); dragBeaker.transform.position = pos; } //滑鼠點左鍵,檢查看是要拿取燒杯或是做動作 if (Input.GetKeyDown(KeyCode.Mouse0)) { RaycastHit2D[] hits = Physics2D.RaycastAll(Camera.main.ScreenToWorldPoint(Input.mousePosition), Vector2.zero); foreach (RaycastHit2D hit in hits) { if (dragBeaker == null) //沒有拿取燒杯的時候 { //只檢查是否點到燒杯 if (hit.transform.tag == "Beaker") dragBeaker = hit.transform.GetComponent<Beaker>(); } else //有拿取燒杯的時候 { if (hit.transform == dragBeaker.transform) continue; //跳過拿取中的燒杯 //簡單做個判斷看點到什麼要做什麼 if (hit.transform.tag == "WaterInfinity") dragBeaker.AddWater(true); //點到無窮水物件,就把燒杯水填滿 else if (hit.transform.tag == "WaterSink") dragBeaker.AddWater(false); //點到水槽就把燒杯水倒掉 else if (hit.transform.tag == "Beaker") hit.transform.GetComponent<Beaker>().AddWater(dragBeaker); //點到燒杯就把水倒過去 else if (hit.transform.tag == "WaterTarget") { Beaker b = hit.transform.GetComponent<Beaker>(); if (b.maxWater == dragBeaker.currentWater) //使用WaterTarget物件上Beaker的maxWater來做判斷 { //達到目標,遊戲結束 Debug.Log("GameOver"); } } } } } //點滑鼠右鍵就讓燒杯回原位 if (Input.GetKeyDown(KeyCode.Mouse1)) { dragBeaker.ReturnStartPosition(); dragBeaker = null; } } }
大致上就這樣,雖然有些是寫死的,不過有達到目標即可,可以再修改許多地方讓整個機制更彈性,像是亂數決定中間兩個燒杯的大小、目標水量之類的,可以讓整個稍微有點變化。
No comments:
Post a Comment