5

我有一个游戏(基于 MonoGame / XNA),其更新方法如下:

public void Update(GameTime gameTime)
{
    component.Update(gameTime);
}

我想将其转换为反应模式。我目前的解决方案是:

public void Initialize()
{
    updateSubject = new Subject<GameTime>();

    component = new Component();

    updateSubject.Subscribe((gameTime) => component.Update(gameTime));
}

public void Update(GameTime gameTime)
{
    updateSubject.OnNext(gameTime);
}

我是 Rx 的新手,所以我仍在学习最好的做事方式。我读到Subject应该避免,Observable.Create应该使用它。

Subject这里合适吗?

在这种情况下我该如何使用Observable.Create

4

2 回答 2

8

您在这里面临的关键问题是您需要一个可观察的来源。.Interval通常,您可以从来自事件、委托、任务、许多可观察扩展(如or .Generate)和主题的各种来源创建可观察对象。

在您的情况下,您必须有一个源代码,您可以在可观察的外部将值推送到该代码。在这种情况下,主题非常好,但您也可以使用委托。

如果您使用主题,那么您的代码就可以了。唯一的缺点是你可以调用updateSubject.OnCompleted并完成 observable。

如果您想使用委托,那么您的代码可能如下所示:

private Action<GameTime> updateGameTime = null;

public void Initialize()
{
    component = new Component();

    Observable
        .FromEvent<GameTime>(a => updateGameTime += a, a => updateGameTime -= a)
        .Subscribe((gameTime) => component.Update(gameTime));
}

public void Update(GameTime gameTime)
{
    updateGameTime(gameTime);
}

这样,您唯一可以做的updateGameTime就是传入一个新的GameTime- 您不能“意外”结束序列。

现在使用主题与使用主题的整个问题Observable.Create是状态之一。在您的代码中,您需要状态,因此主题是可以的。但总的来说,只要有可能,最好封装状态——这就是Observable.Create为你做的。

举个例子:

var i = -1;
var query =
    Observable
        .Range(0, 10).Select(x =>
        {
            i = -i * 2;
            return x * i;
        });

如果我两次订阅这个 observable,我会得到这两个序列:

(1)

0
-4
16
-48
128
-320
768
-1792
4096
-9216

(2)

0
-4096
16384
-49152
131072
-327680
786432
-1835008
4194304
-9437184

因为我使用了状态(即var i = -1;),所以顺序发生了变化。

如果我用我编写代码,Observable.Create我可以避免这种状态:

var query =
    Observable
        .Create<int>(o =>
        {
            var i = -1;
            return
                Observable
                .Range(0, 10).Select(x =>
                {
                    i = -i * 2;
                    return x * i;
                })
                .Subscribe(o);
        });

它仍然是相同的查询,但状态是封装的,所以如果我现在订阅两次,我会得到:

(1)

0
-4
16
-48
128
-320
768
-1792
4096
-9216

(2)

0
-4
16
-48
128
-320
768
-1792
4096
-9216

有时在编写复杂的查询时,您可能会认为使用主题会更容易,而一般来说,这就是发生错误的地方。在这种情况下,在使用主题之前,您应该始终尝试找到一种纯运算符方法。如果不能,则将主题的使用封装在Observable.Create.

像你这样的时代,使用一个主题很好,因为你需要那个外部状态。

于 2016-07-04T04:15:17.170 回答
1

只是指出你的代码不必要地使用了 Rx。

public void Initialize()
{
    //updateSubject = new Subject<GameTime>();

    component = new Component();

    //updateSubject.Subscribe((gameTime) => component.Update(gameTime));
}

public void Update(GameTime gameTime)
{
    //updateSubject.OnNext(gameTime);
    component.Update(gameTime)
}

在这里,我删除了Subjectand 直接调用componentsUpdate方法,以说明这一点。

也许您正在寻找一种私有的轮询方法?在这种情况下Observable.Interval,可能是一个很好的起点。

于 2016-07-04T06:50:45.693 回答