【unity小技巧】unity事件系统创建通用的对象交互的功能

文章目录

前言

游戏开发过程中,要实现玩家和物体之间的交互是非常常见的事情。如果在开发过程中,你希望和箱子交互触发开箱子的方法,和门交互,又触发开门的方法,实现方式其实有很多,比如继承同一个分类或者定义一个接口就是不错的方法,门和箱子都继承这个接口,然后各自重写方法接口里面的触发方法。

但是,如果我说要实现点击一个按钮,打开几个箱子同时开启几个门呢?你可能会说我先获取所有的箱子或者门,然后循环遍历执行里面对应的触发方法不就可以了?当然这种方法是可行的,但是不够优雅。今天我就分享一种使用委托和事件的方式来实现一个通用的物品的交互方式。

至于什么是委托和事件,之前文章我已经有所介绍了,感兴趣可以先去看看:
【unity小技巧】委托(Delegate)的基础使用和介绍
【unity实战】事件(Event)的基本实战使用

实现

1. InteractEvent 类

  • InteractHandler 委托: 定义了一个没有参数和返回值的委托类型。
  • HasInteracted 事件: 事件,当触发时调用所有注册的委托。

方法:

  • CallInteractEvent 方法 : 触发 HasInteracted 事件,如果有订阅者,则调用它们。
csharp 复制代码
public class InteractEvent
{
    public delegate void InteractHandler();
    public event InteractHandler HasInteracted;

    // 调用互动事件
    public void CallInteractEvent() => HasInteracted?.Invoke();
}

2. Interact 类

  • InteractEvent interact : 这是一个 InteractEvent 类的实例,用于处理交互事件。
  • Player player: 用于存储与之交互的玩家实例。

属性和方法:

  • GetInteractEvent 属性 : 返回 InteractEvent 实例。如果 interact 为 null,会在首次访问时初始化。
  • GetPlayer 属性: 返回存储的玩家实例。
  • CallInteract 方法 : 接受一个 Player 参数,设置 player 属性为该参数,然后调用 interactCallInteractEvent 方法。
csharp 复制代码
public class Interact : MonoBehaviour
{
    InteractEvent interact = new InteractEvent();
    Player player;

    // 获取互动事件
    public InteractEvent GetInteractEvent
    {
        get
        {
            if (interact == null)
                interact = new InteractEvent();
            return interact;
        }
    }

    // 获取玩家
    public Player GetPlayer
    {
        get { return player; }
    }

    // 调用互动方法
    public void CallInteract(Player interactedPlayer)
    {
        player = interactedPlayer;
        interact.CallInteractEvent();
    }
}

3. Player 类

  • Update 方法 : 每帧检查玩家是否按下 E 键,如果是,则调用 PlayerInteract 方法。
  • PlayerInteract 方法 : 创建一条射线从相机的视口中心向前发射,然后检测是否击中了特定层级(层0和层3)的物体。如果击中了一个具有 Interact 组件的物体,则调用其 CallInteract 方法,并传递自身实例。
csharp 复制代码
public class Player : MonoBehaviour
{
    void Update()
    {
        // 如果玩家按下E键,进行交互操作
        if (Input.GetKeyDown(KeyCode.E))
        {
            PlayerInteract();
        }
    }

    // 处理玩家交互的函数
    public void PlayerInteract()
    {
        // 定义用于层0和层3的层蒙版,0和3图层都可以满足条件
        var layerMask0 = 1 << 0;
        var layerMask3 = 1 << 3;
        var finalMask = layerMask0 | layerMask3;

        // 从屏幕中心创建一条射线
        Ray ray = Camera.main.ViewportPointToRay(new Vector3(.5f, .5f, 0));

        // 进行射线投射,检查在最终蒙版上是否命中物体(距离不超过15个单位)
        RaycastHit hit;
        if (Physics.Raycast(ray, out hit, 15, finalMask))
        {
            // 从命中的物体获取Interact脚本组件
            Interact interactScript = hit.transform.GetComponent<Interact>();

            // 如果Interact脚本组件存在,调用其CallInteract方法并传递玩家实例
            if (interactScript != null)
            {
                interactScript.CallInteract(this);
            }
        }
    }
}

4. Chest 类

  • Interact openFromInteraction : 存储一个 Interact 类的实例,用于处理与宝箱的交互。

方法:

  • OnEnable 方法 : 当对象激活时,订阅 openFromInteractionHasInteracted 事件到 OpenChest 方法。
  • OnDisable 方法 : 当对象禁用时,取消订阅 openFromInteractionHasInteracted 事件。
  • OpenChest 方法: 当宝箱应该打开时调用,可以在其中添加具体的打开宝箱逻辑,例如生成掉落物品。
