UI Textに様々な効果を付け加えるスクリプト「ModifyText」を作ってみた

# はじめに

こんにちは、[@yoship1639](https://twitter.com/yoship1639)です。
普段は自作エンジンですが、最近Unityにはまっています。

皆様はユーザインタフェース(UI)をどの程度凝っていますでしょうか。UIの見た目や操作性がゲームの面白さを左右するといっても過言ではないくらいゲームにおいてUIは重要な役割を果たしています。

UnityのUIの代表格といえば**Text**です。
Textは説明文や会話などで何度も登場する無くてはならないUIですよね。

そんなTextですが、いまいちパッとしないなんて思った事ありませんか？
「特定の文章だけ別の色に変えたい…」「文字を振動させてもっと臨場感を出したい…」
少なくとも私はそう感じたので、何とかしたいと思いました。

そこで、**ModifyText**なるものを作って様々な効果を付けられる様にしました。これはTextオブジェクトにModifyTextコンポーネントを追加する形でTextの見た目を変化させるスクリプトとなっています。
実際にどう変わるのか見てみたいと思います。

## どう変わるのか

ModifyTextを使うと、ただのTextがこの様に変わります。

<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">ModifyTextなるものを作ってみました。<br>UI Textに様々な効果を付け加えることができます(*&#39;ω&#39;*)<br>（色付け、カラフル、振動、サイズ変更など）<a href="https://t.co/A2FnFZ2IIZ">https://t.co/A2FnFZ2IIZ</a> <a href="https://t.co/hjVvXiyZt0">pic.twitter.com/hjVvXiyZt0</a></p>&mdash; yoship@二日目東メ20a (@yoship1639) <a href="https://twitter.com/yoship1639/status/1075579835009818625?ref_src=twsrc%5Etfw">December 20, 2018</a></blockquote> 

如何でしょうか。特定の文章のみ特定の効果を付け加えることが出来ます。しかも、特に面倒なスクリプトを書かなくてもTextにこの様なタグを付けるだけです。

![031.png](https://qiita-image-store.s3.amazonaws.com/0/113595/db6dfeb0-11cc-fde2-8d00-0ea39ecca598.png)

一々スクリプトを書かずとも文章にタグを付けるだけで簡単に効果を付けられます。
それでは、どの様な種類の効果があるのか見てみたいと思います。

## 効果の種類

2018/12/24 時点で、以下の効果が適用可能です。

### 色付け
![029.png](https://qiita-image-store.s3.amazonaws.com/0/113595/410950c2-c35c-ce0e-c194-d00354a3fdaa.png)
### カラフル化
![8r5je-fapi2.gif](https://qiita-image-store.s3.amazonaws.com/0/113595/a31a16d6-86f1-2f0c-3ca4-23c458b0ade6.gif)
### 振動させる
![v5x6c-te1j6.gif](https://qiita-image-store.s3.amazonaws.com/0/113595/0d10a21f-2440-465e-9c8e-432e75adf8ae.gif)
### 波打たせる
![i3n2f-lingh.gif](https://qiita-image-store.s3.amazonaws.com/0/113595/ae59ebfd-1a62-042a-5469-9c4e248dc9db.gif)
### 円運動させる
![ucvqp-bbqao.gif](https://qiita-image-store.s3.amazonaws.com/0/113595/164d56f9-4922-4c17-8d4b-d2b43963fbb4.gif)
### サイズを変える
![030.png](https://qiita-image-store.s3.amazonaws.com/0/113595/a607bd24-2ec9-2fad-c108-e898b2262b47.png)
### 回転させる
![rot.gif](https://user-images.githubusercontent.com/8346139/50321908-609f9000-0516-11e9-9486-9a14d7f9ec06.gif)
### もぞもぞ動く
![rabd.gif](https://user-images.githubusercontent.com/8346139/50322775-06083300-051a-11e9-859d-0d6b5286d626.gif)


他にもこういうのが欲しい等がございましたら遠慮なく申し出てください！

# ModifyEffectの仕組み

## 詳細はGithubのコードを参照

ソースコード全文を載せたいところですが、載せると長くなってしまうので、ソースコードはGithubの[ModifyText](https://github.com/yoship1639/ModifyText)を参照してください。コードをまるまるコピペすれば動くはずです。使い方もコードと同じ場所にあります。

それでは、どのような仕組みで動いているのか解説します。

## BaseMeshEffectを継承したクラスを定義

UIのTextに効果を付け加えるには、生成されたメッシュを変更するエフェクトの基本クラスであるBaseMesheffectを継承したクラスを定義させます。生成されたメッシュというのはTextの場合、文字のメッシュの事を指します。今回はクラス名はModifyTextControllerという名前にします。
StartメソッドでGraphicとTextコンポーネントを取得しておきます。

```csharp:ModifyTextController.cs
using UnityEngine.UI;
using UnityEngine;

[AddComponentMenu("UI/Effects/Custom/ModifyText")]
public class ModifyTextController : BaseMeshEffect
{
    Graphic graphic;
    Text text;

    void Start()
    {
        graphic = GetComponent<Graphic>();
        text = GetComponent<Text>();
    }
}
```

## ModifyMeshをオーバーライド

次に、BaseMesheffectが持つModifyMeshメソッドをオーバーライドします。*vh.GetUIVertexStream(vertices);*という処理でストリームからTextの文字のメッシュを取得できるので、取得したメッシュを変更させる処理を行い、変更したメッシュをまたストリームに戻し変更が終了します。
UpdateメソッドではModifyMeshを毎フレーム実行するために*graphic.SetVerticesDirty*をコールし続けます。

```csharp
public override void ModifyMesh(VertexHelper vh)
{
    if (!IsActive()) return;

    List<UIVertex> vertices = new List<UIVertex>();
    vh.GetUIVertexStream(vertices); // ストリームから文字メッシュを取得

    // 文字メッシュを変更させる処理

    vh.Clear();
    vh.AddUIVertexTriangleStream(vertices); // 変更を加えたメッシュを戻す
}

void Update()
{
    graphic.SetVerticesDirty();
}
```

これで準備は整いました。あとは、効果を付け加えるだけです。
いくつか例を見てみます。

## 例：文字を赤色に変える

例えば、文字の色を赤に変えたい場合は以下の処理を行います。

```csharp
public override void ModifyMesh(VertexHelper vh)
{
    if (!IsActive()) return;

    var vertices = new List<UIVertex>();
    vh.GetUIVertexStream(vertices);

    var charCount = vertices.Count / 6;
    for (var i = 0; i < charCount; i++)
    {
        for (int c = 0; c < 6; c++)
        {
            var vert = vertices[i * 6 + c];
            vert.color = Color.red;
            vertices[i * 6 + c] = vert;
        }
    }

    vh.Clear();
    vh.AddUIVertexTriangleStream(vertices);
}
```

verticesには文字の数*6の頂点データが渡されます。頂点データ6個で2つの三角メッシュを作り、四角ポリゴンである文字メッシュを表現します。頂点データは以下のように渡されます。
![032.png](https://qiita-image-store.s3.amazonaws.com/0/113595/ea886749-119e-4918-87e3-39cb0852d018.png)
また、verticesにはpositionやcolor、noumalといった情報が含まれているので、ここの情報を書き換えることで、振動させたり色を変えたりという処理が可能となります。この例の場合はcolorをredに書き換えるだけです。
一応変更可能な項目を挙げておきます。

* position
* normal
* tangent
* color
* uv0
* uv1
* uv2
* uv3

これだけの項目を変更可能です。Textメッシュに関するほぼ全ての情報を書き換え可能なのは凄いですね。

もう一つ例を見てみます。

## 例：文字を波打たせる

文字を波打たせたい場合は以下の処理を施します。

```csharp
public float WaveSpeed = 3.0f;
public float WaveRadius = 5.0f;

public override void ModifyMesh(VertexHelper vh)
{
    if (!IsActive()) return;

    var vertices = new List<UIVertex>();
    vh.GetUIVertexStream(vertices);

    var charCount = vertices.Count / 6;
    for (var i = 0; i < charCount; i++)
    {
        var delta = new Vector3(0, WaveRadius * Mathf.Sin(i + Time.time * WaveSpeed), 0);
        
        for (int c = 0; c < 6; c++)
        {
            var vert = vertices[i * 6 + c];
            vert.position += delta;
            vertices[i * 6 + c] = vert;
        }
    }

    vh.Clear();
    vh.AddUIVertexTriangleStream(vertices);
}
```

deltaのy軸に時間で変化するsin波を適用させて、それをpositionにAddするだけで波打たせる処理を実装できてしまいます。思ったよりも簡単なのではないかと思います。

もう一つ紹介！

## 例：文字をカラフルにする

文字をカラフルにしたい場合はこの様に実装します。ある程度予想はつきますよね。

```csharp
public override void ModifyMesh(VertexHelper vh)
{
    if (!IsActive()) return;

    var vertices = new List<UIVertex>();
    vh.GetUIVertexStream(vertices);

    var charCount = vertices.Count / 6;
    for (var i = 0; i < charCount; i++)
    {
        var color = new Color(UnityEngine.Random.value, UnityEngine.Random.value, UnityEngine.Random.value);
        
        for (int c = 0; c < 6; c++)
        {
            var vert = vertices[i * 6 + c];
            vert.color = color;
            vertices[i * 6 + c] = vert;
        }
    }

    vh.Clear();
    vh.AddUIVertexTriangleStream(vertices);
}
```

colorをランダムでセットするだけです。とてもシンプルですね。

ModifyMeshは毎フレーム呼ばれるので、文字ごとに毎回違う色を表現出来ます。*graphic.SetVerticesDirty*を呼ぶ間隔変えれば、色が変わる間隔も変わるので、自分の理想とする処理をうまく見つけてみてください。

この様に効果を適用していけば、[どう変わるのか](#どう変わるのか)に示した様なテキストスクリプトを作ることができます。もちろん、Text中のタグを調べ効果を適用していく等の処理は必要ですが、最初に示した様なスクリプトは、大体300行くらいで作れてしまいます。意外と簡単ですね！

# まとめ

UIのTextに様々な効果を付け加えることができるModiftTextというスクリプトを作成し、軽く解説させていただきました。ソースコード全文を紹介することはできませんでしたが、どの様に作れば最初に示したような効果を実装できるのかは解説で理解する事が出来たのではないでしょうか。

主に会話シーン等で使えると思いますので、是非ご活用ください！
また、リクエストも受け付けていますので、こんな処理も欲しい等がございましたらコメントもしくは[@yoship1639](https://twitter.com/yoship1639)まで遠慮なくお願い致します！

ご覧いただきありがとうございました。