1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

[Unity]TextMeshProの文字をバウンドさせるサンプル

Posted at

自分メモ用
UnityでTextMeshProのテキストをバウンドさせるためのスクリプトのサンプル

出来上がったもの

Point.gif

サンプルコード
BoundText.cs
using System;
using TMPro;
using UnityEngine;

[RequireComponent(typeof(TextMeshProUGUI)), DisallowMultipleComponent]
public class BoundText : MonoBehaviour
{
    private TextMeshProUGUI textMeshPro = null;

    private float elapseTime = 0;

    [SerializeField]
    private float boundTime = 0.2f;

    [SerializeField]
    private float boundHeight = 20.0f;

    [SerializeField]
    private float afterTime = 0.5f;

    private Func<float, float> CalcFunc = null;

    // Awake is called when an instance of the script is loaded
    private void Awake()
    {
        this.textMeshPro = this.GetComponent<TextMeshProUGUI>();
    }

    // Start is called before the first frame update
    private void Start()
    {
        elapseTime = 0.0f;
        CalcFunc = GetBoundFunc(boundHeight, boundTime);
    }

    // Update is called once per frame
    private void Update()
    {
        if (Time.deltaTime < 1)
        {
            Animation(elapseTime, boundTime);
            float deltaTime = Time.deltaTime;
            elapseTime += deltaTime;
        }

        if (elapseTime > (boundTime + afterTime))
        {
            Destroy(this.gameObject);
        }
    }

    private void Animation(float time, float maxTime)
    {
        textMeshPro.ForceMeshUpdate(true);
        TMP_TextInfo textInfo = textMeshPro.textInfo;

        float[] preHeights = new float[4];

        for (int i = 0; i < textInfo.characterInfo.Length; i++)
        {
            TMP_CharacterInfo charInfo = textInfo.characterInfo[i];
            if (!charInfo.isVisible) continue;

            int mIndex = charInfo.materialReferenceIndex;
            int vIndex = charInfo.vertexIndex;

            Vector3[] vectors = textInfo.meshInfo[mIndex].vertices;

            float height = CalcFunc(time - i * boundTime * 0.5f);
            vectors[vIndex + 0].y += height;
            vectors[vIndex + 1].y += height;
            vectors[vIndex + 2].y += height;
            vectors[vIndex + 3].y += height;
        }

        for (int i = 0; i < textInfo.materialCount; i++)
        {
            if (textInfo.meshInfo[i].mesh == null) continue;

            textInfo.meshInfo[i].mesh.vertices = textInfo.meshInfo[i].vertices;
            textMeshPro.UpdateGeometry(textInfo.meshInfo[i].mesh, i);
        }
    }

    private Func<float, float> GetBoundFunc(float maxHeight, float maxTime)
    {
        float v0 = 4 * maxHeight / maxTime;
        float g = 2 * v0 / maxTime;

        return GetBoundFuncFromInitSpeed(g, v0);
    }

    private Func<float, float> GetBoundFuncFromInitSpeed(float g, float v0)
    {
        return (float t) =>
        {
            float h = v0 * t - (g * Mathf.Pow(t, 2.0f) * 0.5f);
            return h < 0 ? 0 : h;
        };
    }
}

コード解説

概要

このスクリプトをアタッチしたTextMeshProは、オブジェクト生成後、1文字ずつ文字がバウンドしていき、全部バウンドを終えると自信を削除(Destroy)する。

詳細

クラス属性

[RequireComponent(typeof(TextMeshProUGUI)), DisallowMultipleComponent]
public class BoundText : MonoBehaviour
  • RequireComponent(typeof(TextMeshProUGUI))
    TextMeshProUGUIコンポーネントを持つGameObjectにのみアタッチできる。

  • DisallowMultipleComponent
    多重にアタッチはできない

パラメタ

    [SerializeField]
    private float boundTime = 0.2f;

    [SerializeField]
    private float boundHeight = 20.0f;

    [SerializeField]
    private float afterTime = 0.5f;
  • boundTime
    1文字が跳ねて落ちるまでの時間
  • boundHeight
    1文字が跳ねる高さ
  • afterTime
    全部跳ね終わった後しばらく表示している時間

