mieki256's diary



2013/10/05() [n年前の日記]

#1 [unity] Unity上でCustom Fontの設定作業が自動化できた

(※ 2013/10/07追記。色々修正した版を _mieki256/ChrRectSet - GitHub にアップしておきました。この記事に書いてあるソースは古いです。)

Unity上で Custom Font を使えばビットマップフォントが使えるようになるけれど。
表示サンプル
1文字ずつUV座標等をGUIで入力していくのが面倒なので、なんとか自動化できないかと。何せ、1文字につき入力欄が10ヶ所、それを10〜95文字分とか、手入力してられないですわ…。

2種類の設定ができるようにしてみたり。 BMFont (Bitmap Font Generator)は _BMFont - AngelCode.com で入手できます。

使うフォント画像の例。 :

以下は、BMFontで出力したフォント画像の例。(M+フォントを使用。画像は256x256。) ギッチリ詰まってるからテクスチャが無駄にならないけど、計算で場所を求めていくのも、レタッチするのも、たぶん無理。 _配置情報(.fntファイル) が無いと使えない。

BMFont出力例



以下は、等間隔で配置されたフォント画像の例。(Molotフォント使用。256x64。数字のみ10文字分。) これなら計算で配置が求められるし、レタッチ等も楽だけど、テクスチャは無駄だらけ。

等間隔で配置されたフォント画像の例。

スクリプトの導入手順。 :

  1. Projects に Editor というフォルダを作る。
  2. Editorフォルダの中に C#ファイルを作成して、ChrRectSet.cs というファイル名にする。
  3. 下記ソースをコピペして保存。
  4. Unityのメニューに、Custom という項目が増えてるはず。

実行手順。 :

Custom → Custom Font Setting → Chr Rect Set を選択。ウインドウが開く。
メニュー選択

BMFontの出力画像を使う場合。.fnt は .txt にリネームしておく。
テーブルファイルを使って設定する場合
  1. Assets から、「Custom Font」「フォント画像」「文字配置情報が書かれたテキストファイル(BMFontが出力した .fnt を .txt にリネームしたもの)」の3つをD&Dで登録。
  2. Setボタンを押せば、Custom Font の Character Rects に、テーブルファイルの内容が代入される。

等間隔に配置されたフォント画像を使う場合。
等間隔に配置されてるフォント画像を使う場合
  1. ウインドウ表示後、Assets から、「Custom Font」「フォント画像」の2つをD&Dで登録して、「画像内でフォントが置かれている範囲」「横方向の文字数」「縦方向の文字数」「使う文字数」を入力してSetボタンを押せば設定される。

ただ、Setボタンを押して値が設定されても、すぐに変更が反映されるわけではないようで。Custom Font の Character Rects を開いたり閉じたりしてると、なんだかそのうち反映される模様。

ソースコード。 :

_ChrRectSet.cs
using UnityEngine;
using UnityEditor;
using System.Collections.Generic;

/*
 * BMFontで出力した bitmap font 画像の文字配置情報を
 * custom Font に設定するUnity拡張。
 * 
 * Custom → Custom Font Setting → Chr Rect Set でウインドウを開いて、
 * Custom Font、テーブルファイル(.txt)、フォント画像、
 * の3つをD&Dで登録して Set ボタンを押せば、
 * Custom Font の Character Rects を設定してくれる。
 * 
 * 等間隔で配置された bitmpa font 画像に関しても設定できる機能付き。
 * Custom Font、フォント画像、
 * 画像内でフォント部分が置かれている範囲、
 * 横方向の文字個数、縦方向の文字個数、使う文字個数、
 * を入力して Set ボタンを押せば設定してくれる。
 */
public class ChrRectSet : EditorWindow {
    public Font customFontObj;
    public TextAsset fontPosTbl;
    public Texture fontTexture;
    public Rect useTexRect = new Rect(0, 0, 256, 256);
    public int fontCountX = 8;
    public int fontCountY = 8;
    public int fontLength = 64;

    struct ChrRect {
        public int id;
        public int x;
        public int y;
        public int w;
        public int h;
        public int xofs;
        public int yofs;

        public int index;
        public float uvX;
        public float uvY;
        public float uvW;
        public float uvH;
        public float vertX;
        public float vertY;
        public float vertW;
        public float vertH;
        public float width;
    }

