ニコ生ゲームを作ろうと思ったらすぐコピペしよう その4


第4回のゲームは4択クイズです。早く多く回答することで得点が高くなるタイプです。

今回は使い回しを想定して出題と回答を追加しやすいようにしました。


出来上がったものがこちらになります。アツマール公開版とは問題やフォントが違います。

※ 不正解時の答え合わせ機能を追加しました。[2021/04/09]
※ 冒頭のランキング表示を追加しました。[2022/06/13]


今回は公式の再配布可能な音ファイルも入れてます。CC BY 2.1 JP DWANGO Co., Ltd.
絵はこちら
ゲームはこんな感じで選択肢が表示されます。アツマールはこちらです。

  


■目次

  • 出題内容作成
  • 正解と不正解
  • パネル配置
  • 画像表示
  • 文字表示
  • 得点設定
  • 不正解時の答え合わせ
  • 共通ランダム化
  • ダイナミックフォント
  • 縁取りビットマップフォント作成





出題内容作成

出題内容は、233行目quizという配列に、1行ずつ追加していきます。

 let imagescale = 1; //上部出題欄toppanelと 大型回答欄largepanel
 let textscale = 1; //中央出題欄centerpanelと 小型回答欄smallpanel
 let fontsize = 42; //文字直接入力
 let quiz = [// top ,center, 正解, 不正解
  [ "vene", "★の場所のイタリアの都市は?", ["ヴェネツィア"], ["ローマ", "ナポリ", "ミラノ", "ジェノヴァ"]],
  [ "snake", "巳年のつぎは何年?", ["午年"], ["未年", "酉年", "丑年"]],
  [ "", "巳年のつぎは何年?", ["午年"], ["未年", "酉年", "丑年"]],
  [ "巳年のつぎは何年?", "", ["午年"], ["未年", "酉年", "丑年"]],
  [ "", "ローマはどこにある?", ["roma"], ["boro", "jeno", "napo", "vene"]],
  [ "", "仲間はずれはどれ?", ["crab", "dog", "snake"], ["crow", "duck", "hawk"]],
 ];


一つ一つの問題は4つの値が格納されています。

それぞれ" "で囲まれた文字列か、文字列が格納された配列です。

  [ 上部出題内容 ,  中央出題内容 ,  正解 ,  不正解 ],

行の最後にもカンマ , がありますので忘れないようにしてください。

アツマール版では出題は110問用意しました。
1問正解後の待ち時間が1.25秒あるので、制限時間60秒だと最大48問進むことになります。
48問よりは多くしておかないと同じ問題が出てしまいます。
問題が少ない場合は制限時間も減らしてください。



パネル配置

パネルの配置レイアウトは3種類あり、上部出題中央出題の文字列で変化します。

1.両方あるタイプ

画像で出題する標準の形。

  [ "snake", "巳年のつぎは何年?", ["午年"], ["未年", "酉年", "丑年"]],

  


2.上部がないタイプ

問題文だけで出題する、または画像で回答させたい場合
一番最初の項目を "" にする。

  [ "", "巳年のつぎは何年?", ["午年"], ["未年", "酉年", "丑年"]],
  [ "", "ローマはどこにある?", ["roma"], ["boro", "jeno", "napo", "vene"]],


  
  
  



3.中央がないタイプ

問題文がない画像に問題文が入っている、または問題文を大きく表示したい場合
二番目の項目を "" にする。

  [ "巳年のつぎは何年?", "", ["午年"], ["未年", "酉年", "丑年"]],

  


正解と不正解

正解と不正解は3つ目と4つ目の場所に入れます。
両方とも配列にするため [ ] に囲んで入れます。文字列が1個しかなくても囲みます。

  [ "", "ローマはどこにある?", ["roma"], ["boro", "jeno", "napo", "vene"]],
  [ "", "仲間はずれはどれ?", ["crab", "dog", "snake"], ["crow", "duck", "hawk"]],


 


配列の中には文字列 "〇〇" を入れますが、正解は1個以上、不正解は3個以上必要です。

両方とも多い分には問題ありません。ゲーム開始時にランダムで選択されます。
クイズは覚えゲーになるのが避けられませんが、正解も不正解も複数あると寿命が伸びます。


画像表示

画像を配置させる場合はassetの名前を入力します。4つの入力箇所全て共通です。

89行目の let assets というリストの中にその文字列があればその画像が配置されます。


