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
List layersOfHitColliders = 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一直在減少了!施放技能的部分還沒做,以後再做吧。











留言
張貼留言