【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是生活!如果你遇到任何问题,也欢迎你评论私信找我, 虽然有些问题我也不一定会,但是我会查阅各方资料,争取给出最好的建议,希望可以帮助更多想学编程的人,共勉~

相关推荐
一个笔记本7 小时前
godot log | 修改main scene
游戏引擎·godot
nnsix8 小时前
Unity PicoVR开发 实时预览Unity场景 在Pico设备中(串流)
unity·游戏引擎
强哥8312 小时前
木筏求生 游戏MOD
游戏
全栈若城13 小时前
使用 CodeBuddy IDE + CloudBase 一站式开发卡片翻翻翻游戏
游戏·aiide·codebuddy推荐官·cloudbase
一只一只14 小时前
Unity之UGUI Button按钮组件详细使用教程
unity·游戏引擎·ugui·button·ugui button
神米米16 小时前
Maya快速安装UE4 布料权重绘制插件PhysX导出apx
游戏引擎·ue4·maya
WarPigs17 小时前
Unity阴影
unity·游戏引擎
无限大.18 小时前
为什么游戏需要“加载时间“?——从硬盘读取到内存渲染
网络·人工智能·游戏
一只一只18 小时前
Unity之Invoke
unity·游戏引擎·invoke
TESmart碲视19 小时前
TESmart 推出全新 DP 1.4 双 8K@60Hz KVM 切换游戏扩展坞,助力专业与游戏工作流高效整合
游戏·计算机外设·音视频·kvm切换器·tesmart