Передать функцию в качестве параметра между классами - PullRequest
0 голосов
/ 26 мая 2018

У меня есть два класса на основе Unity, один из которых представляет объект Player, а другой представляет powerup, который должен иметь возможность манипулировать игроком.

Моя идея состоит в том, чтобы инкапсулировать операции манипулирования игроком в методекоторый может быть передан объекту игрока, например, когда игрок сталкивается с powerup.

Эти манипуляции могут включать в себя простые изменения поля и свойств (очки здоровья), но также, возможно, сопрограммы (автоматический выстрел или щит свременной интервал).

В настоящее время мой класс проигрывателя хранит все связанные с 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, из объекта игрока и сохранить его в соответствующих powerups.

Есть литехническая возможность содержать такие операции в методе, который может бытьоценивается от объекта powerup до объекта player, что-то вроде этого?В лучшем случае объект 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 голосов
/ 26 мая 2018

Позвольте мне начать с того, что я думаю, что передача ссылки на метод является неправильным решением для этого случая.Я бы просто передал ссылку Player на метод внутри класса Powerup.

Сказав, что использование делегатов является важной и мощной частью программирования .Net, поэтому я подумал, что лучше дать вам небольшое введение в это - вот так:

Это возможнои даже довольно легко передавать ссылки на методы, используя делегаты.Начиная с версии .Net 2.0 вы можете использовать для этого предопределенные системные делегаты Action и Func.
Их множество, начиная от отсутствия аргументов до 16 общих.аргументы, означающие, что вам очень редко придется писать делегату по старинке, как мы это делали в .Net 1.1.(давным-давно)

Все Action делегаты представляют void методы, а все Func делегаты представляют метод, который возвращает значение.Довольно легко (и это очень распространенная практика) создать метод, который принимает Action или Func в качестве аргумента и вызывает его - и многие из встроенных классов и методов расширения .Net Framework делают это,такие как 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.

...