3-7 Adding An Energy Mechanic
本章要來製作Energy Bar,當玩家按下滑鼠右鍵的時候會施放技能,導致Energy被消耗,Energy Bar也必須要有反應。由於我們之前已經製作過Health Bar,原理上相差不多,本次教學也大多是利用之前的素材,如有部分原理尚不了解的話,請先看看下列文章:
2-2 Player Health Bar UI
Energy.cs的程式碼如下:
大家應該會注意到Energy中註冊了一個委派事件notifyRightMouseClickObservers,該委派事件撰寫在CameraRaycaster中進行呼叫,以下是新的CameraRaycaseter.cs的程式碼,以後會再進行更聰明的重構,現階段就先這樣寫吧:
接著回到場景上,選擇Health Bar跟Health Bar Mask進行複製。
並更名為Energy Bar跟Energy Bar Mask。
並調整Energy Bar的位置,放在Health Bar的上面,如下圖。
然後再來複製原本health bar的圖檔,也更名為energy bar吧。
簡單修改一下顏色,我用Photoshop的顏色取代工具,簡單改成藍色跟灰色。
修改好圖檔以後,再回到Energy Bar的Inspector視窗,將新的圖檔拉入Raw Image/Texture中。
並記得移除在Energy Bar中的Player Health Bar的Script物件,這是因為我們剛剛用複製的,所以這個Script物件跟著複製過來,且也會影響到Energy Bar的顯示。
最後,回到Player的Inspector視窗,將設定好的Energy Bar拉進Energy Bar Raw Image中。
執行遊戲看看吧!目前Energy Bar還未有消耗。
遭遇敵人!趕快按滑鼠右鍵施放技能,看到Energy Bar一直在減少了!施放技能的部分還沒做,以後再做吧。
2-2 Player Health Bar UI
2-10 Fully Event Based Raycasting
首先新增一個Energy.cs的Script檔。
Energy.cs的程式碼如下:
using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.UI; using RPG.CameraUI; namespace RPG.Character{ public class Energy : MonoBehaviour { [SerializeField] RawImage energyBarRawImage; [SerializeField] float maxEnergyPoints = 100f; [SerializeField] float pointPerHits = 10f; float currentEnergyPoints; CameraRaycaster cameraRaycaster; void Start () { cameraRaycaster = Camera.main.GetComponent(); currentEnergyPoints = maxEnergyPoints; cameraRaycaster.notifyRightMouseClickObservers += ProcessRightClcik; } private void ProcessRightClcik(RaycastHit raycastHit, int layerHit){ float newEnergyPoint = currentEnergyPoints - pointPerHits; currentEnergyPoints = Mathf.Clamp (newEnergyPoint, 0, maxEnergyPoints); float xValue = -(EnergyAsPersent() / 2f) - 0.5f; energyBarRawImage.uvRect = new Rect(xValue, 0f, 0.5f, 1f); } float EnergyAsPersent(){ return currentEnergyPoints / maxEnergyPoints; } } }
大家應該會注意到Energy中註冊了一個委派事件notifyRightMouseClickObservers,該委派事件撰寫在CameraRaycaster中進行呼叫,以下是新的CameraRaycaseter.cs的程式碼,以後會再進行更聰明的重構,現階段就先這樣寫吧:
using UnityEngine; using UnityEngine.EventSystems; using System.Linq; using System.Collections.Generic; namespace RPG.CameraUI{ public class CameraRaycaster : MonoBehaviour { // INSPECTOR PROPERTIES RENDERED BY CUSTOM EDITOR SCRIPT [SerializeField] int[] layerPriorities = null; float maxRaycastDepth = 100f; // Hard coded value int topPriorityLayerLastFrame = -1; // So get ? from start with Default layer terrain // Setup delegates for broadcasting layer changes to other classes public delegate void OnCursorLayerChange(int newLayer); // 宣告一個新的委派型別 public event OnCursorLayerChange notifyLayerChangeObservers; //初始化委派的實體 public delegate void OnClickPriorityLayer(RaycastHit raycastHit, int layerHit); // 宣告一個新的委派型別 public event OnClickPriorityLayer notifyMouseClickObservers; //初始化委派的實體 public delegate void OnRightClick(RaycastHit raycastHit, int layerHit); // 宣告一個新的委派型別 public event OnRightClick notifyRightMouseClickObservers; //初始化委派的實體 void Update() { // 確認鼠標是否放在GameObject之上(這個上面在遊戲中亦代表UI層) if (EventSystem.current.IsPointerOverGameObject ()) { NotifyObserersIfLayerChanged (5); //5 是UI Layer return; // 既然鼠標在UI層,直接跳過以下的判斷事件 } // Raycast to max depth, every frame as things can move under mouse Ray ray = Camera.main.ScreenPointToRay (Input.mousePosition); RaycastHit[] raycastHits = Physics.RaycastAll (ray, maxRaycastDepth); RaycastHit? priorityHit = FindTopPriorityHit(raycastHits); if (!priorityHit.HasValue) // 若碰觸到的Layer不在我們定義的Priority清單中,則預設為Default Layer { NotifyObserersIfLayerChanged (0); // broadcast default layer return; } // 當鼠標碰觸到的Layer有變換時,通知委派執行事件 var layerHit = priorityHit.Value.collider.gameObject.layer; //取得鼠標碰到的Layer NotifyObserersIfLayerChanged(layerHit); // 當滑鼠點擊左鍵時,通知委派執行事件 if (Input.GetMouseButton (0)) { notifyMouseClickObservers (priorityHit.Value, layerHit); } // 當滑鼠點下去右鍵才會觸發MouseButtonDown,即使一直壓著右鍵也不會觸發多次 if (Input.GetMouseButtonDown (1)) { notifyRightMouseClickObservers (priorityHit.Value, layerHit); } } void NotifyObserersIfLayerChanged(int newLayer) { // 確認鼠標碰到的layer是否跟前一個layer不同,不同的話便呼叫委派通知更換Cursor圖標 if (newLayer != topPriorityLayerLastFrame) { topPriorityLayerLastFrame = newLayer; notifyLayerChangeObservers (newLayer); } } RaycastHit? FindTopPriorityHit (RaycastHit[] raycastHits) { // Form list of layer numbers hit ListlayersOfHitColliders = new List (); foreach (RaycastHit hit in raycastHits) { layersOfHitColliders.Add (hit.collider.gameObject.layer); } // 從layerPriorities中依順序偵測 foreach (int layer in layerPriorities) { foreach (RaycastHit hit in raycastHits) { if (hit.collider.gameObject.layer == layer) { return hit; // stop looking } } } return null; // because cannot use GameObject? nullable } } }
接著回到場景上,選擇Health Bar跟Health Bar Mask進行複製。
並更名為Energy Bar跟Energy Bar Mask。
並調整Energy Bar的位置,放在Health Bar的上面,如下圖。
然後再來複製原本health bar的圖檔,也更名為energy bar吧。
簡單修改一下顏色,我用Photoshop的顏色取代工具,簡單改成藍色跟灰色。
修改好圖檔以後,再回到Energy Bar的Inspector視窗,將新的圖檔拉入Raw Image/Texture中。
並記得移除在Energy Bar中的Player Health Bar的Script物件,這是因為我們剛剛用複製的,所以這個Script物件跟著複製過來,且也會影響到Energy Bar的顯示。
最後,回到Player的Inspector視窗,將設定好的Energy Bar拉進Energy Bar Raw Image中。
執行遊戲看看吧!目前Energy Bar還未有消耗。
遭遇敵人!趕快按滑鼠右鍵施放技能,看到Energy Bar一直在減少了!施放技能的部分還沒做,以後再做吧。
留言
張貼留言