如何在Unity中使用事件来强制一个脚本调用另一个脚本的方法?

huangapple go评论125阅读模式
英文:

How to use events in Unity to force one script to call the method of another script?

问题

我阅读了关于动作(Action)、C#事件(event)和UnityEvent的信息和文档,我理解你最好不要使用UnityEvent,因为它们比C#事件和动作慢,但要使用事件或动作,你需要使用public static来使一个脚本订阅在另一个脚本中的事件,这不安全。

所以问题是,如何在不使用静态的情况下使用动作或事件?

编辑:我如何创建动作

Player脚本

public class Player : MonoBehaviour
{
    public event Action OnHitAction;

    public void TakeDamage(int damage)
    {
        OnHitAction?.Invoke();
    }
}

Animator脚本

public class PlayerAnimator : MonoBehaviour
{
    private Animator _animator;
    private Player _player;

    private void Awake()
    {
        _player = GetComponent<Player>();
        _player.OnHitAction += Hit;
        _animator = GetComponent<Animator>();
    }

    private void Hit()
    {
        _animator.SetTrigger("Hit");
    }
}
英文:

I read information and documentation about action, c# event and UnityEvent, as i understand you better not use UnityEvent because they are slower than c# event and action, but to use event or action you need to use public static to make one script subscribe to an event which is in another script, what is not safely.

So the question is how to use action or event without static right?

Edit: how i made action

Player script
public class Player : MonoBehaviour
{
    public event Action OnHitAction;

    public void TakeDamage(int damage)
    {
        OnHitAction?.Invoke();
    }
}

Animator script
public class PlayerAnimator : MonoBehaviour
{
    private Animator _animator;
    private Player _player;

    private void Awake()
    {
        _player = GetComponent&lt;Player&gt;();
        _player.OnHitAction += Hit;
        _animator = GetComponent&lt;Animator&gt;();
    }

    private void Hit()
    {
        _animator.SetTrigger(&quot;Hit&quot;);
    }
}

答案1

得分: 0

每当有人说“使用X因为Y更慢”时,要自己问一些问题:

  1. 更慢多少?
  2. 绝对数字是多少?
  3. 你的使用情况有多频繁?

所以是的,也许UnityEvents比纯C#事件慢,但这并不意味着你应该完全跳过它们。UnityEvents是在脚本之间进行通信的绝佳方式,特别是在游戏的不同领域之间,比如“加入游戏”按钮和NetworkManager行为 - 它们两者不应该彼此了解,但应该能够进行通信,通过将NetworkManager分配为Button的onClick UnityEvent的目标来轻松实现这一点。

纯事件也用于构建应用程序控制流,特别是与接口一起使用,当不是从UnityEngine.Object派生的对象可以通过DI获取彼此的事件以订阅它们时。您需要确保依赖于对象A的对象B能够直接获取A的实例,或者第三个对象C(中介者)能够订阅B的处理程序(方法或委托)到A的事件上。
事件反转的示例:

委托Vector2InputHandler(Vector2);
接口IInputHandler 
{
    事件Vector2InputHandler OnMoveInputChanged {get;}
    事件Vector2InputHandler OnLookInputChanged {get;}
}
...
// class Player
void Start()
{
    IInputHandler inputHandler = GetComponent&lt;IInputHandler&gt;(); 
    inputHandler.OnMoveInputChanged  += HandleMoveInput;
    inputHandler.OnLookInputChanged += HandleLookInput;
}
void HandleMoveInput(Vector2 input) =&gt; ...
void HandleLookInput(Vector2 input) =&gt; ...

其中“IInputHandler”隐藏了可能在其他程序集中的实现,但在这里我们不需要知道太多。

无论如何,我建议你坚持使用UnityEvents,因为在性能上是可以接受的(直到你开始频繁触发事件),并且在编辑器中提供丰富的功能。

静态事件在处理全局事务时确实有意义,比如应用程序启动、场景加载、多人会话开始等等,任何不经常发生但可能在很多地方使用的事情。

英文:

Every time someone says "use X because Y is slower", ask yourself some questions:

  1. How much slower?
  2. What is the absolute numbers?
  3. And how frequently your use case appears?

So yes, maybe UnityEvents are slower than pure c# events, but that doesn't mean that you should skip them entirely. UnityEvents is a great way to communicate between scripts, especially between domains of your game, i.e. "Join game" Button and NetworkManager behaviour - both of them shouldn't know each other, but should be able to communicate, that is easily achieved by assigning NetworkManager as a target of Buttin's onClick UnityEvent.

Pure events are also used to build application control flow, especially together with interfaces, when objects that are not derived from UnityEngine.Object can subscribe to events of each other by obtainig them through DI. You need to make sure that object B that depends on object A is able to get A's instance directly, or some third object C (mediator) is able to subscribe B's handlers (methods or delegates) to the events of A.
Example of dependency inversion with events:

delegate Vector2InputHandler(Vector2);
interface IInputHandler 
{
    event Vector2InputHandler OnMoveInputChanged {get;}
    event Vector2InputHandler OnLookInputChanged {get;}
}
...
// class Player
void Start()
{
    IInputHandler inputHandler = GetComponent&lt;IInputHandler&gt;(); 
    inputHandler.OnMoveInputChanged  += HandleMoveInput;
    inputHandler.OnLookInputChanged += HandleLookInput;
}
void HandleMoveInput(Vector2 input) =&gt; ...
void HandleLookInput(Vector2 input) =&gt; ...

where the IInputHandler hides the implementation that could be in other assembly than the script that uses it, but we really doesn't need to know much about it here.

Anyway, I recommend you to stick with UnityEvents since they are OK performance-wise (until you start firing really a lot) and offers rich functionality in Editor.

Static events do have sense though when you handling such global things as application startup, scene loading, multiplayer session start etc., anything that happens not frequently and could potentially be used in a lot of places.

huangapple
  • 本文由 发表于 2023年3月15日 20:29:05
  • 转载请务必保留本文链接:https://go.coder-hub.com/75744703.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定