    // メニューに登録
    [MenuItem("Custom/Custom Font Setting/Chr Rect Set")]
    static void Init() {
        EditorWindow.GetWindow(typeof(ChrRectSet));
    }

    // 表示ウインドウの内容
    void OnGUI() {
        // Custom Font 登録欄
        customFontObj = (Font)EditorGUILayout.ObjectField("Custom Font", customFontObj, typeof(Font), false);

        // フォント画像指定欄
        fontTexture = (Texture)EditorGUILayout.ObjectField("Font Texture", fontTexture, typeof(Texture), false, GUILayout.Width(64), GUILayout.Height(64));

        // 文字テーブルファイル登録欄
        EditorGUILayout.Space();
        EditorGUILayout.LabelField("Use Font Table Text File", EditorStyles.boldLabel);
        fontPosTbl = (TextAsset)EditorGUILayout.ObjectField("Font Table Text File", fontPosTbl, typeof(TextAsset), false);

        // 実行ボタン
        if (GUILayout.Button("Set Character Rects")) {
            if (customFontObj == null) this.ShowNotification(new GUIContent("No Custom Font selected"));
            else if (fontTexture == null) this.ShowNotification(new GUIContent("No Font Texture selected"));
            else if (fontPosTbl == null) this.ShowNotification(new GUIContent("No Font Position Table file selected"));
            else CalcChrRect(customFontObj, fontPosTbl, fontTexture);
        }

        // 等分割して設定する場合の入力欄
        EditorGUILayout.Space();
        EditorGUILayout.LabelField("Grid", EditorStyles.boldLabel);
        useTexRect = EditorGUILayout.RectField("Use Texture Area", useTexRect);
        fontCountX = EditorGUILayout.IntField("Font Count X", fontCountX);
        fontCountY = EditorGUILayout.IntField("Font Count Y", fontCountY);
        fontLength = EditorGUILayout.IntField("Character Length", fontLength);
        if (GUILayout.Button("Set Character Rects")) {
            if (customFontObj == null) this.ShowNotification(new GUIContent("No Custom Font selected"));
            else if (fontTexture == null) this.ShowNotification(new GUIContent("No Font Texture selected"));
            else CalcChrRectGrid(customFontObj, fontTexture, useTexRect, fontCountX, fontCountY, fontLength);
        }
    }

    // フォントテーブルを元にして設定
    void CalcChrRect(Font fontObj, TextAsset posTbl, Texture tex) {
        // フォント画像のサイズを取得
        float imgw = (float)tex.width;
        float imgh = (float)tex.height;

        // 文字テーブルの内容を取得
        string txt = posTbl.text;
        List<ChrRect> tblList = new List<ChrRect>();
        int asciiStartOffset = 128;
        int maxH = 0;
        foreach (string line in txt.Split('\n')) {
            if (line.IndexOf("char id=") == 0) {
                ChrRect d = GetChrRect(line, imgw, imgh);
                if (asciiStartOffset > d.id) asciiStartOffset = d.id;
                if (maxH < d.h) maxH = d.h;
                tblList.Add(d);
            }
        }
        ChrRect[] tbls = tblList.ToArray();

        // index値を調整
        for (int i = 0; i < tbls.Length; i++) {
            tbls[i].index = tbls[i].id - asciiStartOffset;
        }

        // 新しい CharacterInfo を作成
        SetCharacterInfo(tbls, fontObj);
    }

    // 等分割して設定
    void CalcChrRectGrid(Font fontObj, Texture tex, Rect area, int xc, int yc, int num) {
        float imgw = (float)tex.width;
        float imgh = (float)tex.height;
        int fw = (int)(area.width - area.x) / xc;
        int fh = (int)(area.height - area.y) / yc;
        List<ChrRect> tblList = new List<ChrRect>();
        for (int i = 0; i < num; i++) {
            int xi = i % xc;
            int yi = i / xc;
            ChrRect d = new ChrRect();
            d.index = i;
            d.uvX = (float)(area.x + (fw * xi)) / imgw;
            d.uvY = (float)(imgh - (area.y + (fh * yi) + fh)) / imgh;
            d.uvW = (float)fw / imgw;
            d.uvH = (float)fh / imgh;
            d.vertX = 0;
            d.vertY = 0;
            d.vertW = fw;
            d.vertH = -fh;
            d.width = fw;
            tblList.Add(d);
        }
        ChrRect[] tbls = tblList.ToArray();
        SetCharacterInfo(tbls, fontObj);
    }

