在進行激光攻擊的腳本編寫前,咱們須要先進行必定程度的想象,激光和普通的遠程攻擊有哪些不太同樣的地方。html
正常的遠程攻擊例如子彈,箭矢,技能波等,都有明確的彈道,不可能同時命中多個敵人,並且通常只要命中敵人後就會被銷燬。(特殊技能除外)ide
但激光能夠認爲是一種持續性的範圍傷害,只是它的範圍(長度)是不固定的,在激光的發射階段,它會在第一個被命中的目標或障礙物處截斷。spa
一旦整個激光成型後,通常來講,它將再也不被運動的目標截斷,反而是依靠它已經生成的光柱將目標彈開並形成傷害。code
固然,若是以前被命中的目標或障礙物從激光的光柱範圍內移開,這時激光會自動延長至下一被命中的目標或障礙物位置。orm
也就是說,激光一旦發出,在它的生命週期內,只延長不縮短,直至最後消失。htm
激光發射的過程以下:對象
1.從起始的發射點射出一條不斷向前運動的射線,到達目標點的速度很是快,通常肉眼很難捕捉。直到遇到障礙物截斷,否則持續向前延伸。blog
2.激光一開始是以極小的寬度開始擴散它的能量,它的寬度在發射過程當中是由細到寬最終到達極限寬度的。而不是恆定不變的。生命週期
3.激光因爲快速運動勢必會與空氣產生摩擦,一部分電光會在激光運動的軌跡周圍閃現。事件
4.激光有生命週期,也能夠是中止持續供能後衰減。但激光衰減的過程當中長度不會發生變化,而是經過相似於能量迅速收束的方式使整個光柱逐漸變細直至消失,周圍的電光也在此衰減過程當中逐漸消失。
上面想象模擬了一束激光從生成到凋亡的整個過程,基於此,先定義幾種狀態:
1 public enum EmissionRayState 2 { 3 Off, 4 On 5 } 6 7 public enum EmissionLifeSate 8 { 9 None, 10 //建立階段 11 Creat, 12 //生命週期階段 13 Keep, 14 //衰減階段 15 Attenuate 16 }
主循環的狀態切換:
1 void Update() 2 { 3 switch (State) 4 { 5 case EmissionRayState.On: 6 switch (LifeSate) 7 { 8 case EmissionLifeSate.Creat: 9 ShootLine(); 10 break; 11 case EmissionLifeSate.Keep: 12 ExtendLineWidth(); 13 break; 14 case EmissionLifeSate.Attenuate: 15 CutDownRayLine(); 16 break; 17 } 18 break; 19 } 20 }
屬性列表:
1 //發射位置 2 public Transform FirePos; 3 //激光顏色 4 public Color EmissionColor = Color.blue; 5 //電光顏色 6 public Color EleLightColor = Color.blue; 7 //發射速度 8 public float FireSpeed = 30f; 9 //生命週期 10 public float LifeTime = .3f; 11 //最大到達寬度 12 public float MaxRayWidth = .1f; 13 //寬度擴展速度 14 public float WidthExtendSpeed = .5f; 15 //漸隱速度 16 public float FadeOutSpeed = 1f; 17 //電光數量 18 public int EleLightCount = 8; 19 //電光偏移值 20 public float EleLightOffse = .5f; 21 //擊中傷害 22 public int Damage = 121;
每次發射時建立一個附帶LineRenderer組件的物體,在發射前對其中的一些屬性賦值:
1 public void FireBegin() 2 { 3 switch (State) 4 { 5 //只有在狀態關閉時才能夠開啓激光 6 case EmissionRayState.Off: 7 //實例化激光組件 8 LineRayInstance = ObjectPool.Instance.GetObj(LineRayPrefab.gameObject, FirePos).GetComponent<LineRenderer>(); 9 EleLightningInstance = ObjectPool.Instance.GetObj(EleLightningPerfab.gameObject, FirePos).GetComponent<LineRenderer>(); 10 //設置狀態 11 State = EmissionRayState.On; 12 LifeSate = EmissionLifeSate.Creat; 13 //初始化屬性 14 RayCurrentPos = FirePos.position; 15 LineRayInstance.GetComponent<EmissionRay>().Damage = Damage; 16 LineRayInstance.positionCount = 2; 17 RayOriginWidth = LineRayInstance.startWidth; 18 LineRayInstance.material.SetColor("_Color", EmissionColor); 19 EleLightningInstance.material.SetColor("_Color", EleLightColor); 20 break; 21 } 22 }
該方法外部調用後自動切換到激光的生命週期循環,其中用到的對象池可詳見:
http://www.javashuo.com/article/p-hmqufqmf-gn.html
生成射線階段:
1 //生成射線 2 private void ShootLine() 3 { 4 //設置激光起點 5 LineRayInstance.SetPosition(0, FirePos.position); 6 var dt = Time.deltaTime; 7 8 //射線的終點按發射速度進行延伸 9 RayCurrentPos += FirePos.forward * FireSpeed * dt; 10 11 //在射線運動過程當中建立一個單位長度的射線用來檢測碰撞 12 Ray ray = new Ray(RayCurrentPos, FirePos.forward); 13 RaycastHit hit; 14 //射線長度爲一幀的運動距離,保證不會由於運動過快而丟失 15 if (Physics.Raycast(ray, out hit, dt * FireSpeed)) 16 { 17 RayCurrentPos = hit.point; 18 //向命中物體發送被擊信號 19 SendActorHit(hit.transform.gameObject); 20 21 //切換狀態 22 LifeSate = EmissionLifeSate.Keep; 23 RayCurrentWidth = RayOriginWidth; 24 RayLength = (RayCurrentPos - FirePos.position).magnitude; 25 //建立射線周圍電光 26 CreatKeepEleLightning(); 27 //開始計算生命週期 28 LifeTimer = 0f; 29 } 30 //設置當前幀終點位置 31 LineRayInstance.SetPosition(1, RayCurrentPos); 32 }
1 //發送受擊信號 2 private void SendActorHit(GameObject HitObject) 3 { 4 var actor = HitObject.GetComponent<Actor>(); 5 if (actor != null) 6 { 7 actor.OnHit(LineRayInstance.gameObject); 8 actor.OnHitReAction(LineRayInstance.gameObject, FirePos.forward.GetVector3XZ().normalized); 9 } 10 }
1 //生成電光 2 private void CreatKeepEleLightning() 3 { 4 EleLightningInstance.positionCount = EleLightCount; 5 for (int i = 0; i < EleLightCount; i++) 6 { 7 //計算偏移值 8 var offse = RayCurrentWidth / 2 + EleLightOffse; 9 var eleo = FirePos.position + (RayCurrentPos - FirePos.position) * (i + 1) / EleLightCount; 10 //在射線的左右間隔分佈 11 var pos = i % 2 == 0 ? new Vector3(eleo.x + offse, eleo.y, eleo.z) : new Vector3(eleo.x - offse, eleo.y, eleo.z); 12 EleLightningInstance.SetPosition(i, pos); 13 } 14 }
注意這裏本例中不用任何碰撞體來檢測碰撞,而是單純用射線檢測,這裏只用了一條,更好的作法是在激光的兩條邊緣發射兩條射線檢測。
生命週期階段:
1 private void ExtendLineWidth() 2 { 3 //每幀檢測射線碰撞 4 CheckRayHit(); 5 var dt = Time.deltaTime; 6 //按速度擴展寬度直到最大寬度 7 if (RayCurrentWidth < MaxRayWidth) 8 { 9 RayCurrentWidth += dt * WidthExtendSpeed; 10 LineRayInstance.startWidth = RayCurrentWidth; 11 LineRayInstance.endWidth = RayCurrentWidth; 12 } 13 //生命週期結束後切換爲衰減狀態 14 LifeTimer += dt; 15 if (LifeTimer > LifeTime) 16 { 17 LifeSate = EmissionLifeSate.Attenuate; 18 } 19 }
1 private void CheckRayHit() 2 { 3 Ray ray = new Ray(FirePos.position, FirePos.forward); 4 RaycastHit hit; 5 //按當前激光長度檢測 6 if (Physics.Raycast(ray, out hit, RayLength)) 7 { 8 SendActorHit(hit.transform.gameObject); 9 } 10 }
衰減階段:
1 private void CutDownRayLine() 2 { 3 var dt = Time.deltaTime; 4 //寬度衰減爲零後意味着整個激光關閉完成 5 if (RayCurrentWidth > 0) 6 { 7 RayCurrentWidth -= dt * FadeOutSpeed; 8 LineRayInstance.startWidth = RayCurrentWidth; 9 LineRayInstance.endWidth = RayCurrentWidth; 10 } 11 else 12 FireShut(); 13 }
關閉射線並還原設置:
1 public void FireShut() 2 { 3 switch (State) 4 { 5 case EmissionRayState.On: 6 EleLightningInstance.positionCount = 0; 7 LineRayInstance.positionCount = 0; 8 LineRayInstance.startWidth = RayOriginWidth; 9 LineRayInstance.endWidth = RayOriginWidth; 10 //回收實例化個體 11 ObjectPool.Instance.RecycleObj(LineRayInstance.gameObject); 12 ObjectPool.Instance.RecycleObj(EleLightningInstance.gameObject); 13 State = EmissionRayState.Off; 14 //發送射線已關閉的事件 15 EventManager.QueueEvent(new EmissionShutEvent()); 16 break; 17 } 18 }
這裏用到的事件系統能夠詳見:
http://www.javashuo.com/article/p-kxxarhzu-gq.html
完整腳本:
1 using UnityEngine; 2 3 public enum EmissionRayState 4 { 5 Off, 6 On 7 } 8 9 public enum EmissionLifeSate 10 { 11 None, 12 //建立階段 13 Creat, 14 //生命週期階段 15 Keep, 16 //衰減階段 17 Attenuate 18 } 19 20 public class EmissionRayCtrl : MonoBehaviour 21 { 22 public LineRenderer LineRayPrefab; 23 public LineRenderer EleLightningPerfab; 24 25 private LineRenderer LineRayInstance; 26 private LineRenderer EleLightningInstance; 27 28 //發射位置 29 public Transform FirePos; 30 //激光顏色 31 public Color EmissionColor = Color.blue; 32 //電光顏色 33 public Color EleLightColor = Color.blue; 34 //發射速度 35 public float FireSpeed = 30f; 36 //生命週期 37 public float LifeTime = .3f; 38 //最大到達寬度 39 public float MaxRayWidth = .1f; 40 //寬度擴展速度 41 public float WidthExtendSpeed = .5f; 42 //漸隱速度 43 public float FadeOutSpeed = 1f; 44 //電光數量 45 public int EleLightCount = 8; 46 //電光偏移值 47 public float EleLightOffse = .5f; 48 //擊中傷害 49 public int Damage = 121; 50 51 private EmissionRayState State; 52 private EmissionLifeSate LifeSate; 53 54 private Vector3 RayCurrentPos; 55 private float RayOriginWidth; 56 private float RayCurrentWidth; 57 private float LifeTimer; 58 private float RayLength; 59 void Start() 60 { 61 State = EmissionRayState.Off; 62 LifeSate = EmissionLifeSate.None; 63 } 64 65 public void FireBegin() 66 { 67 switch (State) 68 { 69 //只有在狀態關閉時才能夠開啓激光 70 case EmissionRayState.Off: 71 //實例化激光組件 72 LineRayInstance = ObjectPool.Instance.GetObj(LineRayPrefab.gameObject, FirePos).GetComponent<LineRenderer>(); 73 EleLightningInstance = ObjectPool.Instance.GetObj(EleLightningPerfab.gameObject, FirePos).GetComponent<LineRenderer>(); 74 //設置狀態 75 State = EmissionRayState.On; 76 LifeSate = EmissionLifeSate.Creat; 77 //初始化屬性 78 RayCurrentPos = FirePos.position; 79 LineRayInstance.GetComponent<EmissionRay>().Damage = Damage; 80 LineRayInstance.positionCount = 2; 81 RayOriginWidth = LineRayInstance.startWidth; 82 LineRayInstance.material.SetColor("_Color", EmissionColor); 83 EleLightningInstance.material.SetColor("_Color", EleLightColor); 84 break; 85 } 86 } 87 88 void Update() 89 { 90 switch (State) 91 { 92 case EmissionRayState.On: 93 switch (LifeSate) 94 { 95 case EmissionLifeSate.Creat: 96 ShootLine(); 97 break; 98 case EmissionLifeSate.Keep: 99 ExtendLineWidth(); 100 break; 101 case EmissionLifeSate.Attenuate: 102 CutDownRayLine(); 103 break; 104 } 105 break; 106 } 107 } 108 109 //生成射線 110 private void ShootLine() 111 { 112 //設置激光起點 113 LineRayInstance.SetPosition(0, FirePos.position); 114 var dt = Time.deltaTime; 115 116 //射線的終點按發射速度進行延伸 117 RayCurrentPos += FirePos.forward * FireSpeed * dt; 118 119 //在射線運動過程當中建立一個單位長度的射線用來檢測碰撞 120 Ray ray = new Ray(RayCurrentPos, FirePos.forward); 121 RaycastHit hit; 122 //射線長度爲一幀的運動距離,保證不會由於運動過快而丟失 123 if (Physics.Raycast(ray, out hit, dt * FireSpeed)) 124 { 125 RayCurrentPos = hit.point; 126 //向命中物體發送被擊信號 127 SendActorHit(hit.transform.gameObject); 128 129 //切換狀態 130 LifeSate = EmissionLifeSate.Keep; 131 RayCurrentWidth = RayOriginWidth; 132 RayLength = (RayCurrentPos - FirePos.position).magnitude; 133 //建立射線周圍電光 134 CreatKeepEleLightning(); 135 //開始計算生命週期 136 LifeTimer = 0f; 137 } 138 //設置當前幀終點位置 139 LineRayInstance.SetPosition(1, RayCurrentPos); 140 } 141 142 //發送受擊信號 143 private void SendActorHit(GameObject HitObject) 144 { 145 var actor = HitObject.GetComponent<Actor>(); 146 if (actor != null) 147 { 148 actor.OnHit(LineRayInstance.gameObject); 149 actor.OnHitReAction(LineRayInstance.gameObject, FirePos.forward.GetVector3XZ().normalized); 150 } 151 } 152 153 private void CheckRayHit() 154 { 155 Ray ray = new Ray(FirePos.position, FirePos.forward); 156 RaycastHit hit; 157 //按當前激光長度檢測 158 if (Physics.Raycast(ray, out hit, RayLength)) 159 { 160 SendActorHit(hit.transform.gameObject); 161 } 162 } 163 164 private void ExtendLineWidth() 165 { 166 //每幀檢測射線碰撞 167 CheckRayHit(); 168 var dt = Time.deltaTime; 169 //按速度擴展寬度直到最大寬度 170 if (RayCurrentWidth < MaxRayWidth) 171 { 172 RayCurrentWidth += dt * WidthExtendSpeed; 173 LineRayInstance.startWidth = RayCurrentWidth; 174 LineRayInstance.endWidth = RayCurrentWidth; 175 } 176 //生命週期結束後切換爲衰減狀態 177 LifeTimer += dt; 178 if (LifeTimer > LifeTime) 179 { 180 LifeSate = EmissionLifeSate.Attenuate; 181 } 182 } 183 184 //生成電光 185 private void CreatKeepEleLightning() 186 { 187 EleLightningInstance.positionCount = EleLightCount; 188 for (int i = 0; i < EleLightCount; i++) 189 { 190 //計算偏移值 191 var offse = RayCurrentWidth / 2 + EleLightOffse; 192 var eleo = FirePos.position + (RayCurrentPos - FirePos.position) * (i + 1) / EleLightCount; 193 //在射線的左右間隔分佈 194 var pos = i % 2 == 0 ? new Vector3(eleo.x + offse, eleo.y, eleo.z) : new Vector3(eleo.x - offse, eleo.y, eleo.z); 195 EleLightningInstance.SetPosition(i, pos); 196 } 197 } 198 199 private void CutDownRayLine() 200 { 201 var dt = Time.deltaTime; 202 //寬度衰減爲零後意味着整個激光關閉完成 203 if (RayCurrentWidth > 0) 204 { 205 RayCurrentWidth -= dt * FadeOutSpeed; 206 LineRayInstance.startWidth = RayCurrentWidth; 207 LineRayInstance.endWidth = RayCurrentWidth; 208 } 209 else 210 FireShut(); 211 } 212 213 public void FireShut() 214 { 215 switch (State) 216 { 217 case EmissionRayState.On: 218 EleLightningInstance.positionCount = 0; 219 LineRayInstance.positionCount = 0; 220 LineRayInstance.startWidth = RayOriginWidth; 221 LineRayInstance.endWidth = RayOriginWidth; 222 //回收實例化個體 223 ObjectPool.Instance.RecycleObj(LineRayInstance.gameObject); 224 ObjectPool.Instance.RecycleObj(EleLightningInstance.gameObject); 225 State = EmissionRayState.Off; 226 //發送射線已關閉的事件 227 EventManager.QueueEvent(new EmissionShutEvent()); 228 break; 229 } 230 } 231 }