团结的承诺

Maë*_*son 2 c# unity-game-engine unityscript

我想要一个Character类,继承MonoBehavior并暴露一些方法:Walk,Attack,......

但是,假设两个组件同时使用这些方法,我想将操作排队并有一些方法来通知组件他们的操作已被执行.

在Javascript标准中,我会做这样的事情:

var Character = function ( ) {
    this._deferred = new Deferred( );
};

Character.prototype.walk = function ( ) {
    return this._deferred = this._deferred.then( function ( ) {
        // Do the actual walk
    } );
};

Character.prototype.attack = function ( ) {
    return this._deferred = this._deferred.then( function ( ) {
        // Do the actual attack
    } );
};

var character = new Character( );

// Component A
character.walk( ).then( function ( ) {
    // Executes when the walk is done
} );

// Component B
character.attack( ).then( function ( ) {
    // Executes when the walk AND attack is done
} );
Run Code Online (Sandbox Code Playgroud)

使用Unity/C#的正确方法是什么?

Ser*_*sch 5

前言

  1. 对于答案,我将使用此"我想将操作排队"作为您问题的描述.
  2. 有很多方法可以解决这个问题.我并不假装全面.
  3. 即使在JS中,我也会考虑使用Promises将字符命令排队作为错误的选择.
  4. 我没有编译,运行或测试我在这里提供的代码:D

C#中的承诺

github上有一些 Promise的C#端口.我从来没有使用它,但代码似乎没有任何阻止它在Unity中使用的东西.无论如何,你可以尝试一下.

使用队列<>

我肯定会使用Queue<TCommand>一些TCommand来解决这个问题.唯一的问题是如何使用TCommand.我会在这里举两个例子.但是,像往常一样,有更多的选择.

一些课

像这样的东西:

public enum CommandUpdateResult
{
    Ongoing,
    Finished
}

public interface ICommand
{
   CommandUpdateResult Update();
}

public class RunCommand: ICommand
{
    // Bla-bla-bla
}

public class AttackCommand: ICommand
{
    // Bla-bla-bla
}


public class Character: MonoBehaviour
{
    private Queue<ICommand> commandQueue;

    public void Awake()
    {
        commandQueue = new Queue<ICommand>();
    }

    public void Update()
    {
        if (commandQueue.Count > 0 && commandQueue.Peek().Update() == CommandUpdateResult.Finished)
            commandQueue.Dequeue();
    }

    public void EnqueueCommand(ICommand command)
    {
        commandQueue.Enqueue(command);
    }
}

public class SomeClassThatUsesCharacter
{
    private Character character;
    public void SomeMethodThatUsesCharacter()
    {
        character.EnqueueCommand(new RunCommand(bla-bla-bla));
        character.EnqueueCommand(new AttackCommand(bla-bla-bla));
    }
}
Run Code Online (Sandbox Code Playgroud)

IEnumerator的

使用IEnumerator最简单(但不是很优雅)的方法是使用它与一些无限的协同程序.

public class Character: MonoBehaviour
{
    private Queue<IEnumerator> commandQueue;

    private IEnumerator CommandQueueCoroutine()
    {
        while (true)
        {
            if (commandQueue.Count > 0)
                yield return StartCoroutine(commandQueue.Peek());
            else
                yield return new WaitForFixedUpdate();
        }
    }

    public void Awake()
    {
        commandQueue = new Queue<ICommand>();
        StartCoroutine(CommandQueueCoroutine());
    }

    public void Update()
    {
        if (commandQueue.Count > 0 && commandQueue.Peek().Update() == CommandUpdateResult.Finished)
            commandQueue.Dequeue();
    }

    public void Enqueue(IEnumerator command)
    {
        commandQueue.Enqueue(command);
    }

    IEnumerator RunCommand()
    {
        while (Jenny.Tells("Run"))
        {
            transform.position.x += 1;
            yield return new WaitForFixedUpdate();
        }
    }

    IEnumerator AttackCommand(BadGuy badGuy)
    {
        badGuy.Die();
        yield break;
    }
}

public class SomeClassThatUsesCharacter
{
    private Character character;

    public void SomeMethodThatUsesCharacter()
    {
        character.Enqueue(character.RunCommand());
        character.Enqueue(character.AttackCommand(someBadGuy));
    }
}
Run Code Online (Sandbox Code Playgroud)