如何在反应式编程中正确匹配序列

时间:2018-02-01 19:58:50

标签: c# unity3d reactive-programming unirx

所以我想在Unity中使用UniRx来尝试反应式编程。我给自己一个简单的任务。为W A S D键按键流,按任意键时打印到屏幕,并在WASD序列发生时显示Combi happened

问题在于,如果序列在流的开头显示为完全,那么它不会被“识别”,也不会发出任何值。

这就是我的意思: 首先按下W A S D ..第一个组合未被识别,但所有后续组合都被识别。 No event

在这里,我在开头按一个不同的字母然后做模式,一切都按预期工作。 No problem

以下是代码:

public class TestController : MonoBehaviour {

    // Use this for initialization
    void Start(){
        var tick = this.UpdateAsObservable();
        var w = tick.Where(_ => Input.GetKeyDown(KeyCode.W)).Select(_ => "W");
        var s = tick.Where(_ => Input.GetKeyDown(KeyCode.S)).Select(_ => "S");
        var d = tick.Where(_ => Input.GetKeyDown(KeyCode.D)).Select(_ => "D");
        var a = tick.Where(_ => Input.GetKeyDown(KeyCode.A)).Select(_ => "A");

        w.Subscribe(Debug.Log).AddTo(this);
        s.Subscribe(Debug.Log).AddTo(this);
        d.Subscribe(Debug.Log).AddTo(this);
        a.Subscribe(Debug.Log).AddTo(this);

        var keys = Observable.Merge(w, a, s, d);

        var Combi = new[]{"W", "A", "S", "D"};
        var combiFound = keys.SelectMany(keys.Take(4).Buffer(4))
            .Where(list => list.SequenceEqual(Combi));

        combiFound.Subscribe(_ => { Debug.LogWarning("Combi Happened!"); }, Debug.LogException).AddTo(this);
    }
}

这到底发生了什么?

2 个答案:

答案 0 :(得分:1)

我没有反应专家,但我认为解释它的是SelectMany。在那里你说只有从缓冲区中提取多个项目时,observable才会填充一个项目,因此会调用subscribe。我把它更改为这个crumby修复程序,所以至少它会从第一次尝试登录,但它并不优雅。我希望看到有人做一个声明。

    var Combi = new[] { "W", "A", "S", "D" };
    var buffer = keys.Take(4).Buffer(4);
    var combiFound = keys.SelectMany(buffer)
          .Where(list => list.SequenceEqual(Combi));

    buffer.Subscribe(CombiHappend(), Debug.LogException).AddTo(this);
    combiFound.Subscribe(CombiHappend(), Debug.LogException).AddTo(this);
}

private static System.Action<IList<string>> CombiHappend()
{
    return (items) => { Debug.LogWarning("Combi Happened!" + string.Join(",", items.ToArray())); };
}

答案 1 :(得分:0)

我很高兴找到这个快速教程演示:Link to solution 这正是我想要实现的目标!

所以这是工作代码:

// Use this for initialization
private void Start(){
    var keyCode = new[]{"W", "A", "S", "D"};
    var updateTick = this.UpdateAsObservable();
    // Get a stream for each key
    var w = updateTick.Where(_ => Input.GetKeyDown(KeyCode.W)).Select(_ => "W");
    var s = updateTick.Where(_ => Input.GetKeyDown(KeyCode.S)).Select(_ => "S");
    var d = updateTick.Where(_ => Input.GetKeyDown(KeyCode.D)).Select(_ => "D");
    var a = updateTick.Where(_ => Input.GetKeyDown(KeyCode.A)).Select(_ => "A");

    // Display the pressed key
    w.Subscribe(Debug.Log).AddTo(this);
    s.Subscribe(Debug.Log).AddTo(this);
    d.Subscribe(Debug.Log).AddTo(this);
    a.Subscribe(Debug.Log).AddTo(this);

    // Stream of all previous keys combined
    var keyStream = Observable.Merge(w, a, s, d);

    // Stream of events when sequence is found
    var keyCodeFound = keyStream.Where(key => key == keyCode.First())
        .SelectMany(key => keyStream.StartWith(key).Buffer(keyCode.Length).First())
        .Where(list => list.SequenceEqual(keyCode));

    // Whenever the sequence is found, a message is displayed on the screen
    keyCodeFound.Subscribe(CombiHappened(), Debug.LogException)
        .AddTo(this);
}

private static System.Action<IList<string>> CombiHappened() => items => {
    Debug.LogWarning("Key combination happened! " + string.Join(",", items.ToArray()));
};

第一种情况:我们直接从序列开始: Starting with sequence

第二种情况:我们先从randoms字母开始,然后是序列 Starting with random letters first

我很开心。现在我应该尝试寻找我指定的任何序列。希望它顺利进行!