    // 新しい CharacterInfo を Custom Font に上書き設定
    void SetCharacterInfo(ChrRect[] tbls, Font fontObj) {
        CharacterInfo[] nci = new CharacterInfo[tbls.Length];
        for (int i = 0; i < tbls.Length; i++) {
            nci[i].index = tbls[i].index;
            nci[i].width = tbls[i].width;
            nci[i].uv.x = tbls[i].uvX;
            nci[i].uv.y = tbls[i].uvY;
            nci[i].uv.width = tbls[i].uvW;
            nci[i].uv.height = tbls[i].uvH;
            nci[i].vert.x = tbls[i].vertX;
            nci[i].vert.y = tbls[i].vertY;
            nci[i].vert.width = tbls[i].vertW;
            nci[i].vert.height = tbls[i].vertH;
        }
        fontObj.characterInfo = nci;
    }

    // フォントテーブルの1行分(1文字分)を構造体に記録
    ChrRect GetChrRect(string line, float imgw, float imgh) {
        ChrRect d = new ChrRect();
        foreach (string s in line.Split(' ')) {
            if (s.IndexOf("id=") >= 0) d.id = GetParamInt(s, "id=");
            else if (s.IndexOf("x=") >= 0) d.x = GetParamInt(s, "x=");
            else if (s.IndexOf("y=") >= 0) d.y = GetParamInt(s, "y=");
            else if (s.IndexOf("width=") >= 0) d.w = GetParamInt(s, "width=");
            else if (s.IndexOf("height=") >= 0) d.h = GetParamInt(s, "height=");
            else if (s.IndexOf("xoffset=") >= 0) d.xofs = GetParamInt(s, "xoffset=");
            else if (s.IndexOf("yoffset=") >= 0) d.yofs = GetParamInt(s, "yoffset=");
        }

        // Uv情報を算出
        d.uvX = (float)d.x / imgw;
        d.uvY = (float)(imgh - d.y - d.h) / imgh;
        d.uvW = (float)d.w / imgw;
        d.uvH = (float)d.h / imgh;

        // Vert情報を算出
        //d.vertX = (float)d.xofs;
        d.vertX = 0.0f;
        d.vertY = -(float)d.yofs;
        d.vertW = d.w;
        d.vertH = -d.h;

        // widthを算出
        d.width = d.w;

        return d;
    }

    // "wd=数値を示す文字列" を数値にして返す
    int GetParamInt(string s, string wd) {
        if (s.IndexOf(wd) >= 0) {
            int v;
            if (int.TryParse(s.Substring(wd.Length), out v)) return v;
        }
        return int.MaxValue;
    }
}
ソースのライセンスは…Unityってそのあたり何か強制されるのかな? 特にそういう制限がないのであれば Public Domain ってことで。

どうせそのうち仕様が変わって使えなくなる予感。つーかフツーはNGUIとやらを買って使うのだろうからこんなの使わんですわな。

#2 [anime] 「とある科学の〜」最終回を視聴

参りました。なんかめっちゃ詰め込んできたというか、盛って盛って盛りまくった感が。お祭り感パねえス、みたいな。

本編中でOPが流れてくるあたりにグッときました。長井監督は「あの花」「あの夏」でそのあたりの使い方が上手かった印象があるので、なるほど今回も上手いなあ、これはクルなあ、と。でも、さすがに2回目・3回目は「…え? また?」と思ってしまったけれど。それでも 曲の中盤ぐらいでやっぱりテンションが高まってしまっている自分を自覚できたりもして、これはこれで良し、全然アリだわ、とも。 どの曲も比較的ノリノリでイケイケな曲が多かったから、見事に展開をブーストできていた印象。自分は1期を未見なのでアレですけど、twitterを眺めてたら、1期を見てた人には「キター」って感じだったりもしたようで。作戦成功、だろうなと。まあ、こういう策を「安直」云々とdisってる意見も見かけたけど、よりグッドな代替策も思いついてないのに禁じ手ばかり増やして結果盛り下がる見せ方をしちゃうより、こういうソレでグイグイ押してくほうがまだアリではと。使えそうな技はなんでも使うべき。みたいな。

