i-school - DoTweenを利用してテキストを1文字ずつ表示させる

DoTweenを利用してテキストを1文字ずつ表示させる(未)

 DoTweenのDoText機能を利用して、Textコンポーネントのテキストに文字を1文字ずつ(文字送り)表示させる方法です。

 Listに表示させたい文字列を順番に用意しておいて、それを1メッセージずつ表示させます。
以下の機能を実装しています。

 ・1メッセージ内の文字を1文字ずつ表示する(文字送り)
 ・文字数に関わらず、1文字当たり、X秒で表示する
 ・文字送りの途中でクリック(タップ)した場合には、そのテキストの全文を表示する(スキップ機能) 
 ・全文表示時、まだ未表示のテキストが残っている場合にはタップを促すイメージを表示する
 ・クリック(タップ)で次のテキストの文字送りを開始する

 こちらの記事を参考にさせて頂きました。ありがとうございます。
 
いりこ様
Unity C# でアドベンチャー風テキスト作成
https://qiita.com/irico/items/3a1bb68257547e4a2908

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using DG.Tweening;

public class TextMessageViewer : MonoBehaviour
{
    public List<string> messageList = new List<string>();  // 表示するメッセージのリスト
    public float wordSpeed;                                  // 1文字当たりの表示速度
    
    public Text txtMessage;                                  // メッセージ表示用
    public GameObject iconNextTap;                          // タップを促すイメージ表示

    private int messageListIndex = 0;                        // 表示するメッセージの配列番号
    private int wordCount;                                   // 1メッセージ当たりの文字の総数
    private bool isTapped = false;                          // 全文表示後にタップを待つフラグ
    private bool isDisplayedAllMessage = false;              // 全メッセージ表示完了のフラグ

    private IEnumerator waitCoroutine;                       // 全文表示までの待機時間メソッド代入用。Stopできるようにしておく
    private Tween tween;                                     // DoTween再生用。Killできるように代入して使用する

    void Start() {
        // 1文字ずつ文字を表示する処理をスタート
        StartCoroutine(DisplayMessage());
    }
 
    void Update() {
        if (isDisplayedAllMessage) {
            // 全てのメッセージ表示が終了していたら処理を行わない
            return;
        }

        if (Input.GetMouseButtonDown(0) && tween != null) {
            // 文字送り中にタップした場合、文字送りを停止
            tween.Kill();
            tween = null;
            // 文字送りのための待機時間も停止
            if (waitCoroutine != null) {
                StopCoroutine(waitCoroutine);
                waitCoroutine = null;
            }
            // 全文をまとめて表示
            txtMessage.text = messageList[messageListIndex];
            
            // タップするまで全文を表示したまま待機
            StartCoroutine(NextTouch());
        }

        if (Input.GetMouseButtonDown(0) && wordCount == messageList[messageListIndex].Length) {
            // 全文表示中にタップしたら全文表示を終了
            isTapped = true;
        }
    }

    /// <summary>
    /// 1文字ずつ文字を表示
    /// </summary>
    /// <returns></returns>
    private IEnumerator DisplayMessage() {
        isTapped = false;
        
        // 表示テキストとTweenをリセット
        txtMessage.text = "";
        tween = null;
        
        // 文字送りの待機時間を初期化
        if (waitCoroutine != null) {
            StopCoroutine(waitCoroutine);
            waitCoroutine = null;
        }

        // 1文字ずつの文字送り表示が終了するまでループ
        while (messageList[messageListIndex].Length > wordCount) {
            // wordSpeed秒ごとに、文字を1文字ずつ表示。SetEase(Ease.Linear)をセットすることで一定の表示間隔で表示
            tween = txtMessage.DOText(messageList[messageListIndex], messageList[messageListIndex].Length * wordSpeed).
                SetEase(Ease.Linear).OnComplete(() => {
                    Debug.Log("全文表示 完了");
                    // (TODO) 他にも処理があれば追加する

                });
            // 文字送り表示が終了するまでの待機時間を設定して待機を実行
            waitCoroutine = WaitTime();
            yield return StartCoroutine(waitCoroutine);
        }
        
        // タップするまで全文を表示したまま待機
        StartCoroutine(NextTouch());
    }

    /// <summary>
    /// 全文表示までの待機時間(文字数×1文字当たりの表示時間)
    /// タップして全文をまとめて表示した場合にはこの待機時間を停止
    /// </summary>
    /// <returns></returns>
    private IEnumerator WaitTime() {
        yield return new WaitForSeconds(messageList[messageListIndex].Length * wordSpeed);
    }

    /// <summary>
    /// タップするまで全文を表示したまま待機
    /// </summary>
    /// <returns></returns>
    private IEnumerator NextTouch() {
        yield return null;
        // 表示した文字の総数を更新
        wordCount = messageList[messageListIndex].Length;

        // タップを促すイメージ表示
        iconNextTap.SetActive(true);

        // タップを待つ
        yield return new WaitUntil(() => isTapped);

        iconNextTap.SetActive(false);

        // 次のメッセージ準備
        messageListIndex++;
        wordCount = 0;

        // リストに未表示のメッセージが残っている場合
        if (messageListIndex < messageList.Count) {
            // 1文字ずつ文字を表示する処理をスタート
            StartCoroutine(DisplayMessage());
        } else {
            // 全メッセージ表示終了
            isDisplayedAllMessage = true;
                        
            // 次の処理へ
        }
    }
}

使用例

 Canvas内に新しくTextゲームオブジェクトを作成します。こちらがテキストを表示させるためのエリアです。
名前をtxtMessageに変更します。(アサイン情報と同じ名前にしておく管理が楽です。)

 Canvas内に空のゲームオブジェクトを作成し、名前をTextMessageViewerに変更します。
作成した同名のTextMessageViewerをアタッチします。
 インスペクターを確認してアタッチが無事に行われていれば、publicの項目がアサイン情報として表示されますのでこちらを設定します。
 
 messageListはListになっていますので、Sizeを0から任意の数に変更します。Elementが作成されますので
こちらに1つのメッセージとして表示したい文字列を登録します。
 Element1つが1つのメッセージになります。このメッセージの表示が終わると、次のElement内の文字列がメッセージとして表示されるようになります。

 wordSpeedは1文字当たりの表示速度です。0.2f〜0.3f前後にすると、文字送りがスムースに流れていく感じに見えます。
適宜調整してみてください。

 txtMessageはメッセージが表示されるエリアです。先ほど作成した、TextコンポーネントのアタッチされているtxtMessageゲームオブジェクトをアサインしましょう。
変数名とゲームオブジェクト名を同じ名前にしてありますので、照らし合わせてみたときに、同名以外の場合はアサイン間違いであると気づけると思います。

 iconNextTapには、メッセージが表示された際にタップを促すイメージを登録しておきます。こちらは任意のイメージやアニメーションなどを用意してアサインしてください。
今回はImageをDoTweenを使って点滅させています。


動作時の例です。1つのメッセージに含まれる文字がすべて表示されるとタップを促すイメージが表示されます。
https://gyazo.com/ec5afc5c0581ed84c71c5bc7863322e4