【C#基础】事件(event)是什么?在游戏有什么应用场景?

概念

C# 中,事件event用于实现基于委托的回调机制。

事件允许对象通知其他对象特定的操作已经发生,这些对象可以选择性地对这些操作进行响应。

事件通常用于实现一种发布-订阅模型,其中一个对象(发布者)发出事件,而其他对象(订阅者)侦听事件并在事件发生时执行其操作。

应用场景:

我们假设一个场景,现在如下图有一名角色升级,而升级需要足够exp

  • 如果获得的exp<升级所需的exp,发布者表示经验还不够,那么订阅者不予理会;
  • 如果获得的exp ≥ \ge 升级所需的exp,那么订阅者就要产生相应的升级画面效果

    除了升级这个示例,还有像技能树,使用物品等应用。

事件的优点

  • 将发布者和订阅者解耦,使它们不需要相互了解彼此的实现,这使得系统更加灵活,更容易维护和扩展。
  • 事件还提供了一种统一的方式来扩展类的功能。

看到以上的优点,对于小白来说,可能不知道在讲什么,别担心,让我们继续往下看

如何创建事件

C# 中,事件由委托和事件关键字组合而成。那么一个基本流程如下:

  1. 声明一个事件时,需要先声明一个委托类型,然后使用 event 关键字将委托类型声明为事件。
  2. 订阅者可以通过使用 += 运算符将其方法添加到事件的委托列表中,以便在事件发生时被调用。
  3. 发布者可以通过调用事件的委托列表来触发事件。

以下展示大名鼎鼎的CodeMonkey的 C# 事件示例:

步骤一:创建发布者和订阅者的C#脚本,共同AddComponent到同一个GameObject

步骤二:定义一个发布者

定义三个事件

  • OnSpacePressed事件,该事件是一个带有OnSpacePressedArgs参数的EventHandler委托类型,表示按下空格键时发生的事件。
  • OnFloatEvent事件,该事件是一个TestEventDelegate委托类型,表示在任何时间都可以触发的事件。
  • OnActionEvent事件,该事件是一个Action委托类型,带有boolint参数,表示在任何时间都可以触发的事件。

Update方法

如果检测到按下空格键,将会触发OnSpacePressed、OnFloatEvent和OnActionEvent事件。
OnSpacePressed事件中(包括注释的内容),使用EventArgs.Empty传递空参数,表示此事件不需要任何参数,但我们也可以使用自定义参数OnSpacePressedArgs

OnFloatEvent事件和OnActionEvent事件中,使用Invoke方法来触发事件,并将需要的参数传递给事件处理方法。
TestingEvents.cs

using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Events;

public class TestingEvents : MonoBehaviour
{
    
    
	// 声明一个事件
    public event EventHandler<OnSpacePressedArgs> OnSpacePressed;
    public class OnSpacePressedArgs : EventArgs
    {
    
    
        public int spaceCount;
    }
    private int spaceCount;
    public event TestEventDelegate OnFloatEvent;
    //定义一个委托类型
    public delegate void TestEventDelegate(float f);
    public event Action<bool, int> OnActionEvent;
    //统一的安全API,方便定义委托类型
    public UnityEvent OnUnityEvent;
    // Start is called before the first frame update
    void Start(){
    
    }
    // Update is called once per frame
    // 在触发事件
    void Update()
    {
    
    
        if (Input.GetKeyDown(KeyCode.Space))
        {
    
    
            spaceCount++;
            //Space pressed!
            //if(OnSpacePressed!= null) OnSpacePressed(this,EventArgs.Empty);
            OnSpacePressed?.Invoke(this, new OnSpacePressedArgs {
    
     spaceCount = spaceCount});

            OnFloatEvent?.Invoke(5.5f);

            OnActionEvent?.Invoke(true,56);

            OnUnityEvent?.Invoke();
        }
    }
}

步骤三:实现相应的订阅者

start方法

订阅提到的三个事件,在Start方法中,获取了TestingEvents组件,并将其事件与TestingEventSubscriber类中的方法关联起来,以便在事件触发时执行这些方法。这里订阅的事件分别是OnSpacePressed、OnFloatEvent和OnActionEvent

定义处理这三个事件方法

当事件被触发时,这些TestingEvents_OnSpacePressed、TestingEvents_OnFloatEvent和TestingEvents_OnActionEvent方法将被调用,并按照其相应事件所期望的参数来执行。
每个方法都有一个特定的行为,例如,在TestingEvents_OnSpacePressed方法中,会输出一个带有空格键按下次数的调试信息。

公共方法

TestingEventSubscriber类还定义了一个名为TestingUnityEvent的公共方法,它只输出一条调试信息,以演示如何使用方法作为事件处理程序。
TestingEventSubscriber.cs

using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class TestingEventSubscriber : MonoBehaviour
{
    
    
    // Start is called before the first frame update
    void Start()
    {
    
    
        //作为获得订阅的行为
        TestingEvents testingEvents =GetComponent<TestingEvents>();
        testingEvents.OnSpacePressed += TestingEvents_OnSpacePressed;
        testingEvents.OnFloatEvent += TestingEvents_OnFloatEvent;
        testingEvents.OnActionEvent += TestingEvents_OnActionEvent;

    }

    public void TestingUnityEvent()
    {
    
    
        Debug.Log("TestingUnityEvent");
    }

    private void TestingEvents_OnActionEvent(bool arg1,int arg2) 
    {
    
    
        Debug.Log(arg1 + " " + arg2);
    }

    private void TestingEvents_OnFloatEvent(float f)
    {
    
    
        Debug.Log("Float:" +f );
    }

    private void TestingEvents_OnSpacePressed(object sender, TestingEvents.OnSpacePressedArgs e)
    {
    
    
        Debug.Log("Space!"+e.spaceCount);
        //取消订阅
        //TestingEvents testingEvents = GetComponent<TestingEvents>();
        //testingEvents.OnSpacePressed -= TestingEvents_OnSpacePressed;
    }

    // Update is called once per frame
    void Update(){
    
    }
}

非小白忽略:调试看unityconsole

注意事项:如何用公共方法?

先找到你的GameObject
在这里插入图片描述
再选择好你的公共方法
在这里插入图片描述

回到之前的疑惑

所谓的解耦,你可以去观察为什么发布者不用start方法?而是使用updata方法?
因为我们不希望把订阅者的实现写到发布者的类中。

公共方法其实就是个API,用就对了。

当然不要这样回答面试官问题,人家看你回答那么简单,会鄙视你的。

合作对象

强大的人工只能chatGpt3.5

参考

CodeMonkey的C# Basic视频,分析它的视频内容的是一名2$/month的卑微学生。

猜你喜欢

转载自blog.csdn.net/kokool/article/details/129772271