中盤、美少女をとにかく酷い目に合わせるルール大会絶賛開催中だった頃に、「ああ、もうこういうのはいいですわ」という気分になって、その後は流し見程度だったのですけど。最後に 全部繋げてくると知ってたら…。これはもうちょっと真面目に見ておくべきだったかなと少し後悔したりもして。TVアニメは最後まで見ないと分かりませんな…。

何にせよ、とにかく盛り上げてくぞー、という姿勢がビンビンに伝わってきたような気がするので、かなり好印象なのでした。やりたいことが明確で、方法も合ってたのだから、これを褒めずしてどうするよ、と。

そして、スタッフロールを見てビックリ。吉野脚本だったのか…。なんだか納得。こういう話が書けるあたりが強みなのだろうなと。お祭り感溢れるソレって誰でも書けるものではないだろうと思うので、アニメ業界にとっては貴重な人材なのだろうなと想像したり。

#3 [anime] ログホライズンとやらを見逃した

今日から放送されるとは知らなくて、予約録画してなかった…。途中で気づいて見始めたけど、1話で結構伏線が入ってたという話を聞いて、これは失敗したなと。

えてしてアニメ番組は1話だけ無料配信してたりするものだし、今からでもどこかで見れるんじゃないのかと思って探してみたけど、これが全く見当たらず。NHKオンデマンドの有料配信リストにすら存在していなくてちょっとビックリ。NHK、商売する気が無いな…。いや、そもそもNHKって、商売という概念が存在する組織かどうかも分からんのだけど。受信料で回ってる組織だろうし。

とりあえず、そのうち再放送されることを期待。NHKの強みって比較的再放送がしやすいことだろうと思うわけで。面白い番組を作れたという自負があるならどんどん再放送していただきたい。まあ、面白くなるかどうかはまだ分からんのですけど。…冬休みや春休みに「今からでもまだ間に合う! 一挙再放送!」とかヨサゲな気も。

#4 [anime][neta] 宮崎駿のルパンの娘企画とふたりはミルキーホームズ

唐突に気がついたのでメモ。

昔、宮崎駿が、ルパンの娘と不二子の娘が二人で活躍するアニメ企画を考えてた時期があるそうで。まあ、宮崎駿が一人で考えた企画かどうかは不明ですし、結局は世に出なかったわけですけど。ちなみに、ルパン三世の子孫が出てくる作品は、ルパン小僧とかルパン8世とか既にあるらしいですが。

で。考えてみたら、先日BSでの放送が終わった「ふたりはミルキーホームズ」って、宮崎駿のルパン娘企画の実現にほぼ近いよなと。基本設定がクリソツ。もちろん、双方の企画におそらく関連性は一切無いだろうと思うのですけど。また、基本設定がクリソツだから、「ふたりはミルキーホームズ」が宮崎アニメ並みに面白かったかというと、そこは言及を避けたいところですが。

しかし、おそらくは没になったのであろう宮崎駿のアニメ企画が、今頃になって、スタジオジブリだの何だのから随分と遠く離れた場所で、いかにもな今風の萌えキャラデザインで世の中にポンと出てきてしまったこの状況。なんだか面白いというか、興味深いなと。

昔の宮崎駿は色んなアニメ企画を出したけど徹底的に没にされたと聞いてるわけですが。「ふたりは〜」のソレを思い返すと、今のアニメ業界なら、当時の没企画もバンバン通るんじゃないのかと思えてきたのでした。

スタッフロールに「企画原案:宮崎駿」と出てくる日本製アニメが次々に乱造される時代が到来したらいいのに。てなバカ妄想を。

「宮崎駿妄想集」が欲しい気もしてきたり。 :

手塚治虫は、生前、「自分の頭の中には人に売りたいぐらい漫画のアイデアがたくさんある」「だけど時間が無くて漫画にできない」とボヤいてたそうですが。

せめて、アイデアノートのような形で残しておいてくれたら助かったのでしょうけど…。何せ、起きてる間はずっと原稿と格闘してたらしいからその手のメモも無いだろうし、一人別室で作業してたからスタッフの前でアイデアを喋って披露することも無かっただろうと。結果、その大量のアイデアはどこにも残らず、手塚先生が亡くなると同時に全てこの世から消滅したわけで。実にもったいない。

