Quantcast
Channel: C#タグが付けられた新着記事 - Qiita
Viewing all articles
Browse latest Browse all 8901

【Unity】uGUIのTextに長体をかけたい【収まるように縮める】

$
0
0

現在グレンジでUnityを用いたゲーム開発を行っているみくりやと申します。
UIを実装する際にテキストエリアに関する話題はよく出るかと思います。

「文字数制限超えてテキストがレイアウトからはみ出てます」

とか

「仕様変更で文字数収まらなくなったのでレイアウトの調整お願いします」

とか

そんな話のさなか、デザイナーさんから長体かけたいという言葉が出ました。
Unityだけ触ってる自分には馴染みのない言葉でしたが言い換えると

指定幅に収まるようにテキストにスケールをかける

ことです。
参考:“長体(文字)” の意味・解説

ということで作ってみた

というライトな感じではいけず苦戦しました。
Textクラスを継承しOnPopulateMeshでゴニョゴニョする方針です。

private Matrix4x4 _scaleMatrix = Matrix4x4.identity;

protected override void OnPopulateMesh(VertexHelper toFill) {
    #region Textクラス処理
    if (font == null) {
        return;
    }

    // We don't care if we the font Texture changes while we are doing our Update.
    // The end result of cachedTextGenerator will be valid for this instance.
    // Otherwise we can get issues like Case 619238.
    m_DisableFontTextureRebuiltCallback = true;

    Vector2 extents = rectTransform.rect.size;

    var settings = GetGenerationSettings(extents);

    #region 追記箇所
    float overRate = preferredWidth / rectTransform.rect.width;
    if (overRate > 1f) {
        switch (alignment) {
            case TextAnchor.LowerLeft:
            case TextAnchor.LowerRight:
                settings.textAnchor = TextAnchor.LowerCenter;
                break;
            case TextAnchor.MiddleLeft:
            case TextAnchor.MiddleRight:
                settings.textAnchor = TextAnchor.MiddleCenter;
                break;
            case TextAnchor.UpperLeft:
            case TextAnchor.UpperRight:
                settings.textAnchor = TextAnchor.UpperCenter;
                break;
        }

        // 変換行列を作成
        _scaleMatrix = Matrix4x4.identity;
        // scale x
        _scaleMatrix.m00 = 1f / overRate;
        // scale y
        _scaleMatrix.m11 = 1f;
        // scale z
        _scaleMatrix.m22 = 1f;

        // テキストが切られないようにOverflow指定
        settings.horizontalOverflow = HorizontalWrapMode.Overflow;
    }
    #endregion

    cachedTextGenerator.PopulateWithErrors(text, settings, gameObject);

    // Apply the offset to the vertices
    IList<UIVertex> verts = cachedTextGenerator.verts;
    float unitsPerPixel = 1 / pixelsPerUnit;
    int vertCount = verts.Count;

    // We have no verts to process just return (case 1037923)
    if (vertCount <= 0) {
        toFill.Clear();
        return;
    }

    Vector2 roundingOffset = new Vector2(verts[0].position.x, verts[0].position.y) * unitsPerPixel;
    roundingOffset = PixelAdjustPoint(roundingOffset) - roundingOffset;
    toFill.Clear();
    if (roundingOffset != Vector2.zero) {
        for (int i = 0; i < vertCount; ++i) {
            int tempVertsIndex = i & 3;
            m_TempVerts[tempVertsIndex] = verts[i];
            m_TempVerts[tempVertsIndex].position *= unitsPerPixel;
            m_TempVerts[tempVertsIndex].position.x += roundingOffset.x;
            m_TempVerts[tempVertsIndex].position.y += roundingOffset.y;

            #region 追記箇所
            if (overRate > 1f) {
                m_TempVerts[tempVertsIndex].position = _scaleMatrix.MultiplyPoint3x4(m_TempVerts[tempVertsIndex].position);
            }
            #endregion

            if (tempVertsIndex == 3) {
                toFill.AddUIVertexQuad(m_TempVerts);
            }
        }
    } else {
        for (int i = 0; i < vertCount; ++i) {
            int tempVertsIndex = i & 3;
            m_TempVerts[tempVertsIndex] = verts[i];
            m_TempVerts[tempVertsIndex].position *= unitsPerPixel;

            #region 追記箇所
            if (overRate > 1f) {
                m_TempVerts[tempVertsIndex].position = _scaleMatrix.MultiplyPoint3x4(m_TempVerts[tempVertsIndex].position);
            }
            #endregion

            if (tempVertsIndex == 3) {
                toFill.AddUIVertexQuad(m_TempVerts);
            }
        }
    }

    m_DisableFontTextureRebuiltCallback = false;
    #endregion
}

まずTextクラスのOnPopulateMesh処理をゴリッと持ってきます(ここがイケてないところですが…)
参考:https://bitbucket.org/Unity-Technologies/ui/src/2019.1/

そして「#region 追記箇所」の位置に処理を追加しています。

最初の追記箇所ではTextの設定値に関する計算とどのくらい縮めるかの計算を行います。
以下の計算式でどのくらいはみ出してるかを割り出しました。

float overRate = preferredWidth / rectTransform.rect.width;

この値を用いてどのくらい縮めるかの変換行列を作成します。

// 変換行列を作成
_scaleMatrix = Matrix4x4.identity;
// scale x
_scaleMatrix.m00 = 1f / overRate;
// scale y
_scaleMatrix.m11 = 1f;
// scale z
_scaleMatrix.m22 = 1f;

今回は横方向のみに長体をかけますが、m11を使用することで縦に対しても反映できます。

次にGetGenerationSettingsで取得した設定値に関する処理です。
まずtextAnchorをCenterにしている箇所は LeftかRightだと頂点計算時の処理に手を加える必要がありややこしくなりそうだったのですが、長体をかけるときはどうせrectの幅いっぱいの状態なので割り切って上書くようにしました。
horizontalOverflowも Wrapだと文字が切れるのでOverflowで上書きします。
また、設定値を直接上書きすると長体をかけないでいいときにもとに戻す処理がややこしくなるのでGetGenerationSettingsで得た値を書き換えます。
ややこしいんです。

あとは頂点計算後に_scaleMatrix.MultiplyPoint3x4してやればスケールをかけることが出来ます。

キャプチャ1-1.PNG
こんな感じのTextに文字を入力していくと

キャプチャ1-2.PNG
このように一文字の横幅が収まるサイズに縮んでくれます。やったね!

まとめ

Textの処理内容に直接手をかけることになりましたが一応希望の機能は作ることが出来ました。デフォであっていいような気もするのでUnityさん作ってくれないかな~。もうちょっとスマートな方法を模索したい今日このごろです。


Viewing all articles
Browse latest Browse all 8901

Trending Articles