読者です 読者をやめる 読者になる 読者になる

いんでぃーづ

個人でゲーム開発してる上で吸収したモノたち紹介。UnityからGIMPまでなんでも。デザインとかゲーム論まで語っちゃうよ的なアレ。

MENU

UniRxでアドベンチャーゲームのテキスト表示を実装

UniRx Unity C#

最近UniRxにハマっていまして、今回はアドベンチャーゲームなど汎用的に使えるテキストシステムをUniRxで書いてみました。

以下のような動作になります。

f:id:sugar_affordance:20170113123408g:plain

かなり"それっぽく"見えるでしょう。

ソース

とりあえずソース貼ります。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using UniRx;
using System;

public class UniRxTextSystem : MonoBehaviour {

    [SerializeField]
    Text TextWindow;
    [SerializeField]
    Image NextTextImage;

    [SerializeField]
    List<string> ShowTextList;

    public string showText;

    // Use this for initialization
    void Start () {
        ShowNextText ();
    }
    void ShowNextText() {
        ShowText(ShowTextList [0]);
        ShowTextList.RemoveAt (0);
    }
    void ShowText(string showText) {

        TextWindow.text = "";    // TextWindow = uGUIのText
        NextTextImage.enabled = false; // NextTextImage = 次のテキストを表示するアイコン

        // クリックしたときに全表示するための待ちを作成
        IDisposable flushDisposable = Observable.EveryUpdate()
            .Where(frame => Input.GetMouseButtonDown(0))
            .Subscribe(frame => TextWindow.text = showText);

        // 一定間隔でテキストを表示していく
        Observable.Interval(TimeSpan.FromMilliseconds(300))
            .Scan((prev, current) => prev + 1)
            .TakeUntil(
                // テキストが全部表示されたら終了
                TextWindow.ObserveEveryValueChanged(textWindow => textWindow.text)
                .Where(text => showText.Length <= text.Length )
            )
            .Subscribe(
                (textLength) => TextWindow.text = showText.Substring(0, (int)textLength),
                () => {
                    flushDisposable.Dispose();  // クリック時の全表示待ちを終了

                    // アイコン点滅
                    Observable.Interval(TimeSpan.FromMilliseconds(200))
                        .Scan((prev, current) => prev + 1)
                        .TakeUntil(Observable.EveryUpdate()
                            .Where(frame => Input.GetMouseButtonDown(0)))
                        .Subscribe(
                            count => NextTextImage.enabled = !NextTextImage.enabled,
                            ShowNextText
                        );
                }
            );


    }

}

以下のステップで使えば動きます。

  • 表示テキストの配列 ShowTextList にテキストのリストを設定
  • UI要素の変数TextWindow, NextTextImage を設定
  • 表示させたいところで ShowNextText 関数を呼ぶ

以下につらつらと書いてあるワタクシのつたない解説は読まなくていいんですけど自分の中で消化するために書きます。

解説

テキストを全部表示させる待ちを作る

まずテキストが流れている最中にクリックした場合の、残りのテキストを一気に表示するための待ちを作ります。

IDisposable flushDisposable = Observable.EveryUpdate()
〜

これをあらかじめ作っておくのは、クリックせずにテキストが全て流れた後に、この待ちをキャンセルする必要があるためです。

一定のインターバルでテキストを表示させていくストリームを作る

Observable.Interval(TimeSpan.FromMilliseconds(300))

から始まる一連の処理で行います。

メソッドチェーンのTakeUntilの中でさらにObservableを作り、テキストが全部表示し終わったら終わりという判定を作っています。

Subscribeの第二引数には、テキスト全部表示した際に一度だけ呼ばれるコールバック処理を食わせています。
この中では最初に作成した待ちをキャンセルする処理と、クリックを促すためにアイコンを点滅させるObservableをさらに作成しています。

アイコン点滅

Observable.Interval(TimeSpan.FromMilliseconds(200))
〜

から始めるアイコン点滅処理のメソッドチェーンでもクリックを判定しており、この中からテキスト表示処理を再度開始すると、上記までのテキスト処理がループすることになります。


突貫工事ですがまあまあ上手くできたかなと。

Rxは奥が深くておもしろいです。

Amazon.co.jpアソシエイト