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

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

前言

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


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

至于什么是委托和事件,之前文章我已经有所介绍了,感兴趣可以先去看看:

【unity小技巧】委托(Delegate)的基础使用和介绍

【unity实战】事件(Event)的基本实战使用

实现

1. InteractEvent 类

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

方法:

  • CallInteractEvent 方法: 触发 HasInteracted 事件,如果有订阅者,则调用它们。
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 方法。
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 方法,并传递自身实例。
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 方法: 当宝箱应该打开时调用,可以在其中添加具体的打开宝箱逻辑,例如生成掉落物品。
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 方法来打开宝箱。

开单个箱子

挂载脚本

效果

按钮触发打开很多箱子

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

效果

拾取物品(传参)

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

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

目录
相关文章
|
9天前
|
存储 JSON 关系型数据库
【unity实战】制作unity数据保存和加载系统——大型游戏存储的最优解
【unity实战】制作unity数据保存和加载系统——大型游戏存储的最优解
18 2
|
9天前
|
图形学
【unity实战】Unity中基于瓦片的网格库存系统——类似《逃离塔科夫》的库存系统(下)
【unity实战】Unity中基于瓦片的网格库存系统——类似《逃离塔科夫》的库存系统
10 0
|
9天前
|
图形学 容器
【unity实战】Unity中基于瓦片的网格库存系统——类似《逃离塔科夫》的库存系统(上)
【unity实战】Unity中基于瓦片的网格库存系统——类似《逃离塔科夫》的库存系统
11 0
|
9天前
|
图形学
【unity小技巧】Unity中实现一个战斗连击连招系统,可以动态添加减少连击连招段数功能
【unity小技巧】Unity中实现一个战斗连击连招系统,可以动态添加减少连击连招段数功能
8 0
|
9天前
|
图形学
【制作100个unity游戏之27】使用unity复刻经典游戏《植物大战僵尸》,制作属于自己的植物大战僵尸随机版和杂交版3(附带项目源码)
【制作100个unity游戏之27】使用unity复刻经典游戏《植物大战僵尸》,制作属于自己的植物大战僵尸随机版和杂交版3(附带项目源码)
21 2
|
9天前
|
图形学
【制作100个unity游戏之28】花半天时间用unity复刻童年4399经典小游戏《黄金矿工》(附带项目源码)
【制作100个unity游戏之28】花半天时间用unity复刻童年4399经典小游戏《黄金矿工》(附带项目源码)
22 0
|
9天前
|
图形学
【制作100个unity游戏之29】使用unity复刻经典游戏《愤怒的小鸟》(完结,附带项目源码)(上)
【制作100个unity游戏之29】使用unity复刻经典游戏《愤怒的小鸟》(完结,附带项目源码)
19 2
|
9天前
|
图形学
【制作100个unity游戏之27】使用unity复刻经典游戏《植物大战僵尸》,制作属于自己的植物大战僵尸随机版和杂交版2(附带项目源码)
【制作100个unity游戏之27】使用unity复刻经典游戏《植物大战僵尸》,制作属于自己的植物大战僵尸随机版和杂交版2(附带项目源码)
11 1
|
9天前
|
存储 JSON 图形学
【unity实战】制作unity数据保存和加载系统——小型游戏存储的最优解
【unity实战】制作unity数据保存和加载系统——小型游戏存储的最优解
10 0
|
9天前
|
图形学
【制作100个unity游戏之29】使用unity复刻经典游戏《愤怒的小鸟》(完结,附带项目源码)(下)
【制作100个unity游戏之29】使用unity复刻经典游戏《愤怒的小鸟》(完结,附带项目源码)(下)
18 0