文章目錄
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)
;
}
}
七效果