そんな話を思い返すと。宮崎駿監督には、手塚先生と同じ轍を踏んでほしくないなと。殴り書きのメモでもいいし、スタッフの前でひたすらアニメの企画を喋り続けるのでも構わないし。何かしら周囲にアレコレ残しておいてほしいなと。というか、発言を毎日書き止めてblogその他にアップしていく専門の係がジブリに居てもいいぐらい。

で、その発言・妄想・夢想をまとめた本が出てきたら尚いいよなと。「宮崎駿妄想妄言集」とか「宮崎駿のアニメ企画産卵現場」とか「宮崎駿アニメ企画の精子ブシャー!」とかそんな感じで。「雑想ノート」よりはるかに無責任な内容で、企画としてちゃんと読めるものじゃなくても全然OK。そんなんでも、若くて才能のある作家さん/アニメ業界人にとっては、着想のヒントぐらいにはなるだろうと。「ネタに詰まったら『宮さんブシャー本』を開け」と業界内で言い伝えられる貴重な参考書になったりしないかと。

宮崎駿監督自身は、長編アニメはもう作らないと言っているのだから、監督の頭の中にあるアイデアの数々は、監督が一人でこっそり抱え込んでいても、もはや一銭の価値もないわけで。だけど、そのアイデアに触れた若い人が、化学反応を起こして何かを作ってくれる可能性は高いだろうと。とにかく出力して、若者達の目に触れるところに、これみよがしに置いておけば、それらのアイデアは価値あるものに変化する…ような気が。

創作という行為が、その作家さん自身の両足で走り続ける行為だとすれば。宮崎駿の中で眠らせてるアイデアの数々は、これから走り出したいけど今は立ち止まってる、そんな若い作り手の背中を押し出してくれる、先輩の熱い想いがこもった風になったりしないかなー、と。

でもまあ、それがジブリ美術館の展示物だったりするのかな…。ジブリ美術館の中に飾ってある宮崎駿の作ったアレコレが、それを目にした少年少女の中で熟成されていって、ある時何かの拍子にポンと出てくる、てな展開を期待してたりするのかしら、とも。どうしてジブリ美術館に注力するのだろうと若干不思議に思ってたけど、そういうことであれば、なんだか夢がある話のように思えてきたり。

でも、ジブリ美術館って、全国の子供達が見れる場所じゃないし…。あそこは、比較的選ばれた子供達だけが入れる場所じゃないのかと…。1年先まで予約で一杯とどこかで聞いた記憶も…。ていうか、地方の子供達はなかなか行けないよな。

そのうちバーチャルジブリ美術館が作られたらいいのに。ニコニコ動画あたりからリンクを張って。てなバカ妄想を。

2013/1027追記。 :

コメント欄で「ジブリ美術館の予約システムの作りからして1年前から予約なんて無理ッス」と教えていただきました。ありがとうございます。自分の聞いたソレはデマだったのですね…。

「ジブリ美術館ならそのくらい人気があるだろうなあ。子供にとっては夢の国のような場所だろうし」と思ってしまって、すんなりデマを信じちゃいました…。

この記事へのツッコミ

Re: 宮崎駿のルパンの娘企画とふたりはミルキーホームズ by 名無しさん    2013/10/23 22:42
ジブリ美術館は大体一ヶ月前ならまず予約できますよ。
年末とか夏休み、大型連休とかだと難しいですけど。
風立ちぬ効果で混んでますが、時期に因っては平日なら翌週のチケットも買えたりしますし、
そもそもチケット予約は翌月分までしか出来ないので、1年先までって事はないです。
物理的に遠い分にはどうしょうもないですが

以上、1 日分です。

過去ログ表示

Prev - 2013/10 - Next
1 2 3 4 5
6 7 8 9 10 11 12
13 14 15 16 17 18 19
20 21 22 23 24 25 26
27 28 29 30 31

カテゴリで表示

検索機能は Namazu for hns で提供されています。(詳細指定/ヘルプ


注意: 現在使用の日記自動生成システムは Version 2.19.6 です。
公開されている日記自動生成システムは Version 2.19.5 です。

Powered by hns-2.19.6, HyperNikkiSystem Project