【Unity】DOTweenを使ってテキスト送りの機能を自作する

記事タイトルの件でしばらく頭を抱えていたため、久々のnote更新らしい。



さて、自分は現在Unityを使用してゲームを制作しているのですが、恐らくUnity使用者がほぼ通るであろう「テキスト送りどうするか問題」の壁にぶち当たっておりました。
※ここで言うテキスト送り=会話シーンなどで文章が次々と流れるような機能と思ってください

どんなゲームにも存在する機能だし文字で表現すると簡単なんだけど、これが実際に作ってみるとなかなか難しい。
他の方が書いている記事などもいろいろ読み漁ってみたものの、自分がイメージするような形が上手いこと実現できず。

という問題を抱えつつ、数日頑張ってようやく基本形が完成したので、同じ悩みを抱えている誰かの役に立てばよいなと思って記事を残すことにしました。
(正直なところ、ちょっとだけ誉めてほしいという気持ちもあるのは内緒)
良ければ参考にしてくださいませ。




事前準備

まずは全体像をイメージ。
こんな感じのものを作りたいんじゃ!というもの

  1. テキスト表示時に1文字ずつ表示させるようなアニメーションにしたい

  2. 文字表示中にマウスがクリックされたら全文表示されるようにしたい

  3. 全文が表示されたら、マウスクリックで次のテキスト表示に移りたい

  4. 汎用性を持たせてゲーム全体で使用できるものとしたい


次に、作成における前提条件。

  • Unityのアセットストアで販売されている DOTween Pro を使用

実は購入するかめっちゃ迷いました(そんなに高いものでもないけど)。
ただ結論、購入して大正解です。使わなくても今回の機能は作れそうですがソースコードはかなりスッキリしましたし、アニメーションを使用する他の箇所でも大いに役立ってくれそうだなと。
お手頃価格でゲームの質を何段階もあげてくれる優良アセットだと思うので、気になる方はぜひお手に取ってみてください。


スクリプト作成

というわけで、上記のイメージとアセットを握りしめて実際にスクリプトを作成していきましょう。
以下、TextController.cs としてこのような形で作成しました。

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


public class TextController : MonoBehaviour
{
    // アタッチするもの
    public TMP_Text _textA;
    public TMP_Text _textB;
    public GameObject _iconNextClick;

    // 表示テキスト管理
    private TMP_Text _text;
    private List<string> _messageTextList = new();
    private int _messageTextListIndex = 0;
    [SerializeField] private float _delayDuration = 0.5f;

    // DOTween状態管理
    private Tween _tween;

    private void Update()
    {
        // 文字表示アニメーション中にマウスクリックされたら現在の文章を全表示
        if (_tween != null && Input.GetMouseButtonDown(0))
        {
            _tween.Kill(true);
        }
    }

    public void DisplayText(List<string> messageTextList, Main.TextMode textMode)
    {
        // 初回処理
        _messageTextList = messageTextList;
        _messageTextListIndex = 0;

        // テキストオブジェクトの決定
        // 複数のテキストオブジェクトを使い分けたい場合はこんな感じで使っています
        switch (textMode)
        {
            case Main.TextMode.A:
                {
                    _text = _textA;
                    break;
                }
            case Main.TextMode.B:
                {
                    _text = _textB;
                    break;
                }
        }

        // DOTweenを使用してテキストを1文字ずつ表示
        OperateDOTweenText();
        
    }

    private IEnumerator DisplayNextText()
    {
        _messageTextListIndex++;
        _iconNextClick.SetActive(true);

        // Kill処理が走った時のために1秒だけ待つ
        yield return new WaitForSeconds(1);

        // マウスクリックされたら次の文章が流れる
        yield return new WaitUntil(() => Input.GetMouseButtonDown(0));
        _iconNextClick.SetActive(false);
        OperateDOTweenText();
    }

    private void OperateDOTweenText()
    {
        _text.text = "";
        _tween = _text.DOText(_messageTextList[_messageTextListIndex], _messageTextList[_messageTextListIndex].Length * _delayDuration).
            SetEase(Ease.Linear).OnComplete(() =>
            {
                if (_messageTextListIndex < _messageTextList.Count - 1)
                {
                    // 次の文章を呼び出す準備
                    StartCoroutine(DisplayNextText());
                }
                else
                {
                    // コントローラ内で使用するパラメータの初期化
                    ResetControllerParameter();

                    // TODO: ウィンドウを閉じる、などの処理を入れても良いかも
                }
            });
    }

    private void ResetControllerParameter()
    {
        _messageTextList = new();
        _messageTextListIndex = 0;
        _iconNextClick.SetActive(false);
        _tween = null;
    }
}


使い方

  • 空のゲームオブジェクトにこのスクリプトをアタッチ

  • スクリプト内 public で宣言されているものをインスペクタからアタッチ

    • 対象となるテキストオブジェクトだったり、次の文章あるよ~という目印になるアイコンだったり

  • 必要な箇所でListを引数として DisplayText() を呼び出してもらえればOK


ポイント

  • DOText().OnComplete() がめちゃ助かる

テキストへのアニメーション処理がすべて終わった後に OnComplete() の中身を実行するよってやつなんですけど、これがあるだけでめちゃくちゃスッキリしました。

  • Kill(true) の軌跡

マウスクリックで全文表示するために使っているんですけど、普通に Kill() しちゃうと文字が流れている途中で停止しちゃうんですよね。
でも Kill(true) と書くことによって最後まで処理が流れるかつ OnComplete() まで呼んでくれるという不思議。

  • コルーチンって何だよ

これがわからなすぎてずっと頭を抱えていた。

※最近のバージョンは日本語化されてない部分もあったのでちょっと古めのバージョンのマニュアルを貼ってます

ざっくり理解するのであれば、「とある動作をするまで待っているよ」ってものらしいのですが、書き方が難しいんですよね。
上記の Kill(true) の処理との兼ね合いがずっと上手くいかなくて、最終的にたどり着いたのが「1秒待つ」という力技。でも意外と正解だったかもしれない。

  • あとは

好きにカスタマイズしてお使いください!
もしわからない箇所がある場合はnoteでもXでも、お気軽に質問してもらえればと思います。


サンプル動画

こんな感じで動作します。
始めて撮影した動画をYouTubeに上げたので、画質悪いとか編集されてなくて雑とかありますが、お許しくださいませ。



画面上部の赤文字部分と、リセットのテキストはどちらも DisplayText() を使って表示させています。
あとは全てのテキストが流れ終えた後の処理とか、まだまだカスタマイズできそうなので、それを含めこれからの制作も楽しんでいきたいところです。


おわりに

以上、ちょっとは頑張ってゲーム作ってますよって話でした。
早いところ1作目を作り上げて何かしらの形で公開したいのですが、予想を遥かに上回る時間がかかりますね。。

ちょうど季節の変わり目というのもあるので、体調に気を付けつつ(特に今は花粉が超絶ヤバい)無理なく続けていこうと思っております。

それではまた次の記事で!


この記事が気に入ったらサポートをしてみませんか?