Promises with Unity

本文关键字:Unity with Promises | 更新日期: 2023-09-27 18:05:10

我想要一个字符类,从MonoBehavior继承并暴露少量方法:行走,攻击,…

然而,假设两个组件同时使用这些方法,我希望将操作排队,并有一些方法通知组件它们的操作已经执行。

在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
} );

Unity/c#的正确方法是什么?

Promises with Unity

序言

    对于答案,我将使用这个"我想排队的行动"作为你的问题的描述。有很多方法可以解决这个问题。我也不想假装什么都懂。
  1. 即使在JS中,我也会考虑使用Promises来排队字符命令是错误的选择。
  2. 我从来没有编译,运行或测试的代码,我现在在这里:D
c#中的承诺在github上有一些c#的Promises移植。我从未使用过它,但代码似乎不包含任何阻止它在Unity中使用的内容。不管怎样,你可以试一试。 <标题>使用Queue<>

我肯定会使用一些TCommand的Queue<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));
    }
}

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));
    }
}

一个非常好的Unity Promise库:https://github.com/Real-Serious-Games/C-Sharp-Promise,参见相关博客http://www.what-could-possibly-go-wrong.com/promises-for-game-development/

或者,我认为您也可以使用对WalkAttack事件作出反应的观察者进行RX方法。见https://github.com/neuecc/UniRx

现在Unity已经允许你使用:

  • TaskCompletionSource作为promise;
  • 任务作为未来;

但如果Promise本身更适合您的项目,请尝试使用:C-Sharp-Cancellable-Promise库

这是Real-Serious-Games Promise库的一个分支,更新到最新的。net和,支持Promise取消。