用意する画像のサイズはバラバラで大丈夫ですが、だいたい同じサイズが好ましいです。
一括でスケールを調整することはできますが、個別に調整はできません。

このように 400 x 320 と 80 x 80 の画像が混在していると、
小さい方が小さいままになり、調整しきれません。

 
  



ただし、大きいパネル横長の小さいパネルは、スケール調整が別々になっています。


大きいパネル

余白抜きで 幅420 高さ280 ぐらいの画像、余白は透明が望ましい。

230行目の let largepanelscale で一括サイズ調整が可能です。

上部出題欄 toppanel
 大型回答欄 largepanel




横長の小さいパネル

余白抜きで 幅420 高さ110 ぐらいの画像が望ましい。
文字を配置する想定のパネルですが、画像も配置できます。

231行目の let smallpanelscale で一括サイズ調整が可能です。

中央出題欄 centerpanel
 小型回答欄 smallpanel






文字表示

画像の代わりに直接文字を入力することもできます。

89行目の let assets にない文字列であれば自動で文字入力なります。

 [ "vene", "★の場所のイタリアの都市は?", ["ヴェネツィア"], ["ローマ", "ナポリ", "ミラノ", "ジェノヴァ"]],





フォントサイズは232行目の let fontsize で一括調整できます。
バラバラ調整は不可です。


ただし、残念ながら改行は使えません。
改行したい場合は画像にしてGoogle図形描画で書くのが早いと思います。

一応、akashic-labelというものもあり、これを導入すれば改行ができるようです。
こちらこちらこちらを読めばできるかもしれません。私はできません。


得点設定

得点は正解時は100~150点の範囲に設定しています。
時間経過で0.1秒あたり1点減っていきます。

不正解時の減点の値は305行目にあります。

  let score = -50; // 不正解のときの得点
  if (panel.tag.seikai) {
    score = Math.floor(150 - (g.game.age - startframe) / g.game.fps *10);
    if (score < 100) score = 100;
    // if (soundstate) scene.assets["se_seikai"].play().changeVolume(0.4);
  } else {
    // if (soundstate) scene.assets["se_hazure"].play().changeVolume(0.6);
  }
  g.game.vars.gameState.score += score;
  if (g.game.vars.gameState.score < 0) g.game.vars.gameState.score = 0;


不正解によるマイナスで総得点もマイナスになるかどうかは、314行目で判定しています。
313行目の得点計算の直後で判定することで、マイナス点の状態を極力なくしています。

総得点がマイナスになってもいい場合は、314行のみ削除してください。



不正解時の答え合わせ

不正解時の答え合わせ機能について、要望が多かったので追加しました。

144行目の kotaeawase を true にすると、選択すると同時に正解に丸が出てきます。

  let kotaeawase = true; // 不正解時に答え合わせをするか

false にすると無効になります。

この機能があると、デバイスやアカウントを2つ使うと簡単にカンニングできてしまうので
意図的に外していました。私の作品では今のところ無効にする予定です。

ただやはりプレイした方の中で「正解が知りたい」という声は多く、
必ずしもガチのクイズ競技として遊ぶわけでもないと思います。
問題を単に楽しんで遊ぶ用途に機能を選択できるようにしました。




改造箇所の説明はここまでです。
ここからは細かい説明です。


共通ランダム化

それぞれのクイズは難易度が異なるので、プレイヤー全員が同じ問題になるようにします。
一方で、マンネリ化しないように多数からランダムに選択します。

共通したランダムの結果を使用するのがparam.randomです。

133行目に以下の定義があります。

  let syncrandom = param.random;

このsyncrandomという文字列は私が勝手につけているだけなので何でもよいです。
シンプルにrandomでもいいです。

