画像の数字をスコアとして扱う

お久しぶりです。夏のインターンも終わってひと段落してる頃です。前々から記事にしようと思ってた画像で書かれた数字をスコアとして扱う方法を書いていきます。

それってやる意味あるの?

number

結構前にゲームデザイナーさんに聞いた話ですが、ゲームで当たり前に使われてるフォントって結構著作権的なあれで(文字数の多い日本語フォントは特にそう)グレーゾーンだったりするそうです。某フリーフォントサイトで商用利用フリー!!とか書かれても製作者の考え1つでアウト!!ってなった話とか結構あったそうです。(その人曰くただより高いものはないらしい)
一応ですがゲームにフォントを使う際には組み込み不可と書かれてる場合はアウトってことを認識しておいてください。どうしてもフォントで扱いたいって場合にはお金を払って権利を買うっていう手もありますが、結構現実味がないお値段だったりするそうです。
なのでゲームでスコアだけ~とか一部分にしか文字、数字を使わない場合は画像として数字を扱うってのがポピュラーな手法だそうです。画像にすることによって、2次配布防止、組み込み回避、容量削減やゲームにあったデザインができるなど結構うれしいことだらけらしいです。まぁテキストノベルゲームとかだと素直にフォント使った方がいいと思いますけど・・・・

実装方法 導入編

画像で文字を表現する大切さを知ったところで本題の実装に入りたいと思います。今回は、ミニゲームなど終了時に表示されるスコアを画像で表示して見たいと思います。
まず適当に0~9まで書かれた画像を用意してください。(上の画像でもOKです)

画像をdrag&dropで取り込んだらTextureTypeをSpriteに変えてSpriteModeをMultipleに変更したあとApplyをクリックします

texturefromsprite

次にSpriteEditorを開いて右上のsliceをクリックしたのちsliceをすればいい感じに切り分けてくれます。

slice

導入に関しては以上です。

実装方法 コーディング

実際にソースコードかいて実装して行きます。
今回はスコアはint型で渡されると仮定して、引数にスコアの値を渡したらそのスコアを表示するというプログラムを組んでみます。
あらかじめCanvasを用意した状態で、ScoreImage(任意)なるImageオブジェクトを作っておいてください。

default

ここでいうScoreImageが原本になります。このScoreImageの場所が1桁目の数字が入るってイメージです。あとはこれを複製して桁ごとに左にx座標-幅分ずらすだけになります。

figure

後は桁数分forで回して桁ずらしをx座標-幅*1,x座標-幅*2….ってやれば実装できます。
以下ソースコード

void View(int score) {
        var digit = score;
        //要素数0には1桁目の値が格納
        List<int> number = new List<int>();
        while ( digit != 0 ) {
            score = digit % 10;
            digit = digit / 10;
            number.Add(score);
        }

        GameObject.Find("ScoreImage").GetComponent<Image>().sprite= numimage[number[0]];
        for (int i=1 ;i<number.Count ;i++ ) {
            //複製
            RectTransform scoreimage =(RectTransform) Instantiate(GameObject.Find("ScoreImage")).transform;
            scoreimage.SetParent(this.transform , false);
            scoreimage.localPosition = new Vector2(
                scoreimage.localPosition.x - scoreimage.sizeDelta.x * i ,
                scoreimage.localPosition.y);
            scoreimage.GetComponent<Image>( ).sprite = numimage[number[i]];
        }
    }

解説