csharp 复制代码
public class Chest : MonoBehaviour
{
    public Interact interact;

    // 当对象启用时订阅交互事件
    private void OnEnable()
    {
        if (interact != null)
        {
            interact.GetInteractEvent.HasInteracted += OpenChest;
        }
    }

    // 当对象禁用时取消订阅交互事件
    private void OnDisable()
    {
        if (interact != null)
        {
            interact.GetInteractEvent.HasInteracted -= OpenChest;
        }
    }

    // 处理宝箱打开的函数
    public void OpenChest()
    {
        // 掉落一些酷炫的东西
    }
}

工作流程说明:

  • Player 类 中的 PlayerInteract 方法检测玩家按下 E 键后,发射一条射线检测是否与可交互物体碰撞。
  • 如果射线击中了具有 Interact 组件的物体,就调用其 CallInteract 方法,并传递玩家实例。
  • Interact 类中的 CallInteract 方法将玩家实例存储,并调用其内部的 InteractEvent 实例的 CallInteractEvent 方法,从而触发 HasInteracted 事件。
  • Chest 类中的 OnEnable 方法在对象启用时订阅 HasInteracted 事件,当事件触发时调用 OpenChest 方法来打开宝箱。

开单个箱子

挂载脚本

效果

按钮触发打开很多箱子

我们可以让一个按钮与多个对象进行交互,挂载脚本

效果

拾取物品(传参)

带玩家参数的拾取物品功能,脚本挂载在物体预制体上即可

csharp 复制代码
public class ItemPickup : MonoBehaviour
{
    public string item; // 物品名称
    public int amount; // 物品数量
    public Interact interact; // 拾取物品的交互对象

    private void OnEnable()
    {
        Interact getInteract = GetComponent<Interact>();
        if (getInteract == null)
        {
            getInteract = gameObject.AddComponent<Interact>(); // 如果没有,则添加Interact组件   
        }
        interact = getInteract;
        interact.GetInteractEvent.HasInteracted += InteractPickup;
    }

    private void OnDisable()
    {
        if (interact)
        {
            interact.GetInteractEvent.HasInteracted -= InteractPickup; // 取消监听交互事件
        }
    }

    public void InteractPickup()
    {
        AddItem(interact.GetPlayer); // 执行AddItem方法,传入交互对象的玩家信息
    }

    public void AddItem(Player player)
    {
        // 给玩家添加物品的逻辑在这里实现
    }
}

参考

https://www.youtube.com/watch?v=MdOi9ymb07s

完结

赠人玫瑰,手有余香!如果文章内容对你有所帮助,请不要吝啬你的点赞评论和关注,以便我第一时间收到反馈,你的每一次支持都是我不断创作的最大动力。当然如果你发现了文章中存在错误或者有更好的解决方法,也欢迎评论私信告诉我哦!

好了,我是向宇https://xiangyu.blog.csdn.net

一位在小公司默默奋斗的开发者,出于兴趣爱好,最近开始自学unity,闲暇之余,边学习边记录分享,站在巨人的肩膀上,通过学习前辈们的经验总是会给我很多帮助和启发!php是工作,unity是生活!如果你遇到任何问题,也欢迎你评论私信找我, 虽然有些问题我也不一定会,但是我会查阅各方资料,争取给出最好的建议,希望可以帮助更多想学编程的人,共勉~

相关推荐
国服第二切图仔2 小时前
Electron for 鸿蒙PC项目实战案例之数独游戏
游戏·electron·鸿蒙pc
_大学牲3 小时前
Flutter 勇闯2D像素游戏之路(一):一个 Hero 的诞生
flutter·游戏·游戏开发
jtymyxmz3 小时前
《Unity Shader》10.1.4 折射
unity·游戏引擎
wanhengidc3 小时前
云手机 多端互通 科技
运维·服务器·科技·游戏·智能手机
在路上看风景4 小时前
12. Burst
unity
平行云PVT6 小时前
实时云渲染解决UE5 像素流插件迁移及传输数据受限问题
unity·ue5·xr·实时云渲染·云桌面·像素流·云推流
星空露珠9 小时前
lua获取随机颜色rgb转换hex
数据结构·数据库·算法·游戏·lua
熬夜敲代码的小N9 小时前
Unity WebRequest高级操作:构建高效稳定的网络通信模块
android·数据结构·unity·游戏引擎
萘柰奈9 小时前
Unity【小问题】----URP项目中加载AssetBundle中的预设体即使加载了依赖的材质依然是紫色的问题
unity·游戏引擎·材质
wonder1357911 小时前
UGUI合批分析和优化方法整理
unity·ugui