この定義は、function makescene(param) {またはfunction main(param) { の中にあり、
scene.onLoad.add(function () { の外にあります。

アセットの登録と同じような場所ですね。



この後、syncrandomを使うと共通化されたランダムが使えるようになります。

利用するときは、以下のように使います。248行目や389行目にあります。

  syncrandom.generate();
  syncrandom.get(2, 4);


ただし、getの方は今後推奨されないらしくそのうち使えなくなるかもしれません。
その場合は以下のようにしてください。

  2 + Math.floor(3 * syncrandom.generate());


ダイナミックフォント

今回のコードではダイナミックフォントを使用しています。

ダイナミックフォントはフォント画像やフォントファイルを用意しなくて良いので、
漢字などの様々な文字をすぐに使えるのがメリットです。

デメリットはフォントの種類が3種類しかないことと、縁取りに限界があることです。


個人的な話ですが、ダイナミックフォントは縁取りと太字化ができないと思いこんでいて、
視認性の面からあまり使っていませんでした、

実際には十分実用に耐えますし、今回のように文字表示が多い場合は大活躍するので、
説明していきます。


まず、153行目のように let font = new g.DynamicFont({ と宣言します。

// フォントの生成
let font = new g.DynamicFont({
  game: g.game,
  fontFamily: "sans-serif",
  // "sans-serif": サンセリフ体・ゴシック体のフォント。
  // "serif": セリフ体・明朝体のフォント。
  // "monospace": 等幅フォント
  fontWeight: "bold", // "normal" または "bold"
  size: 96, fontColor: "black", strokeWidth: 8, strokeColor: "white",
});

ビットマップフォントと違ってglyphの定義が不要で、アセットの登録も不要です。

使うときは、g.Labelのfontにフォントの名前fontを入れるだけです。

// スコアラベル
let scorelabel = new g.Label({
  scene: scene, text: "", parent: uilayer,
  font: font, fontSize: 42,
  x: g.game.width-16, y: 40,
  anchorX: 1, anchorY: 0.5, opacity: 1,
});


ダイナミックフォントのパラメータは主に以下のものがあります。

  • fontFamily : "sans-serif""serif""monospace" から選ぶ
             それぞれのフォントのデザインはこちらで確認

  • fontWeight : 太字なら"bold"。通常は"normal"

  • size      : 大きめのサイズ 96 を入れる
               実際には都度サイズを指定するが、大きいとぼやけにくくなる

  • fontColor  : 色指定"red""#112233""rbg(0,100,200)"など
              色の違うフォントを併用する場合は、let font_redなどを別途定義

  • strokeWidth : 縁取りの太さ。サイズが96の場合はだいたい 8 が限界
             もっと太くしたいところだが、文字が欠ける

  • strokeColor : 基本的に"white"か"black"のどちらか。他の色も可



フォントの色はg.Labelのfontの方に、textColor: "red",と入力して変えることもできます。
こっちはfontColorではなくtextColorです。

しかしこの場合は、フォントの色と縁取りのstrokeの色が両方とも変わってしまいます。
縁取りをしない場合はこの方法も使えますね。



[2021/2/19加筆]
ここからの記事は、縁取りフォントにビットマップフォントを使う前提で記載しています。
ダイナミックフォントでも縁取りが簡単にできることがわかりましたので、
フォントにこだわらない方やクイズゲームにだけ興味がある方はこの先は無視してください。

縁取りをもっと分厚くして視認性を向上させたい場合や、
こだわりのフォントを使いたい場合は、ここからの手順を参考にしてください。

個人的に丸みのあるフォントが好きなので、この方法で作成したものを使っています。
下の方で作成したものを公開しています。しかし、漢字はほぼ全く使えません。



縁取りビットマップフォント作成

以前も解説しましたが、フォントを縁取りすると視認性が大幅に改善します。

今回のクイズゲームは背景に白いパネルがあるのでなんとかなりますが、
一度よく使う文字で作ってしまえば使い回せるので、作り方も紹介します。

まず、フォントファイルはこちらのリンクにあるものを確認してください。

 


基本的に縁取りフォントファイルのの作り方は3つあります。

  1. bmpfont-generator の strikeコマンド
  2. bmpfont-generator と 画像縁取り加工
  3. bmfont64 と glyphファイル変換

1.と2.はニコ生ゲームを作ろうと思ったらすぐコピペしよう その2で紹介しましたが、
1.は縁取りを太くできず、2.は縁取りが若干不自然、という問題があります。

3.の方法であればきれいに作れますので今回紹介します。
しかし手順が長く細かいので、苦手な方は上のリンクにあるものをそのまま使ってください。


その長い手順を5個に分けてみます。

  1. bmfont64導入
  2. 設定ファイル読み込み
  3. 各種設定
  4. FNTファイル出力
  5. JSONへ変換


1. bmfont64導入

bmfont64(正式名称:Bitmap Font Generator)はこちらでダウンロードできます
ダウンロードしたファイルを起動するだけです。

このソフトはドワンゴやakashic engineとは全く関係ありません。ご注意願います。

bmfont64でGoogle検索をしてもらったら、最初に日本語の解説サイトが出てきます。
そちらを見るだけでも基本的な使い方はわかると思います。


2. 設定ファイル読み込み

メニューの option から Load configuration を選択して設定ファイルを読み込みます。

読み込む設定ファイルは上のリンクにあります。中のfont_round.bmfc を開いてください。




これで縁取りや文字の種類などの設定が読み込まれます。

今回こちらのフォントを使っています。このフォントがないとエラーが出るかもしれません。
あとから変えられるのでエラーは一旦無視してください。



3. 各種設定

設定はいくつかありますが、まずメニューに有る Font settings から。



めぼしい設定は以下のとおりです。他のものはあまり理解していません。

  • Font フォントを指定します。Windowsにインストールしたフォントが使えます
  • Add font file 直接フォントファイルを指定することもできます
  • Size フィントサイズ。小さいと拡大時にボケる。大きいと画像が大きくなりすぎる
  • Match char height 高さを揃えるかどうか。必要だったようなそうでもないような…
  • Outline thickness 縁取りの厚さ(太さ)



次は Export options



  • Texture Width - Height
             生成する画像のサイズ
             文字数や文字の大きさによって合わせる
             数値が小さいと画像が2枚になるのでそうならないような大きさにする
  • Chnl - Value - Invert
             文字色と縁取り色の変更
             設定の法則性が難解なので、既存のファイルを参考にしてください
             もしくは画像編集ソフトで画像の色調を直接調整してください


今回フォントのサイズを48でやっていますが、拡大時にぼやける場合は
Size 、Outline thickness 、Texture Width - Height をそれぞれ変更してください。



そして、文字選択は2つの方法があります。

1つ目メインのウィンドウでクリックして追加していきます。



右側がカテゴリ分けになっていて、名前をクリックすると左側が切り替わります。
チェックボックスをクリックすると全部登録されてしまいます。
カテゴリを切り替えて、左側の文字を一つ一つクリックして文字を選択していきます。

文字にはコードがあり、HTML数値文字参照(10進数表記) が右下に表示されています。
上の例だと9632という数字です。これを参考にして探してもいいのですが…
次に説明する方法のほうが簡単かもしれません。


2つ目の方法は文字が書かれたテキストファイルを読み込む方法です。
bmpfont-generatorにも似たような方法がありました。

Edit の Select chars from file からファイルを指定できます。
あまり使ってませんでしたが、簡単そうですね。
テキストファイルの保存はUTF-8にしましょう。





4. FNTファイル出力

設定ができたら出力して確認しましょう
Option - Save bitmap font as... からファイルを指定して保存します。

FNTファイルというのが出てきますが、同時に画像も生成されます。
画像が1枚に収まっていて、色や文字の綺麗さが問題なければOKです。

FNTファイルをテキストエディタなどで開くと、
「char id=」で始まる行が文字の数だけあります。



id , x , y , width , height の5つの値が今後フォントファイルを作るのに必要ですが、
jsonと書き方が違います。

また、右側のoffsetの値や下の方にあるkerningも不要です。

次にこれらを加工していきます。


5. jsonへ変換

jsonの書き方に変換するには、こちらスプレッドシートを使っています。
変換するプログラムをササッと作れればいいんですが、私はそういうのじゃないので。

このシートは閲覧可能ですが、編集不可にしています。
ご自身のGoogleドライブにコピーして使ってください。

使い方は以下のとおりです。

  1. FNTファイルの「char id=」で始まる行をすべてコピーする
  2. シートの左端A列に貼り付る
  3. 右端のO列をコピーして、jsonファイルに貼り付ける





縁取りフォント作成の手順は以上です。

ビットマップフォントはちょっとのミスでテストプレイの表示が真っ白になります。
これだけ長々とやって真っ白になると落ち込みますが、
既存のうまく行っているファイルと見比べたりしてなんとか頑張ってください。




半分近くがクイズと直接関係ないフォントの話ですが、これで終わりです。

クイズの方は文字と画像とタイトル画面とBGMあたりを変えるだけでゲームになりそうです。
興味あればぜひ改造してみてください。

[2021/3/2]
この記事のコードを利用していただいた方が現れました! gm18707 gm18805
パネル画像やフォントの雰囲気が統一されていて素晴らしいです。
この一連の企画が役に立ってるかどうか知る方法がなかったのでひとまず安心しました。

この記事以外にも、コピペできそうなコードや解説を公開しています。
こちらに記事のリンクがまとまっていますので御覧ください。