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