void View(int score) {
        var digit = score;
        //要素数0には1桁目の値が格納
        List<int> number = new List<int>();
        while ( digit != 0 ) {
            score = digit % 10;
            digit = digit / 10;
            number.Add(score);
        }

1の桁から順に値を区切ってint型のリストに格納する。
思いつかなかったからこの手を使ったけど、これ要素数0の時に1の桁の数字が入るので結構わかりずらいかも
イメージしにくかったら

        List<int> number = new List<int>();

をパブリック変数にするとどんな感じで値が入ってるか確認しやすい。

   pubic List<int> number = new List<int>();
       void View(int score) {
        var digit = score;
        //要素数0には1桁目の値が格納
       number = new List<int>();

number

nubmerリストの中身(スコアが123の時)

        GameObject.Find("ScoreImage").GetComponent<Image>().sprite= numimage[number[0]];

複製元には1の桁の数字が入るので、あらかじめ画像を入れておく

        for (int i=1 ;i<number.Count ;i++ ) {
            //複製
            RectTransform scoreimage =(RectTransform) Instantiate(GameObject.Find("ScoreImage")).transform;
            scoreimage.SetParent(this.transform , false);
            scoreimage.localPosition = new Vector2(
                scoreimage.localPosition.x - scoreimage.sizeDelta.x * i ,
                scoreimage.localPosition.y);
            scoreimage.GetComponent<Image>( ).sprite = numimage[number[i]];
        }
    }

格納した値分for分を回すInstantiateでオブジェクトを複製する。
この際複製したオブジェクトは親オブジェクトになるので、setParentで子オブジェクト化するのを忘れないようにする
あとはx座標-幅分ずらした座標を複製したオブジェクトのx座標に代入していくだけの処理。
最後にnumimage(スライスした0から9までの画像)からnumberリストの値を引っ張ればOK

numimage

今回実装したソースコード(全部)

using UnityEngine;
using System.Collections;
using UnityEngine.UI;
using System.Collections.Generic;

public class Score : MonoBehaviour {
    public Sprite[] numimage;
    public List<int> number = new List<int>( );
    public void RandomScore() {
        //クリックされるたびにrandomでスコアを変動
        var random = Random.Range(1 , 90000);
        //いままで表示されてたスコアオブジェクト削除
        var objs = GameObject.FindGameObjectsWithTag("score");
        foreach ( var obj in objs ) {
            if ( 0 <= obj.name.LastIndexOf("Clone") ) {
                Destroy(obj);
            }
        }
        View(random);
    }
    //スコアを表示するメソッド
    void View(int score) {
        var digit = score;
        //要素数0には1桁目の値が格納
        number = new List<int>( );
        while ( digit != 0 ) {
            score = digit % 10;
            digit = digit / 10;
            number.Add(score);
        }

        GameObject.Find("ScoreImage").GetComponent<Image>( ).sprite = numimage[number[0]];
        for ( int i = 1 ; i < number.Count ; i++ ) {
            //複製
            RectTransform scoreimage = (RectTransform)Instantiate(GameObject.Find("ScoreImage")).transform;
            scoreimage.SetParent(this.transform , false);
            scoreimage.localPosition = new Vector2(
                scoreimage.localPosition.x - scoreimage.sizeDelta.x * i ,
                scoreimage.localPosition.y);
            scoreimage.GetComponent<Image>( ).sprite = numimage[number[i]];
        }
    }
}

実行結果

score

最後に(雑談)

インターンの時、実装に手間取ってた人がいたのと前々から書きたいと思ってたのでかいてみました。久々に記事を書くけど結構疲れるなぁ
ドラムロール式とか、ゼロ詰め方式とかあるけど実装方法思いつかなかった・・・・
気が向いたら実装チャレンジしてみるかも
あとこのブログもついに2万アクセス行きました。ブログ始めたのが2年くらい前なので大体1年1万アクセスくらいになるのかな。これからもゆるりと情報を発信していきたいと思います。3万アクセスになったらドメインでも取ろうかな・・・・

参考サイト様

UnityDOCUMENTATION Transform.SetParent

それは悲しみ (˘ω˘) uGUIのImageの移動、サイズ変更などなど

Unityにも使える!アプリに使える商用利用可能・再配布可能なフォントまとめ

画像の数字をスコアとして扱う」への2件のフィードバック

コメントを残す

以下に詳細を記入するか、アイコンをクリックしてログインしてください。

WordPress.com ロゴ

WordPress.com アカウントを使ってコメントしています。 ログアウト / 変更 )

Twitter 画像

Twitter アカウントを使ってコメントしています。 ログアウト / 変更 )

Facebook の写真

Facebook アカウントを使ってコメントしています。 ログアウト / 変更 )

Google+ フォト

Google+ アカウントを使ってコメントしています。 ログアウト / 変更 )

%s と連携中