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自身がオブザーバーだったので削除されても問題なく正常にストップします。

