Unity LineRenderer 射線檢測 激光攻擊

在進行激光攻擊的腳本編寫前,咱們須要先進行必定程度的想象,激光和普通的遠程攻擊有哪些不太同樣的地方。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 }
View Code
相關文章
相關標籤/搜索