系列文章链接
本文目录
项目创建2D URP
安装Unity编辑器
- 本次学习案例使用的是2022.3.9f1c1,理论上2022的LTS版本均可使用,不一定需要2022.3.9f1c1,初学者非必要情况不建议使用2023及以上的版本,因为2023版本中有部分函数方法是过时的,会有警告提示(如下图),需要手动调整并修改(比较简单但可以不需要这一步),当前版本不会影响使用,但每次报错会比较烦。(评论可以求2023修改后插件版本)
新建项目
- 2022的LTS版本
- 2D URP
- 项目名称英文
- (建议)关闭Unity的版本管理
导入素材
- 如果已经购买插件和领取了素材的话,可以选择Package Manager直接导入
- 或者使用本地素材包导入
2D RaySensor
项目初始准备
- 添加设置一个简易背景
- 选择背景图片,并放大18倍
- 为了区分背景、人物、道具,添加渲染分类层,Sorting Layer,设置Back,Default,Front三层。同时将背景设置为Back层
- 设置Player物体,选择动态物理精灵图,我们采用2D俯视角,不需要重力的影响,因此设置重力倍数为0,设置Player物体为Front层,且Order设置为5,数字越大越在上方叠加,5是一个任意数字,如有需要再增加。替换Sprite为Farmer 0 的Stand 0,同时Player需要选择Capsule Collider 2D,Size设置为X=0.6,Y=1。
- 添加水果图标,直接拖入苹果精灵图,同时要注意!Sensor传感器是基于射线检测和碰撞体的,所以我们在一定情况下可以抛弃刚体,但是必须要有碰撞体。给苹果添加BoxCollider2D组件,调整苹果Transform的值为(0,2,0),位于Player上方。
- 然后复制当前场景,重命名为BaseScene,为以后需要的时候直接复制。
给Player添加2D RaySensor组件
- 直接选择Player,AddComponent 添加2D RaySensor组件,可以看到2DRaySensor有非常多的设置,同时在Scene窗口中可以看到Player的蓝色线,它就是Ray射线的检测长度,非常直观地在Scene窗口中表现出来
- 如果Sensor传感器就在该物体上的时候,可以设置IgnoreList 忽略列表,将自身物体添加进入,点击Test就会过滤掉自身。
- 面板内容很简单,看一遍文档就可以快速理解
链接:RaySensor的属性Properties
RaySensor的事件Events(核心点)
- 点击Events,可以看到一共有6个Event事件
- OnDetected
- OnLostDetection
- OnSomeDetection
- OnNoDetection
- OnObstruction
- OnClear
- 接下来我们进行对所有事件进行验证,新建Scripts文件夹,开始编写C#脚本,新建脚本文件,命名为RaySensorController,并挂载到Player上。
using Micosmo.SensorToolkit;
using UnityEngine;
public class RaySensorController : MonoBehaviour
{
RaySensor2D _raySensor;
void Awake()
{
_raySensor = GetComponent<RaySensor2D>();
}
#region OnDetected事件
// 无参函数
public void PrintDetectionNameNoParam()
{
Debug.Log("检测到物体了,但是没有传入参数,不知道是哪个物体");
}
// 有参函数
public void PrintDetectionNameHaveParam(GameObject obj, Sensor sensor)
{
string objectName = obj.name;
Debug.Log("检测到物体了,传入了两个参数,可以知道检测到的物体是:" + objectName);
}
// 以上两个函数都可以添加到OnDetected事件中,Inspector面板的UnityEvent是可以接受参数类型和数量不一致的方法订阅的。
// 同时该事件的两个参数分别是检测到的物体和检测该物体的传感器Sensor,这个函数是会对每个物体都触发一次的,
// 据事件结果可知,这个事件会在之前没有检测到,但是之后检测到了该物体时,触发一次
#endregion
#region OnLostDetection事件
// 无参函数
public void PrintLostDetectionNameNoParam()
{
Debug.Log("对某个物体丢失检测了,但是没有参数,不知道是哪个物体");
}
// 有参函数
public void PrintLostDetectionNameHaveParam(GameObject obj, Sensor sensor)
{
string objectName = obj.name;
Debug.Log("对某个物体丢失检测了,传入了两个参数,可以知道丢失检测到的物体是:" + objectName);
}
// 以上两个函数都可以添加到OnLostDetection事件中,Inspector面板的UnityEvent如果有参数,是可以接受参数类型和数量不一致的方法订阅的。
// 同时该事件的两个参数分别是检测到的物体和检测该物体的传感器Sensor,这个函数是会对每个物体都触发一次的,
// 据事件结果可知,这个事件会在之前检测到了,但是之后没有检测到该物体时,触发一次
#endregion
#region OnSomeDetection事件
// 无参函数
public void PrintSomeDetectionNameNoParam()
{
Debug.Log("这个传感器之前没有检测到任何物体,现在检测到了物体");
}
// 可以看到OnSomeDetection事件是没有参数的,所以Inspector面板的UnityEvent中不能接受有参方法的订阅
// 据事件结果可知,这个事件会在该传感器之前没有检测到任何物体,但是之后检测到有物体时,触发一次
#endregion
#region OnNoDetection事件
// 无参函数
public void PrintNoDetectionNameNoParam()
{
Debug.Log("这个传感器之前检测到了物体,现在没有检测到任何物体了");
}
// 可以看到OnNoDetection事件是没有参数的,所以Inspector面板的UnityEvent中不能接受有参方法的订阅
// 据事件结果可知,这个事件会在该传感器之前检测到了某个或者某些物体,但是之后没有检测到任何物体时,触发一次
#endregion
#region OnObstruction事件
// 无参函数
public void PrintObstructionNameNoParam()
{
Debug.Log("这个传感器发出的射线被阻挡了,无法继续向前射出");
}
// 有参函数
public void PrintObstructionNameHaveParam(IRayCastingSensor sensor)
{
// RayHit是插件自定义的存储检测到的物体数据的结构体
RayHit obstructionRayHit = sensor.GetObstructionRayHit();
var objectName = obstructionRayHit.GameObject.name;
Debug.Log("现在射线被某个物体阻挡了,它的名字是:" + objectName);
}
// 以上两个函数都可以添加到OnObstruction事件中,Inspector面板的UnityEvent如果有参数,是可以接受参数类型和数量不一致的方法订阅的。
// 同时该事件的参数是继承了IRayCastingSensor接口的Sensor传感器,RaySensor是其中一个。
// 据事件结果可知,这个事件会在射线之前没有被阻碍,但是之后被阻挡时,触发一次
#endregion
#region OnClear事件
// 无参函数
public void PrintClearObstructionNameNoParam()
{
Debug.Log("传感器发出的射线之前被阻挡了,现在变得畅通无阻");
}
// 有参函数
public void PrintClearObstructionNameHaveParam(IRayCastingSensor sensor)
{
// RayHit是插件自定义的存储检测到的物体数据的结构体
RayHit obstructionRayHit = sensor.GetObstructionRayHit();
var distance = obstructionRayHit.Distance;
Debug.Log("之前射线被某个物体阻挡了,这个物体现在的距离是" + distance + ",现在射线处于畅通无阻");
}
// 以上两个函数都可以添加到OnClear事件中,Inspector面板的UnityEvent如果有参数,是可以接受参数类型和数量不一致的方法订阅的。
// 同时该事件的参数是继承了IRayCastingSensor接口的Sensor传感器,RaySensor是其中一个。
// 据事件结果可知,这个事件是在传感器在先前受阻时变得畅通无阻时,触发一次。
#endregion
}
总结
- 以上就是RaySensor2D的一个基础学习验证,核心点在于理解并使用UnityEvent事件。同时Events除了在Inspector面板中拖拽订阅,还可以在代码中进行订阅。
关于代码订阅事件
可以参考CSDN博主「YY-nb」的原创文章分析:Unity 事件番外篇:UnityEvent
- 在代码中使用AddListener 和 RemoveListener 操作的就是非持久化监听器。
- 它只能在程序运行时被添加、移除。所以它也叫 runtime listener。
- 非持久化监听器无法在 Inspector 面板中显现,因此它是不可序列化的。
- 非持久化监听器是强引用,也就是物体被销毁后,非持久化监听器不会被移除。因为监听器方法和委托绑定的缘故导致监听器所属的对象仍然存在于内存中,无法被释放。除非我们在物体销毁时移除监听器。
- 持久化监听器则不一样。
- 它可以在 Unity 编辑器的 Inspector 面板中显现,它是可序列化的。
- 只能通过面板来添加和移除事件处理器。
- 持久化监听器是弱引用,物体销毁后监听器在内存中会被释放。