読者です 読者をやめる 読者になる 読者になる

プログラミングで世界を変える

ゲームプログラミングと技術のこと

UnityでTextのアルファ値をリッチテキスト使用時にも適用するスクリプト

uGUI Unity プログラミング

f:id:splas_boomerang:20151230223647g:plain

今回書いたスクリプト

UnityでTextのアルファ値をリッチテキスト使用時にも適用するスクリプト · GitHub

UnityでTextのアルファ値をリッチテキスト使用時にも適用するスクリプト (UniRx未使用バージョン) · GitHub

経緯とか

UnityのuGUIのTextにはリッチテキストという機能があって、Textの指定とは別にテキスト内で個別にフォントスタイルの指定をすることが出来ます。

以下のように指定することで上の画像のように一つのText内で複数の色を指定することが出来ます。

ガールズ<size=48><color=#8cd1a4ff>&</color></size> 
 パンツァー

しかし、リッチテキストで色指定した部分は、元のTextで指定したアルファ値が上書きされてしまうため、演出などでテキストのフェードアウトなどをしたい場合などに不都合が生じます。ちなみに、<color=#00ff00>のようにアルファ値部分を指定しなかった場合でも、<color=#00ff00ff>と解釈され、同様に元のTextのアルファ値は適用されません。

そこで、リッチテキストの指定によらずにアルファ値を適用できるスクリプトを書きました。

f:id:splas_boomerang:20151230224417g:plain f:id:splas_boomerang:20151230224420g:plain

(左がスクリプト適用前、右がスクリプト適用後です。)

使い方

Textコンポーネントと同じオブジェクトにRichtextAlphaAdapterコンポーネントをアタッチするだけです。 Text.textとText.colorの値が変更されるたびに内部のアップデート関数が呼ばれます。

実装にUniRxを使用しています。AssetStoreから落とせますので、importしてから使ってください。一応、UniRxを使わない実装をしたバージョンも用意してあります。

実装

gist.github.com

値の監視

UniRxのObserveEveryValueChanged()を使用しました。

参考: qiita.com

リッチテキストにアルファ値を適用する部分

メンバ変数

あらかじめRegexの変数を持っておいて

   private Regex regex;

初期化

Start()関数でリッチテキストのカラーに適応するパターンで初期化します。

       // 大文字小文字の区別、二重引用符の有無など、リッチテキストの仕様に準拠
        string pattern = "(<color=\"?#[a-f0-9]{6})[a-f0-9]{2}?(\"?>)";
        regex = new Regex(pattern, RegexOptions.IgnoreCase);

タグの記述の揺れについては正規表現で丸め込みました。なので、

<color=#12aF60Ae>あああ</color>
<CoLoR="#000000">いいい</cOlOr>

みたいな記述でも問題なく動作します。スペースは許容されていないようだったので、考慮していません。

また、特に二重引用符あたりとか、ユーザーがこの文字列を使用する際に勝手に消したり追加したりすると色々と不都合が生じそうなので、16進数部分以外はそのまま利用しています。

ちなみにRegex初期化の第二引数のRegexOptions.IgnoreCaseは大文字小文字を区別しないようにするやつです。

UpdateAlpha()関数

メイン部分。UpdateAlpha()関数でアルファ値を16進数に変換し、リッチテキストのアルファ値部分を置換します。

   private void UpdateAlpha(float alpha)
    {
        string alpha16 = ConvertTo16 (alpha);

        string replacement = "${1}" + alpha16 + "${2}";
        text.text = regex.Replace (text.text, replacement);
    }

${1}とか${2}では正規表現でマッチしたグループのインデックス番号を指定しています。 $1とかでも動作するんですけど、直後の変数alpha16が"00"とかの場合に$100(100番目のインデックス)と判定されてしまうので要注意です。30分くらいハマってました…。

Text.colorのアルファ値を16進数に変換する処理

   private string ConvertTo16(float alpha)
    {
        // Text.colorの持つアルファ値は0f-1f
        // 16進数に変換するために0-255の整数型に変換
        int alpha10 = (int)(alpha * 255f);

        string alpha16 = Convert.ToString (alpha10, 16);

        // 16進数で一桁の場合、二桁目を0詰する。
        if (alpha10  < 16) alpha16 = "0" + alpha16;

        return alpha16;
    }

全部コメントで書いてあるので補足だけ。UnityのGUI上ではあたかも0-255の整数値で値を持っているようにみえるんですけど、内部的には0-1の少数型で扱われています。

f:id:splas_boomerang:20151230234239p:plain

また、C#には10進数の整数を16進数の文字列に変換する関数が用意されているのですが、桁詰めを行ってくれません。リッチテキストでは必ず二桁の16進数でなければならないため、手動で行いましょう。

おまけ

その1: 役にたたなさそうなスクリプト

UnityでShadowのアルファ値をTextのアルファ値と同期させるスクリプト · GitHub

今回紹介したスクリプト同様に、Shadowコンポーネントのアルファ値も適用するスクリプトです。Shadowを継承しているOutlineにも適用されます。

書いてから気がついたんですけど、Shadowのアルファ値ってTextのアルファ値に依存するみたいです。

Shadowによって描画される部分のアルファ値 = 
 Textで指定したアルファ値 x Shadowで指定したアルファ値 

ってことですね。となると、かなり使用する場面が限られてくるスクリプトでした。折角書いたので使ってくれる人募集してます。

一応使い方…

同様にTextと同じオブジェクトにShadowAlphaAdapterコンポーネントをアタッチするだけです。 こっちはUniRx版しかないです。

GetComponetsしているので、Shadowが複数アタッチされていても全てに適用されます。

その2: よく分かる正規表現

"(<color=\"?#[a-f0-9]{6})[a-f0-9]{2}?(\"?>)"

の解説します。正規表現初心者向けです。

「\"?」

「"」を「\」でエスケープして「\"」になります。 「?」は直前の文字を0回か1回繰り返すという意味なので、あってもなくてもいいという意味になります。 ちなみに「"」のエスケープは@をつけた表記では「""」になります。上のパターンでいうと、@"(<color=""?#〜〜〜)"といった感じ。

「[a-f0-9]{6}」

「[]」は中の文字列の中の1文字を意味します。「[abc]」だったらaかbかcのこと。「[-]」は連続する文字の中の1文字です。なので、[a-f0-9]はa,b,c,d,e,f,0,1,2,3,4,5,6,7,8,9のいずれか1文字、つまり1桁の16進数を意味していることが分かります。「{n}」は直前の文字をn回繰り返すという意味になるので、6桁の16進数を表現することができます。今回の場合はアルファ値を含まない色指定の部分になります。

「[a-f0-9]{2}?」

今まで紹介した正規表現の組み合わせです。「[a-f0-9]{2}」は2桁の16進数を表していて、最後の「?」は「[a-f0-9]{2}」に対してかかっています。今回の場合アルファ値の指定はあってもなくても大丈夫なので、16進数が6桁の場合と8桁の場合に対応するためにこのようにしました。

「(〜〜〜)」

「()」はグループ化を意味します。正規表現を使う際にも便利ですが、今回は置換文字列の部分指定に用いました。具体的には、括弧に使われている部分は置換後もそのまま使用し、アルファ値部分だけ新しい値を使用しています。ちなみにグループ番号は初めから順に振られていますが、「(?〜〜)」というようにグループ名を明示し「${name}と使用することも可能です。