isobe_yakiのブログ

ニコ生ゲーム開発者向けの記事を書きます

ニコ生ゲーにフォントを埋め込んでお洒落にする

ビットマップフォント

AkashicEngineにはBitmapFont | Akashic Engineというものがある。あらかじめ画像化したフォントとその画像のどのエリアがどの文字なのか対応付けたjsonをコンストラクタに渡すことで使えるようになる。

公式で配ってるビットマップフォントの画像
多分ほとんどのゲームがこれで個性的なUIを作っていると思う。でも、1つ問題があって、この方式だと文字サイズを動的に変更できなかったり、縁取り指定が出来なかったり、ユーザー名のような想定外の文字の表示には使えない(or厳しい)。

ダイナミックフォント

ビットマップフォントで対応しきれない問題を解決するにはやはりDynamicFont | Akashic Engineだろう。こちらはシステムにインストールされたフォントを使って動的にサイズや縁取りを変更してあらゆる文字がレンダリングできる。事前に必要な文字を考慮する必要が無い。

非常に便利なのだがこれもこれで問題があり、使えるフォントがユーザー環境依存なのだ。画像とか動画編集良くするよって人なら有名どころのフォントもインストールされているだろうが、大抵のユーザー特にスマホ勢などは基本的なフォントしか使えないだろう。そのためAkashicのリファレンスにも次のように書かれている。

フォント名として指定できる値は環境に依存する。 少なくとも "sans-serif", "serif", "monospace" (それぞれサンセリフ体、セリフ体、等幅の字体) は有効な値である。

Webフォント

webフォント - Google 検索 Web界隈詳しくないんでよく知らないけど、なんかWeb上にフォントが公開されてて、ページデザインとかに自由に使っていいよーみたいなのをWebフォントというらしい。

@font-face {
  font-family: "Open Sans";
  src:
    url("/fonts/OpenSans-Regular-webfont.woff2") format("woff2"),
    url("/fonts/OpenSans-Regular-webfont.woff") format("woff");
}

こんな感じでcssの中にフォントファイルのURLを指定するだけでいいので、自分でフォントを配布する必要もユーザーのシステムにフォントをインストールする必要もない。

これをダイナミックフォントに使えたらめちゃくちゃ便利そう、だがisobe-yaki.hateblo.jpで書いたようにニコ生ゲームから外部サイトへのアクセスはできないのでもちろんWebフォントも使えない。

otf埋め込み

多くのWebフォントはコンテンツへ埋め込んで使うこともできる*1ので、実はニコ生ゲームにフォントファイルを直接埋め込めばダイナミックフォントで使えるのだ。 フォントファイルの埋め込みやURLの取得方法については過去の記事参照。

isobe-yaki.hateblo.jp

まず、game.jsonに以下のように記述する。

{
  ...,
  "assets": {
    "font": {
      "type": "text",
      "path": "font/hogehoge.otf"
    }
  },
  ...
}

そのページで読み込んだフォントを使えるようにするためにはFontFace - Web APIs | MDNクラスを使う。詳しい説明は省くが、とにかくコンストラクタにフォントファイルのURLかバイナリデータを渡せばフォントをインストールできるようだ。

ここで少しハマったのだが、URLを渡すコンストラクタだとエラーが出てしまった。

new FontFace("_hoge", g.game._assetManager.configuration.font.path)
.load()
.then(font => {
    document.fonts.add(font);
});

エラー文は忘れたが、外部のアクセスはダメです的なやつだった気がする。なので最初は(フォント読み込み無理なのか?)と思ったが、バイナリデータを渡す方のコンストラクタで普通に読めた。なんでや

fetch(g.game._assetManager.configuration.font.path)
.then(b => b.blob())
.then(blob => blob.arrayBuffer())
.then(buf => {
    new FontFace("_hoge", buf)
    .load()
    .then(font => {
        document.fonts.add(font);
    });
});

ここまでできたら後はg.DynamicFontのコンストラクタでこのフォントの名前を指定すれば自由にフォントが使えるようになる。(もちろん非同期読込なので完了まで待ってからDynamicFontを作ること)

new g.DynamicFont({
    game: g.game,
    fontFamily: '_hoge',
    size: 30
});
フォントでかいよ・・・

とはいえニコ生ゲームは容量制限があって未圧縮状態で10MB以下でなければならない。フォントファイルは物によって全然サイズが違うが日本語フォントだと大体数MBは食うのでかなり厳しい。

やや力技だがフォントファイルを圧縮してからパッケージに含めて実行時に自前で展開するという方法もある。jsのライブラリでもzip展開できるのあるし検討してみるべきだろう。とは言えフォントファイルって70%くらいにしか縮まないので凝ったフォントはやはり厳しい。ちなみに自分はc++&emscriptenで開発しているので圧縮展開は自前のbzip系アルゴリズムを実装した。

追記

現在ゲームの容量制限が緩和されて30MBまで使えるようになっている。なので割と自由にフォントを使えそうだ。更に圧縮をする場合もCompression Streams API - Web API | MDNgzipが使えるので簡単だし展開速度も普通に速いので積極的に使っていいと思う。

*1:フォントごとにライセンスが異なるので使いたいフォントのライセンスをよく確認すること