Unity LineRenderer 射线检测 激光攻击

在进行激光攻击的脚本编写前,我们需要先进行一定程度的想象,激光和普通的远程攻击有哪些不太一样的地方。

正常的远程攻击例如子弹,箭矢,技能波等,都有明确的弹道,不可能同时命中多个敌人,而且一般只要命中敌人后就会被销毁。(特殊技能除外)

但激光可以认为是一种持续性的范围伤害,只是它的范围(长度)是不固定的,在激光的发射阶段,它会在第一个被命中的目标或障碍物处截断。

一旦整个激光成型后,一般来说,它将不再被运动的目标截断,反而是依靠它已经生成的光柱将目标弹开并造成伤害。

当然,如果之前被命中的目标或障碍物从激光的光柱范围内移开,这时激光会自动延长至下一被命中的目标或障碍物位置。

也就是说,激光一旦发出,在它的生命周期内,只延长不缩短,直至最后消失。

激光发射的过程如下:

1.从起始的发射点射出一条不断向前运动的射线,到达目标点的速度非常快,一般肉眼很难捕捉。直到遇到障碍物截断,不然持续向前延伸。

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     }

该方法外部调用后自动切换到激光的生命周期循环,其中用到的对象池可详见:

https://www.cnblogs.com/koshio0219/p/11572567.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     }

这里用到的事件系统可以详见:

https://www.cnblogs.com/koshio0219/p/11209191.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 }

原文地址:https://www.cnblogs.com/koshio0219/p/12102577.html

时间: 2024-10-12 09:12:53

Unity LineRenderer 射线检测 激光攻击的相关文章

Unity的射线检测碰撞(JavaScript)

#pragma strict function Start () { } function Update () { var hit:RaycastHit; if(Physics.Raycast(transform.position,transform.forward,hit,8)) { print("射线触碰了"+hit.collider.gameObject+"物体"); } }

unity 射线检测

unity中射线检测时非常实用也经常实用的一种手段.下面讲解一下射线检测问题. 1)Ray 根据射线端点和射线的方向定义一条射线 Ray ray= new Ray(transform.position, transform.forward); 定义一个包含射线投射信息的变量RaycastHit hit,并进行射线检测Physics.SphereCast RaycastHit hit; if(Physics.SphereCast(ray,1f,out hit)) { if(hit.distance

unity中射线碰撞检测总结

这阵子通过看视频,看书对unity中射线碰撞检测,有了一些了解,这里我把它总结一下写下来,希望能帮助到你们,也希望通过各位大神来指正不足之处: 射线碰撞检测,就是由某一物体发射出一道射线,射线碰撞到物体之后,可以得到该物体的相关信息,然后就可以对该物体进行一些操作的过程了.PS:个人见解: 射线的发射分为可见和不可见,可见的情况就是在游戏当中你可以看到射线,而不可见就是游戏运行时,你只可以在Scene试图下观察到它,而这到射线又是存在的: 而其实两者区别不大,关键是看你怎么用它了: 下面先看看不

Unity3D 之射线检测

这里来记录下射线检测的相关内容: 射线检测故名就是通过射线去检测是否和碰撞器产生了交集,和碰撞器与碰撞器发生交集一样,会返回一个真. 射线的用法很多:比如检测是否跳跃,通过向地面投射射线控制在地面时候可以跳起. 射击游戏中可以通过定长射线去判断目标物体是否被击中,等 主要用到的工具类是: Physics RaycastHit 光线投射碰撞 Ray 射线 第一种是: Physics.Linecast 线性投射 从开始位置到结束位置做一个光线投射,如果与碰撞体交互,返回真. Debug.DrawLi

Unity屏幕射线碰撞

Layers层: 从Layers设置看来,最多支持32层.  图层的值开始依次 0^2,1^2, 2^2, 3^3 依次增加. 当摄像机Culling Mask属性 Nothing= –1 Evening = 全部图层之和, 项目开始默认设置了7个图层. 值就是0^2,1^2, 2^2, 3^3 .. 6^2  = 279 如果我们选择Defulat图层,所在第0位置值 0^2 = 1 如果我们选择Defualt,Transparent,图层位置0,1 = 0^2+1^2 = 3 组合: 假设在

【转】 [UnityUI]UGUI射线检测

http://blog.csdn.net/lyh916/article/details/50947026 1.Graphic Raycaster 主要用于UI上的射线检测,挂有这个组件的物体,必须要挂上Canvas这个组件(当挂上Graphic Raycaster时Canvas也会自动挂上). Ignore Reversed Graphics:是否忽略反方向的图形,如果为true,则表示图形正面展示时,会接收到射线检测:反面展示时,不会接收到射线检测:否则,正反面展示都会接收到射线检测 Bloc

[Unity菜鸟] 射线

1. 射线用 Physics.Raycast 都可以判断,用 collider.Raycast 只在某些(不明)情况下可以 void Update() { Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition); RaycastHit hit; // if (collider.Raycast(ray, out hit, 100f)) if (Physics.Raycast(ray,out hit ,100f)) { Debug.Dr

UGUI射线检测

1.Graphic Raycaster 主要用于UI上的射线检测,挂有这个组件的物体,必须要挂上Canvas这个组件(当挂上Graphic Raycaster时Canvas也会自动挂上). Ignore Reversed Graphics:是否忽略反方向的图形,如果为true,则表示图形正面展示时,会接收到射线检测:反面展示时,不会接收到射线检测:否则,正反面展示都会接收到射线检测 Blocking Objects:屏蔽指定类型的(物理)对象,使它们不参与射线检测.渲染模式不为ScreenSpa

unity3d之射线检测

问题:在<英雄联盟>里,你的鼠标在地图上点击在了哪里,你的英雄就会走到你所点击的地方,那么,用u3d如何来实现这个小小的功能呢??? 解决方案:做射线检测.首先你要准备两个东西(1):摄像机(这是射线的出发点)       (2):"碰撞信息"(用以记录射线与对象所产生的碰撞点的信息) 在u3d中创建一个空工程并添加一个c#脚本:RaycastTest.添加好我们刚才所说的两个东西:"摄像机"."碰撞信息",并初始化他俩(摄像机稍后再