Awake

    private void Awake()
    {
        this.textMeshPro = this.GetComponent<TextMeshProUGUI>();
    }

スクリプト内で参照するTextMeshProコンポーネントの事前取得

Start

    private void Start()
    {
        elapseTime = 0.0f;
        CalcFunc = GetBoundFunc(boundHeight, boundTime);
    }

スクリプト内で使用する変数の初期化

  • elapseTime
    スクリプト開始(オブジェクト生成)からの起動時間をカウントする変数
    経過時間に伴うバウンドの高さを計算することに使用
  • CalcFunc
    バウンドする高さを計算するための関数を生成
    時間以外のパラメタは事前生成したほうがいいと思ったのでここで生成している
GetBoundFunc
    private Func<float, float> GetBoundFunc(float maxHeight, float maxTime)
    {
        float v0 = 4 * maxHeight / maxTime;
        float g = 2 * v0 / maxTime;

        return GetBoundFuncFromInitSpeed(g, v0);
    }

    private Func<float, float> GetBoundFuncFromInitSpeed(float g, float v0)
    {
        return (float t) =>
        {
            float h = v0 * t - (g * Mathf.Pow(t, 2.0f) * 0.5f);
            return h < 0 ? 0 : h;
        };
    }
  • maxHeight
    バウンドする高さ
  • maxTime
    バウンドしている時間

基本的な計算式は、重力加速度($ y = v_0t - \frac{1}{2}gt^2 $)と同じ
maxHeightとmaxTimeから初速$ v_0 $と重力加速度$g$を算出している。

計算式は下記ページを参考にしている
https://ja.wikipedia.org/wiki/%E3%83%9C%E3%83%BC%E3%83%AB%E3%81%AE%E8%B7%B3%E3%81%AD%E8%BF%94%E3%82%8A%E9%81%8B%E5%8B%95

Update

    private void Update()
    {
        if (Time.deltaTime < 1)
        {
            Animation(elapseTime, boundTime);
            float deltaTime = Time.deltaTime;
            elapseTime += deltaTime;
        }

        if (elapseTime > (boundTime + afterTime))
        {
            Destroy(this.gameObject);
        }
    }

if (Time.detailTIme < 1)は、初回Updateの時に大きな数になることがあったのでセーフティで挿入
Animation(elapseTime, boundTime);で、文字の高さを変更
elapseTime += deltaTimeで、経過時間を加算

Animation
    private void Animation(float time, float maxTime)
    {
        textMeshPro.ForceMeshUpdate(true);
        TMP_TextInfo textInfo = textMeshPro.textInfo;

        for (int i = 0; i < textInfo.characterInfo.Length; i++)
        {
            TMP_CharacterInfo charInfo = textInfo.characterInfo[i];
            if (!charInfo.isVisible) continue;

            int mIndex = charInfo.materialReferenceIndex;
            int vIndex = charInfo.vertexIndex;

            Vector3[] vectors = textInfo.meshInfo[mIndex].vertices;

            float height = CalcFunc(time - i * boundTime * 0.5f);
            vectors[vIndex + 0].y += height;
            vectors[vIndex + 1].y += height;
            vectors[vIndex + 2].y += height;
            vectors[vIndex + 3].y += height;
        }

        for (int i = 0; i < textInfo.materialCount; i++)
        {
            if (textInfo.meshInfo[i].mesh == null) continue;

            textInfo.meshInfo[i].mesh.vertices = textInfo.meshInfo[i].vertices;
            textMeshPro.UpdateGeometry(textInfo.meshInfo[i].mesh, i);
        }
    }

TextMeshProの文字位置変更については下記ページを参考
https://coposuke.hateblo.jp/entry/2020/06/07/020235

メインは下記

            float height = CalcFunc(time - i * boundTime * 0.5f);
            vectors[vIndex + 0].y += height;
            vectors[vIndex + 1].y += height;
            vectors[vIndex + 2].y += height;
            vectors[vIndex + 3].y += height;

ここで高さを計算し各文字にいれている
- i * boundTime * 0.5f部分で、文字ごとに次々バウンドするようにしている。
iが文字位置なので、文字位置ごとにバウンド時間の半分の時間ズラして表示する)

参考URL

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?