画线功能Linerenderer
1、LineRenderer是什么
LineRenderer是Unity提供的一个用于画线的组件
使用它我们可以在场景中绘制线段
一般可以用于
1、绘制攻击范围 2、武器红外线 3、辅助功能 4、其它画线功能
2、LineRender参数相关
3、LineRender代码相关
private Material m;
void Start()
{
GameObject line = new GameObject();
line.name = "line";
LineRenderer lineRenderer = line.AddComponent<LineRenderer>();
lineRenderer.loop = true;
lineRenderer.startWidth = 0.02f;
lineRenderer.endWidth = 0.02f;
lineRenderer.startColor = Color.white;
lineRenderer.endColor = Color.red;
m = Resources.Load<Material>("M");
lineRenderer.material = m;
lineRenderer.positionCount = 4;
lineRenderer.SetPositions(new Vector3[] { new Vector3(0, 0, 0),
new Vector3(0, 0, 5),new Vector3(5, 0, 5)});
lineRenderer.SetPosition(3, new Vector3(5, 0, 0));
lineRenderer.useWorldSpace = true;
lineRenderer.generateLightingData = true;
}
思考1 请写一个方法,传入一个中心点,传入一个半径,用LineRender画个圆出来
using UnityEngine;
public class CicleLine : MonoBehaviour
{
void Start()
{
DrawCicleLine(Vector3.zero, 5, 360);
}
public void DrawCicleLine(Vector3 centerPos, float r, int pointNum)
{
GameObject obj = new GameObject();
obj.name = "R";
LineRenderer cicleLine = obj.AddComponent<LineRenderer>();
cicleLine.loop = true;
cicleLine.positionCount = pointNum;
float angle = 360f / pointNum;
for (int i = 0; i < pointNum; i++)
{
cicleLine.SetPosition(i, centerPos + Quaternion.AngleAxis(angle * i, Vector3.up) * Vector3.forward * r);
}
}
}
思考2 在Game窗口长按鼠标用LineRender画出鼠标移动的轨迹
using UnityEngine;
public class MouseLine : MonoBehaviour
{
private LineRenderer DrawLine;
private Vector3 nowMousePos;
void Update()
{
if(Input.GetMouseButtonDown(0))
{
GameObject obj = new GameObject();
DrawLine = obj.AddComponent<LineRenderer>();
DrawLine.loop = false;
DrawLine.startWidth = 0.5f;
DrawLine.endWidth = 0.5f;
DrawLine.positionCount = 0;
}
if (Input.GetMouseButton(0))
{
DrawLine.positionCount += 1;
nowMousePos = Input.mousePosition;
nowMousePos.z = 10;
DrawLine.SetPosition(DrawLine.positionCount-1, Camera.main.ScreenToWorldPoint(nowMousePos));
}
}
}
核心系统
1、物理系统之范围检测
回顾:物理系统之碰撞检测
碰撞产生的必要条件
1.至少一个物体有刚体
2.两个物体都必须有碰撞器
碰撞和触发
碰撞会产生实际的物理效果
触发看起来不会产生碰撞但是可以通过函数监听触发
碰撞检测主要用于实体物体之间产生物理效果时使用
什么是范围检测
游戏中瞬时的攻击范围判断一般会使用范围检测
没有实体物体只想要检测在指定某一范围是否让敌方受到伤害时便可以使用范围判断
在指定位置进行范围判断,我们可以得到处于指定范围内的对象
目的是对对象进行处理,比如受伤减血等
如何进行范围检测
必备条件
必备条件:想要被范围检测到的对象必须【具备碰撞器】
注意点:
1.范围检测相关API:只有当执行该代码时,进行一次范围检测,它是瞬时的
2.范围检测相关API:并不会真正产生一个碰撞器,只是碰撞判断计算而已
范围检测API
1.盒状范围检测
范围检测API
1.盒状范围检测
参数一:立方体中心点
参数二:立方体三边大小
参数三:立方体角度
参数四:检测指定层级(不填检测所有层)
参数五:是否忽略触发器(UseGlobal(默认)-使用全局设置 Collide-检测触发器 Ignore-忽略触发器)
返回值:在该范围内的触发器(得到了对象触发器就可以得到对象的所有信息)
重要知识点:关于层级
通过名字得到层级编号LayerMask.NameToLayer 我们需要通过编号左移构建二进制数
这样每一个编号的层级都是对应位为1的2进制数
我们通过位运算可以选择想要检测层级
好处一个int就可以表示所有想要检测的层级信息
Collider[] colliders = Physics.OverlapBox(Vector3.zero, Vector3.one,
Quaternion.AngleAxis(45, Vector3.up),
1 << LayerMask.NameToLayer("UI"),
QueryTriggerInteraction.UseGlobal);
for (int i = 0; i < colliders.Length; i++)
{
print(colliders[i].gameObject.name);
}
参数四的层级编号是0~31刚好32位是一个int数
每一个编号代表的都是二进制的一位
例如:
0000 0001 左移5: 1 << 5
0010 0000
另一个API
可以判断有无触发碰撞器
返回值:碰撞到的碰撞器数量
参数:传入一个数组进行存储
if (Physics.OverlapBoxNonAlloc(Vector3.zero, Vector3.one, colliders) != 0){}
2.球形范围检测
2.球形范围检测
参数一:中心点
参数二:球半径
参数三:检测指定层级(不填检测所有层)
参数四:是否忽略触发器(UseGlobal(默认)-使用全局设置 Collide-检测触发器 Ignore-忽略触发器)
返回值:在该范围内的触发器(得到了对象触发器就可以得到对象的所有信息)
Physics.OverlapSphere(Vector3.zero,3,1<<LayerMask.NameToLayer("UI"));
另一个API
可以判断有无触发碰撞器
返回值:碰撞到的碰撞器数量
参数:传入一个数组进行存储
if (Physics.OverlapSphereNonAlloc(Vector3.zero, 3, colliders) != 0){}
3.胶囊范围检测
3.胶囊范围检测
参数一:半圆中心点
参数二:半圆二中心点
参数三:半圆半径
参数四:检测指定层级(不填检测所有层)
参数五:是否忽略触发器(UseGlobal(默认)-使用全局设置 Collide-检测触发器 Ignore-忽略触发器)
返回值:在该范围内的触发器(得到了对象触发器就可以得到对象的所有信息)
Physics.OverlapCapsule(Vector3.zero, Vector3.up, 1, 1 << LayerMask.NameToLayer("UI"),
QueryTriggerInteraction.UseGlobal);
另一个API
可以判断有无触发碰撞器
返回值:碰撞到的碰撞器数量
参数:传入一个数组进行存储
if (Physics.OverlapCapsuleNonAlloc(Vector3.zero, Vector3.up, 1, colliders) != 0){}
总结
范围检测主要用于瞬时的碰撞范围检测
主要掌握
Physics类中的静态方法
球形、盒装、胶囊三种API的使用即可
思考 范围检测
世界坐标原点有一个立方体,键盘WASD键可以控制其前后移动和旋转
请结合所学知识实现
1.按J键在立方体面朝向前方1米处进行立方体范围检测
2.按K键在立方体前面5米范围内进行胶囊范围检测
3.按L键以立方体脚下为原点,半径10米内进行球形范围检测
using UnityEngine;
public class RangeSensing : MonoBehaviour
{
public float moveSpeed = 8;
public float rotateSpeed = 30;
void Update()
{
transform.Translate(Vector3.forward * Time.deltaTime * moveSpeed * Input.GetAxis("Vertical"));
transform.Rotate(Vector3.up, Input.GetAxis("Horizontal") * rotateSpeed * Time.deltaTime);
if (Input.GetKeyDown(KeyCode.J))
{
Collider[] colliders = Physics.OverlapBox(transform.position + transform.forward,
Vector3.one * 0.5f,
transform.rotation,
1 << LayerMask.NameToLayer("Monster"));
for (int i = 0; i < colliders.Length; i++)
{
print(colliders[i].gameObject.name + "受到伤害");
}
}
if (Input.GetKeyDown(KeyCode.K))
{
Collider[] colliders = Physics.OverlapCapsule(transform.position,
transform.position + transform.forward * 5,
0.5f,
1<<LayerMask.NameToLayer("Monster"));
for (int i = 0; i < colliders.Length; i++)
{
print(colliders[i].gameObject.name + "受到伤害");
}
}
if (Input.GetKeyDown(KeyCode.L))
{
Collider[] colliders = Physics.OverlapSphere(transform.position, 10, 1 << LayerMask.NameToLayer("Monster"));
for (int i = 0; i < colliders.Length; i++)
{
print(colliders[i].gameObject.name + "受到伤害");
}
}
}
}
2、物理系统之射线检测
1、什么是射线检测
物理系统中
目前我们学习的物体相交判断
1.碰撞检测的必备条件
1刚体 2碰撞器
2.范围检测的必备条件
碰撞器
如果想要做这样的撞检测呢
1.鼠标选择场景上一物体
2.FPS射击游戏(无弹道-不产生实际的子弹对象进行移动)
等等需要判断一条线和物体的碰撞情况
射线检测就是来解决这些问题的
它可以在指定点发射一个指定方向的射线
判断该射线与哪些碰撞器相交,得到对应对象
2、射线对象
1,3D世界中的射线假设有一条
起点为坐标(1,0,0)
方向为世界坐标Z轴正方向的射线
注意:
理解参数含义
参数一:起点
参数二:方向向量(一定记住不是两点决定射线方向,第二个参数直接就代表方向向量)
Ray ray = new Ray(Vector3.right,Vector3.forward);
Ray中的参数
ray.origin;
ray.direction;
2,摄像象机发射出的射线
得到一条从屏幕位置作为起点
摄像机视口方向为方向的射线
Ray ray2 = Camera.main.ScreenPointToRay(Input.mousePosition);
注意
单独的射线对于我们来说没有实际的意义
我们需要用它结合物理系统进行射线碰撞判断
3、碰撞检测函数
Physics类中提供了很多进行射线检测的静态函数
它们有很多种重载类型,我们只需要掌握核心的几个函数
注意
射线检测也是瞬时的
执行代码时进行一次射线检测
1.最原始的射线检测(只会判断是否碰撞)
准备一条射线
Ray ray = new Ray(Vector3.zero,Vector3.forward);
进行射线检测,如果碰撞到对象,则返回true
参数一:射线
参数二:检测的最大距离超出这个距离不检测
参数三:检测指定层级(不填检测所有层)
参数四:是否忽略触发器(UseGlobal(默认)-使用全局设置 Collide-检测触发器 Ignore-忽略触发器)
返回值:bool 当碰撞到对象时 返回true,没有返回false
if (Physics.Raycast(ray, 1000, 1 << LayerMask.NameToLayer("UI"), QueryTriggerInteraction.UseGlobal))
{
print("碰撞到了对象");
}
还有一种重载不用传入射线直接传入起点和方向也可以用于判断
就是把第一个参数,变成了射线的两个点(一个起点,一个方向)
if (Physics.Raycast(Vector3.zero,Vector3.forward, 1000, 1 << LayerMask.NameToLayer("UI"), QueryTriggerInteraction.UseGlobal))
{
print("碰撞到了对象");
}
2.获取相交的【单个物体信息】
物体信息类RaycastHit
RaycastHit hitInfo;
参数一:射线
参数二:RaycastHit是结构体,是值类型Unity会通过out关键在在函数内部处理后,得到碰撞数据后返回到该参数中
参数三:检测距离
参数四:检测指定层级(不填检测所有层)
参数五:是否忽略触发器(UseGlobal(默认)-使用全局设置 Collide-检测触发器 Ignore-忽略触发器)
if(Physics.Raycast(ray,out hitInfo,1000,1<<LayerMask.NameToLayer("UI"),QueryTriggerInteraction.UseGlobal))
{
print("得到了碰撞到物体的信息");
print("碰撞到物体的名字" + hitInfo.collider.name);
print("碰撞到物体的名字" + hitInfo.collider.gameObject.name);
print("相交位置" + hitInfo.point);
print("法线" + hitInfo.normal);
print("对象的位置" + hitInfo.transform.position);
print("距离" + hitInfo.distance);
}
另一种重载,不用传入射线(直接传入起点和方向也可以用于判断)
if (Physics.Raycast(Vector3.zero, Vector3.forward, out hitInfo, 1000, 1 << LayerMask.NameToLayer("UI"), QueryTriggerInteraction.UseGlobal)){ }
3.获取相交的【多个物体信息】
可以得到碰撞到的多个对象
如果没有就是容量为0的数组
参数一:射线
参数二:检测距离
参数三:检测指定层级(不填检测所有层)
参数四:是否忽略触发器(UseGlobal(默认)-使用全局设置 Collide-检测触发器 Ignore-忽略触发器)
RaycastHit[] hits = Physics.RaycastAll(ray,1000,1<<LayerMask.NameToLayer("UI"),QueryTriggerInteraction.UseGlobal);
for (int i = 0; i < hits.Length; i++)
{
print(hits[i].collider.name);
}
另一种重载,不用传入射线(直接传入起点和方向也可以用于判断)
RaycastHit[] hits = Physics.RaycastAll(Vector3.zero, Vector3.forward, 1000,
1<<LayerMask.NameToLayer("UI"),QueryTriggerInteraction.UseGlobal);
还有一种函数返回的碰撞的数量,通过out得到数据
if(Physics.RaycastNonAlloc(ray, hits, 100, 1 << LayerMask.NameToLayer("UI"), QueryTriggerInteraction.UseGlobal) > 0)
{
}
4、使用时注意的问题
注意(使用参数)
距离、层级两个参数都是int类型
当我们传入参数时一定要明确传入的参数代表的是距离还是层级
举例
if(Physics.Raycast(ray,1<<LayerMask.NameToLayer("UI")){}
这样写是错误的,第二个参数虽然是float但编辑器不会提示错误,因为第二个参数代表的是距离,不是层级
思考1 子弹射击到物体效果
1.请用资料区给的资源,实现鼠标点击场景上一面墙,在点击的位置创建子弹特效和弹孔
using UnityEngine;
public class ShootBullet : MonoBehaviour
{
RaycastHit info;
void Update()
{
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
Debug.DrawRay(ray.origin, ray.direction);
if (Input.GetMouseButtonDown(0))
{
if (Physics.Raycast(Camera.main.ScreenPointToRay(Input.mousePosition), out info, 100, 1 << LayerMask.NameToLayer("Wall")))
{
GameObject hitObj = Instantiate(Resources.Load<GameObject>("Effect/BoomEff"));
hitObj.transform.position = info.point + info.normal * 0.1f;
hitObj.transform.rotation = Quaternion.LookRotation(info.normal);
Destroy(hitObj, 0.8f);
hitObj = Instantiate(Resources.Load<GameObject>("Effect/BulletHole"));
hitObj.transform.position = info.point + info.normal * 0.1f;
hitObj.transform.rotation = Quaternion.LookRotation(info.normal);
}
}
}
}
思考2 控制场景物体
2.场景上有一个平面,有一个立方体,当鼠标点击选中立方体时,
长按鼠标左键可以拖动立方体在平面上移动,松开鼠标左键取消选中
using UnityEngine;
public class SceneObject : MonoBehaviour
{
private Transform nowSelObj;
RaycastHit info;
void Update()
{
if (Input.GetMouseButtonDown(0))
{
if (Physics.Raycast(Camera.main.ScreenPointToRay(Input.mousePosition), out info, 1000, 1 << LayerMask.NameToLayer("Monster")))
{
nowSelObj = info.transform;
}
}
if (Input.GetMouseButton(0) && nowSelObj != null)
{
if (Physics.Raycast(Camera.main.ScreenPointToRay(Input.mousePosition), out info, 1000, 1 << LayerMask.NameToLayer("Wall")))
{
nowSelObj.position = info.point + Vector3.up * nowSelObj.transform.position.y;
}
}
if (Input.GetMouseButtonUp(0))
{
nowSelObj = null;
}
}
}