Related Posts Plugin for WordPress, Blogger...

3-7 Adding An Energy Mechanic

本章要來製作Energy Bar,當玩家按下滑鼠右鍵的時候會施放技能,導致Energy被消耗,Energy Bar也必須要有反應。由於我們之前已經製作過Health Bar,原理上相差不多,本次教學也大多是利用之前的素材,如有部分原理尚不了解的話,請先看看下列文章:

2-2 Player Health Bar UI

2-10 Fully Event Based Raycasting 

首先新增一個Energy.cs的Script檔。

Energy.cs的程式碼如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
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<cameraraycaster>();
   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;
  }
 }
}
</cameraraycaster>

大家應該會注意到Energy中註冊了一個委派事件notifyRightMouseClickObservers,該委派事件撰寫在CameraRaycaster中進行呼叫,以下是新的CameraRaycaseter.cs的程式碼,以後會再進行更聰明的重構,現階段就先這樣寫吧:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
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<int> layersOfHitColliders = new List<int> ();
   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
  }
 }
}
</int></int>

接著回到場景上,選擇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一直在減少了!施放技能的部分還沒做,以後再做吧。

留言