unity3d 如何在Unity/C#中向无关的游戏对象发送事件/消息?

knsnq2tg  于 2023-02-09  发布在  C#
关注(0)|答案(2)|浏览(186)

假设我有两个实体,一个玩家和一个敌人。他们每个人都有自己的C#脚本。在敌人上,它有这样的基本健康管理器代码:

[SerializeField] float health = 3;

public void TakeDamage(float damage)
{
    health -= damage;

    if (health <= 0) {
        Destroy(gameObject);
        // SOME EVENT HERE?
    }
}

我想要的是让玩家知道他已经杀死了敌人(以及知道他消灭了哪个敌人)。但是,我不知道如何做到这一点。我已经看了几种方法:

  • 行动/代表;他们会要求玩家从敌人那里"导入"一个公共行为/代理(如果我理解正确的话;我对C#还是新手),我不希望这两件事之间有依赖关系......它们在概念上是不相关的;我不应该有'进口'从敌人的球员。
  • 使用Unity事件(如您在UI中配置的事件,例如按钮OnClick());这也不起作用,因为Player和Enemy都可能在运行时示例化,而不是预定义。

在我的脑海里,我想象播放器脚本会有这样的东西来监听事件:

void OnEnemyDestroyed(GameObject enemy) { ...do things in reaction to enemy death here... }

这可能吗?

dfddblmv

dfddblmv1#

对于这种特殊情况,我的建议是使用接口。例如:

public interface IUnit
{
    void OnEnemyKilled(IUnit enemy);
}

玩家和敌人脚本都将实现此接口,TakeDamage方法还需要附加一个类型为IUnit的新参数。

class Enemy : IUnit
{
    public void TakeDamage(float damage, IUnit attacker = null)
    {
        health -= damage;

        if (health <= 0) {
            attacker?.OnEnemyKilled(this);
            Destroy(gameObject);
        }
    }

    public void OnEnemyKilled(IUnit enemy){}
}

这种方法不仅解决了问题,其优点是在某些情况下,单个伤害值是不够的,您可能需要关于谁进行了此攻击的信息,然后可以向接口添加其他方法并在TakeDamage中执行它们。
注意,最好预先放置回调函数,这样就可以在回调函数中继续访问已删除实体的所有属性。
要回答评论中的问题,有很多方法都有意义,你可以:
1.通过空值TakeDamage(10f);(更新上述方法)
1.传递默认的IUnit实现

class NotAUnit : IUnit
{
    public static readonly NotAUnit Instance = new();
    public void OnEnemyKilled(IUnit enemy){}
}

TakeDamage(10f, NotAUnit.Instance);

1.重载方法。(与方法1相同)。

py49o6xq

py49o6xq2#

如果它通常有一个全局事件来监听对象的销毁,则可以有一个额外的组件,如

public class DestroyEvent : MonoBehaviour
{
    public static event Action<GameObject> OnDestroyed;

    private void OnDestroy()
    {
        OnDestroyed?.Invoke(gameObject);
    }
}

因此,您可以全局地将侦听器附加到

DestroyEvent.OnDestroyed += Listener;

...

private void Listener(GameObject destroyedObject)
{
    Debug.Log(destroyedObject);

    // e.g. list all attached components
    foreach(var component in destroyedObject.GetComponents<Component>())
    {
        Debug.Log(component.GetType());
    }
}

因此,只要对象在销毁时附加了该组件,您就会收到回调,而不管它是在运行时产生的,还是在运行时之后附加的组件。
请记住,这只是一个简单的示例-如果场景发生变化或您退出应用程序,您还将获得当前的事件。但您可以在此基础上进行构建,例如全局打开事件,并使用附加标志关闭事件等

相关问题