将函数作为类

时间:2018-05-26 11:52:30

标签: c# function unity3d parameter-passing

我有两个基于Unity的类,其中一个表示Player对象,另一个表示应该能够操作播放器的通电。

我的想法是将玩家操纵操作封装在可以传递给玩家对象的方法中,例如,当玩家与通电相撞时。

这些操作可以包括简单的场和属性变化(健康点),但也可能包括Coroutines(自动射击或具有时间间隔的盾牌)。

目前,我的播放器类本身包含所有与powerup相关的操作,并根据碰撞时从powerup对象传递的字符串调用它们:

PlayerController.cs

public class PlayerController : MonoBehaviour
{
    private Dictionary<string, UnityAction> powerupMethods;

    // ...

    public void ActivatePowerUp(string name)
    {
        Invoke(powerUpMethods[name]);
    }
}

Powerup.cs

public class Powerup : MonoBehaviour
{
   private string _name;

   // ...

   private void OnCollision2D(Collision col)
   {
       PlayerController p = col.collider.GetComponent<PlayerController>();
       if (p != null)
       {
           p.ActivatePowerup(_name);
       }
   }
}

我的愿望是从播放器对象中消除所有与powerup相关的逻辑,并将其保存在各自的通电中。

是否有技术可能在一个可以从powerup对象传递给player对象的方法中包含这样的操作,这样的话?最好的情况是玩家对象可以访问方法中的私有字段。

// Powerup.cs
public void Ability()
{
    // Do stuff with Player object, even private fields
    // e.g. player._health++;
    // But also activate self-based Coroutines
    // e.g. StartCoroutine(CoroutineMethod());
}

private IEnumerator CoroutineMethod()
{
    // e.g. auto fire for 5 seconds
}

private void OnCollision2D(Collision2D col)
{
    PlayerController p = col.collider.GetComponent<PlayerController>();
    if (p != null) p.Invoke(Ability());
}

1 个答案:

答案 0 :(得分:3)

首先我想说,我认为传递方法的引用对于这种情况是错误的解决方案。我只是将Player的引用传递给Powerup类中的方法。

话虽如此,使用代理是.Net编程的一个重要而有力的部分,所以我想我最好给你一点介绍 - 所以这里有:

使用委托来传递对方法的引用是可能的,甚至很容易。自.Net 2.0版本起,您可以使用ActionFunc预定义的系统代理。
它们中有很多,介于16个通用参数之间没有参数,这意味着你必须像我们在.Net 1.1中那样用旧的时尚方式编写委托。 (很久很久以前)

所有Action个委托代表void个方法,所有Func个委托代表返回值的方法。创建一个接受ActionFunc作为参数并调用它的方法(以及许多内置类和扩展方法)非常简单(也是一种非常常见的做法) .Net框架执行此操作,例如List<T>.ForEach和linq方法几乎完全基于Func个委托。

让我们以linq为对象Any()方法源代码(因为它很简单):

public static bool Any<TSource>(this IEnumerable<TSource> source, 
                                Func<TSource, bool> predicate) {
    if (source == null) throw Error.ArgumentNull("source");
    if (predicate == null) throw Error.ArgumentNull("predicate");
    foreach (TSource element in source) {
        if (predicate(element)) return true;
    }
    return false;
}

正如您所看到的,它需要Func<TSource, bool>并在IEnumerable中的每个项目上调用它,直到它找到谓词返回true的项目,或者它到达IEnumerable的结尾并返回false