UniRxのシンプルなサンプルの取扱説明書
前(Buttonが押されたら動くGameObjet)
[次(完了の通知)]
(http://qiita.com/Marimoiro/items/31cad556e1675c557be3)
#購読の停止#
以下のコードを見てください
using UnityEngine;
using System.Collections;
using UniRx;
using System;
public class DeadUpdate : Base {
// Use this for initialization
void Start () {
gameObject.transform.position = new Vector2(0, 1f);
//5秒後にgameObjectが死ぬ
Observable.Timer(TimeSpan.FromSeconds(5))
.Subscribe(_ => Destroy(gameObject));
//0.5秒ごとに0.05右に移動
Observable.Interval(TimeSpan.FromMilliseconds(500))
.Subscribe(l => Move(0.5f, 0));
}
private void Move(float x, float y)
{
gameObject.transform.position = new Vector2(
gameObject.transform.position.x + x,
gameObject.transform.position.y + y
);
}
}
このコードは5秒後にgameObject
が破棄されるまで0.5秒毎にカクカクと右に動き続けることを想定したコードです。
しかし、このコードは5秒後にgameObject
の破棄と同時に例外を吐いてしまいます。
Observable.Interval(TimeSpan time)
はtime
ごとに値をプッシュし続ける登録先です。
Observable.Timer(TimeSpan time)
はtime
の後に1回だけ値をプッシュしてくれる登録先です。
それぞれ詳しくはUniRxのシンプルなサンプル その9(TimerとInterval 一定時間後に実行)を参照してください。
これの何が悪いかというとObservable.Interval(TimeSpan.FromMilliseconds(500)).Subscribe(l => Move(0.5f, 0));
で行った登録がgameObjectが破棄された後も生き残っていてMove
を呼びだしちゃってるんですよね。
これはSubscribe
で行った登録の登録解除(購読の停止とかいいます)をちゃんとしなければいけないよということです。
なので、この問題の解消をします。
準備
かなりシンプルに1個だけSafeUpdate
というものをくっつけて作ります。
AddTo
一番簡単な方法は以下のコードになります。
using UnityEngine;
using System.Collections;
using UniRx;
using System;
public class SafeUpdate : Base
{
// Use this for initialization
void Start () {
gameObject.transform.position = new Vector2(0, 1f);
//5秒後にgameObjectが死ぬ
Observable.Timer(TimeSpan.FromSeconds(5))
.Subscribe(_ => Destroy(gameObject));
//0.5秒ごとに0.05右に移動
Observable.Interval(TimeSpan.FromMilliseconds(500))
.Subscribe(l => Move(0.1f, 0))
.AddTo(this);//これを消すと登録が解除できず例外
}
}
このようにSubscribe
の後に.AddTo(Component c);
でc
が破棄されたら一緒に購読停止してくれます。
これが一番簡単な方法ですね。
TakeUntilDestroy
更にTakeUnitilDestroy
とかTakeUntilDisable
とかもあります。
using UnityEngine;
using System.Collections;
using UniRx;
using System;
public class SafeUpdate : Base
{
// Use this for initialization
void Start () {
gameObject.transform.position = new Vector2(0, 1f);
//5秒後にgameObjectが死ぬ
Observable.Timer(TimeSpan.FromSeconds(5))
.Subscribe(_ => Destroy(gameObject));
//0.5秒ごとに0.05右に移動
Observable.Interval(TimeSpan.FromMilliseconds(500))
.TakeUntilDestroy(this)//これを消すと例外
.Subscribe(l => Move(0.1f, 0));
}
}
TakeUntilDestroy(this)
はthis
が破棄されたらプッシュをやめます。
ちなみにですが後で説明する予定ですが、AddTo
ではDispose()
されるだけなのに対し、TakeUntilDestroy
等ではOnCompleted
というプッシュの終了が通知されることになるという違いがあります。
購読の停止(手動)
実はSubscribe
の戻り値はIDisposable
です。
これをDispose
すると購読が停止されます。
なので手動での削除はこんなコードになります。
using UnityEngine;
using System.Collections;
using UniRx;
using System;
public class SafeUpdate : Base
{
// Use this for initialization
void Start () {
gameObject.transform.position = new Vector2(0, 1f);
IDisposable mover = Observable.Interval(TimeSpan.FromMilliseconds(500))
.TakeUntilDestroy(this)
.Subscribe(l => Move(0.1f, 0));
//5秒後にgameObjectが死ぬ
Observable.Timer(TimeSpan.FromSeconds(5)).Subscribe(_ =>
{
mover.Dispose();//moverを破棄して購読停止
Destroy(gameObject);
});
}
}
削除されることが想定されているようなgameObject
を扱うときは登録先とか自分自身の寿命を考えるといいと思います。
ていうか基本AddTo
するといいと思います。
今までのサンプルのthis.UpdateAsObservable()
はthis
自身がオブザーバーだったので削除されても問題なく正常にストップします。