文章目录
Unity发射激光,光线被镜子折射详细实现
最终效果如图所示
一总体思路
理一下思路,首先关键是用LineRenderer渲染光线,但是LineRenderer需要线的每个点坐标
已知条件:
发射点坐标,发射方向
需要求出:
途中经过的所有点坐标
最终:
将所有点通过代码实时用LineRenderer渲染出来
有了这些其他都是小问题,光线的形状LineRenderer可以设置,发射器的形状自己定义,被反射墙加碰撞体,然后算碰了墙后走的方向,这些都是有函数的,所以现在,开始
二发射器实现
1生成空物体存放发射器和光线
生成空物体,命名LineAndLight(后文统一叫做LineAndLight)下面放发射器和光线,后面算位置就属于同一个坐标系下,空物体位置设置成(0,0,0)以防光线渲染出问题。
2 生成2d精灵Sprite
spriteRenderer拖进去发射器图片
3给发射器挂C#脚本
给发射器挂载脚本,命名Light,具体解释在后面的关键代码解释中
脚本内容如下,这里只说明下 [SerializeField] private GameObject position;这行,需要一个物体代表发射的点,给后面的算法提供发射点的位置,我们在发射器下添加空物体,这里点击这里让空物体显示出来
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class lightddd: MonoBehaviour
{
public LineRenderer laser;
public List<Vector3> laserPoint = new List<Vector3>();
public LayerMask marrow;
public bool hitdoor = false;
public GameObject shaining;
[SerializeField] private int reflectNum;
[SerializeField] private GameObject position;
public enum LightDiraction
{
down,
up,
right,
left
}
public LightDiraction lightDiraction;
// Start is called before the first frame update
void Start()
{
laser.gameObject.SetActive(true);
GameManager.RegistLight(this);
}
// Update is called once per frame
void Update()
{
CastLaser();
laser.positionCount = laserPoint.Count;
laser.SetPositions(laserPoint.ToArray());
}
void CastLaser()
{
laserPoint.Clear();
//开始的点
var startPoint = position.transform.position;
//两个关卡不同时,灯泡初始方向不同,所以选择判断改变初始点
//发射方向
var diraction = new Vector2(0, -1);
switch (lightDiraction)
{
case LightDiraction.up:
{
diraction = new Vector2(0, 1);
break;
}
case LightDiraction.down:
{
diraction = new Vector2(0, -1);
break;
}
case LightDiraction.right:
{
diraction = new Vector2(1, 0);
break;
}
case LightDiraction.left:
{
diraction = new Vector2(-1, 0);
break;
}
}
//第一个出发点
laserPoint.Add(startPoint);
int i = 0;
do
{
RaycastHit2D hit = Physics2D.Raycast(startPoint, diraction,1000f,marrow);
//添加击中点到路径中,raycast返回值 RaycastHit2D, RaycastHit2D.point返回接触到碰撞体的表面
if (hit)
laserPoint.Add(hit.point);
//如果击中门,门特效开,hitdoor变量变换,用于胜负判断,击中门的时候跳出while很重要
if (hit.transform.tag == "door")
{
shaining.SetActive(true);
hitdoor = true;
break;
}
else
{
shaining.SetActive(false);
hitdoor = false;
}
//发射方向,RaycastHit2D.normal返回被射线击中的曲面的法向量
// if(hit.transform.rotation.z==90)
// diration = Vector2.Reflect(hit.point - (Vector2)startPoint, -hit.normal);
// else if(hit.transform.rotation.z == 0)
diraction = Vector2.Reflect(hit.point -(Vector2)startPoint,hit.normal);
// if (hit.transform.rotation.z == 90)
// diration = new Vector2(-diration.x, -diration.y);
startPoint = hit.point+diraction*0.01f;
i++;
} while (i < reflectNum)
;
}
}
4给发射器加2D碰撞体
三光线LineRenderer实现
1新建空物体
在前面的空物体LineAndLight下新建空物体,命名Line
2添加LineRenderer组件
给Line添加组件LineRenderer
颜色宽度凭自己爱好自己设置
四折射光线的墙壁实现
你想让什么折射光线就建什么物体,可以加到镜子上,墙壁上,但是最重要的是图层一定要设置,图层设置后在光线的代码里才能检测是否遇到了墙壁
这里建一个四面的墙
1建立空物体命名Wall
2添加墙壁
墙壁设置图层,加碰撞体
五关键算法解释
1声明变量解释
public LineRenderer laser;
public List<Vector3> laserPoint = new List<Vector3>();
public LayerMask marrow;
public bool hitdoor = false;
public GameObject shaining;
[SerializeField] private int reflectNum;
[SerializeField] private GameObject position;
这些声明的东西都是干嘛的这里讲一下
1 public LineRenderer laser;
这就是绑定刚才的线那个物体
2 public List laserPoint = new List();
这个装坐标的动态数组装所有经过的重要点(折射点,能渲染Line的点)
3 public LayerMask marrow;
绑定图层,用这个图层判断光线是不是碰到墙壁了
4 public bool hitdoor = false;
这是我用来判断是否碰到接收器了,这里对大家没用,如果大家想要加接收器,喜欢我的文章,我后期更新加接收器的
5 public GameObject shaining;
这是我用来让接收器发光的
6 [SerializeField] private int reflectNum;
决定光线折射几次后停止继续被折射
7 [SerializeField] private GameObject position;
绑定初始点,获取位置
2计算点的算法 Physics2D.Raycast,Vector2.Reflect
laserPoint.Clear();
刚开始清空光线走过的点的数组
var startPoint = position.transform.position;
取到初始点,加入走过的点的数组
var diraction = new Vector2(0, -1);
switch (lightDiraction)
{
case LightDiraction.up:
{
diraction = new Vector2(0, 1);
break;
}
case LightDiraction.down:
{
diraction = new Vector2(0, -1);
break;
}
case LightDiraction.right:
{
diraction = new Vector2(1, 0);
break;
}
case LightDiraction.left:
{
diraction = new Vector2(-1, 0);
break;
}
}
由他的方向得到需要发射的光线方向
RaycastHit2D hit = Physics2D.Raycast(startPoint, diraction,1000f,marrow);
//添加击中点到路径中,raycast返回值 RaycastHit2D, RaycastHit2D.point返回接触到碰撞体的表面
if (hit)
laserPoint.Add(hit.point);
//如果击中门,门特效开,hitdoor变量变换,用于胜负判断,击中门的时候跳出while很重要
从初始点出发向指定方向发射,如果击中marrow图层的,返回RaycastHit2D对象hit,这个hit可以取到点,法向量
diraction = Vector2.Reflect(hit.point -(Vector2)startPoint,hit.normal);
得到发射后的方向 ,RaycastHit2D.normal返回被射线击中的曲面的法向量,Reflect通过传入两个向量,入射向量,法向量,得到出射向量,出射向量作为新方向,击中点作为新的初始点,再循环就行了,前面的reflectNum控制循环几次。
六总代码解释
光线,也就是前面的Line在游戏中要关闭,在代码中打开
重写了下注释
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class lightddd: MonoBehaviour
{
public LineRenderer laser;
public List<Vector3> laserPoint = new List<Vector3>();
public LayerMask marrow;
public bool hitdoor = false;
public GameObject shaining;
[SerializeField] private int reflectNum;
[SerializeField] private GameObject position;
//创建枚举状态控制发射方向
public enum LightDiraction
{
down,
up,
right,
left
}
//生成枚举类型对象
public LightDiraction lightDiraction;
//设置光线为打开状态
void Start()
{
laser.gameObject.SetActive(true);
GameManager.RegistLight(this);
}
void Update()
{
CastLaser();
//设置LineRenderer的关键点为我们算出来的点
laser.positionCount = laserPoint.Count;
laser.SetPositions(laserPoint.ToArray());
}
void CastLaser()
{
laserPoint.Clear();
//开始的点
var startPoint = position.transform.position;
//两个关卡不同时,灯泡初始方向不同,所以选择判断改变初始点
//发射方向
var diraction = new Vector2(0, -1);
switch (lightDiraction)
{
case LightDiraction.up:
{
diraction = new Vector2(0, 1);
break;
}
case LightDiraction.down:
{
diraction = new Vector2(0, -1);
break;
}
case LightDiraction.right:
{
diraction = new Vector2(1, 0);
break;
}
case LightDiraction.left:
{
diraction = new Vector2(-1, 0);
break;
}
}
//第一个出发点
laserPoint.Add(startPoint);
int i = 0;
do
{
RaycastHit2D hit = Physics2D.Raycast(startPoint, diraction,1000f,marrow);
//添加击中点到路径中,raycast返回值 RaycastHit2D, RaycastHit2D.point返回接触到碰撞体的表面
if (hit)
laserPoint.Add(hit.point);
//如果击中门,门特效开,hitdoor变量变换,用于胜负判断,击中门的时候跳出while很重要
if (hit.transform.tag == "door")
{
shaining.SetActive(true);
hitdoor = true;
break;
}
else
{
shaining.SetActive(false);
hitdoor = false;
}
//发射方向,RaycastHit2D.normal返回被射线击中的曲面的法向量
// if(hit.transform.rotation.z==90)
// diration = Vector2.Reflect(hit.point - (Vector2)startPoint, -hit.normal);
// else if(hit.transform.rotation.z == 0)
diraction = Vector2.Reflect(hit.point -(Vector2)startPoint,hit.normal);
// if (hit.transform.rotation.z == 90)
// diration = new Vector2(-diration.x, -diration.y);
startPoint = hit.point+diraction*0.01f;
i++;
} while (i < reflectNum)
;
}
}
七效果