鳥小屋.txt

主に自作ゲームをつくったりしているよ。制作に関することやそうじゃないことのごった煮ブログ

『ニコ生早押しクイズセレクション』を公開しました

1ヶ月くらい前にね!(記事書くの遅すぎ)

『ニコ生早押しクイズセレクション』

そんなわけでクイズゲームです。ゲームというかツールなのか?

名前に露骨に入っているとおり、ニコ生ゲーム向けのやつです。生放送上で配信者が視聴者のみんなと早押しなクイズゲームをできる……みたいなイメージで作ったやつです。

自作クイズを出題しよう!

このゲームにはプリセットのクイズが入っていますが、プリセットだけではなく自作のクイズが使えます(パソコン配信のみ)

クイズのエディタどうしようかな~と思ったのですが、エディタ作るのも大変だし、僕がプリセットのデータを作るために作ってた Google スプレッドシートをもう少しだけ便利にしたテンプレートを公開して、それで作れるようにしました。

作るの面倒くさい~と思うかもしれません。その通りです。でも、僕もプリセットの入力を頑張ったのでキミも頑張ってください(意味不明)

選択肢、大変すぎる。

中身の話

@akashic-extension/coe

@akashic-extension/coe という公式のライブラリがあります。説明には以下みたいなことが書いてあります。

COE フレームワークは Akashic 上で共体験 (co-experience) を実現させるための拡張ライブラリです。 共体験は主に次の機能を提供します。

  1. Akashic コンテンツの容易なマルチプレイ化
    • 各ユーザからのメッセージの集計
    • 各ユーザに対するメッセージのブロードキャスト
  2. コンテンツ内で別のコンテンツを実行

この説明だと「おまえは何を言っているんだ」みたいな感じですが、 通常の Akashic コンテンツとの違い を見てみるともうちょっとわかりやすいです。

Akashic Engine のマルチプレイは「全プレイヤーの操作を全プレイヤーに同期する」みたいな理屈で動いていますが、この @akashic-extension/coe を使うと、プレイヤーの操作が一旦サーバーに送られ、サーバーでゴニョゴニョしてから全プレイヤーに配信したりしなかったりができるようです。

なので、例えばプレイヤーがクイズの回答を送信し、サーバーで採点してからみんなに「Ruたんさんは正解で1234点でした」みたいな情報を配信する……みたいなことができる感じ。

代わりに操作の自動同期が一切行われなくなるので、そのあたりも全部自分で管理する必要が出てきます。普通に大変。

なので、ゲームというよりはツールとかに向いてる気がしますね。ゲームの場合だと、公式の「みんなでつりっくま」みたいな個人戦のやつはいいのかも。

内部的には Game#addEventFilter を使って制御しているみたいです。こんなことできるんだ~、みたいのがわかって普通に面白かったです。 @akashic-extension/coe 自体のコード量はかなり少なくて AkashicEngine 探検隊みたいになった。

g.game.onJoin in ニコ生

これ、まじで悩んだのでブログに書いとく。

ニコ生ゲームにおいて、プレイヤーが join しているかどうかを見ることで配信者なのかどうかを判定することができます。join イベントが発火すると g.game.onJoin が呼ばれるのでそこで拾うか、g.game.joinedPlayerIds を見ることで判定することができます。

が、 @akashic-extension/coe を使っているとき……もとい、 new g.Scene({ tickGenerationMode: 'manual' }) をしているときは注意が必要です。

おそらくなのですが、ローカル動作確認用の akashic serve とニコ生では join イベントが発生するタイミングが若干違います。多分ニコ生のほうがタイミングが遅いです。

遅いと言っても、別に何秒も遅れてくるわけではなく本当に一瞬違うくらいなので通常ゲームは概ね問題なさそうです。一方で @akashic-extension/coe を使うとマルチプレイゲーム内の「時間の流れ」という概念が無くなるので、onJoin を待っていても一生来ません。配信者判定をする処理を一番最初に入れていたので、ニコ生上だと何も動かなかった……。

なので @akashic-extension/coe を使う場合は、ゲーム開始後にサーバー( Controller )から何かしらのデータを broadcast して世界の流れを自分で進めてあげる必要があります。こうすると、そのタイミングで onJoin も一緒に流れてくるので配信者判定ができます。

// イメージ
class MyController extends COEController {
  constructor(param) {
    super();

    this.setTimeout(() => {
      // 開始したあとにテキトーなデータを broadcast しておく
      // こうすることで、このタイミングで `Game#onJoin` も一緒に発火する
      this.broadcast({type: 'ping'});
    }, 500);
  }
}

ニコ生でしか起きないから調べるのめっちゃ大変だった……10個くらいゴミみたいな確認サンプルをアップロードしてしまった。

自作クイズ

自作クイズの読み込みどうなってんねん!というのも書いておきます。

あれの正体は単純に <input type="file"> です

const sprite = new g.Sprite({ /* 略 */ });
sprite.touchable = true;
sprite.onPointDown(() => {
  try {
    const input = document.createElement('input');
    input.type = 'file';
    input.accept = '.json';
    input.click(); // ファイルダイアログを開くよ
  } catch (e) {
    // アプリとかは動かない可能性高いので catch して握りつぶす
  }
});

良きタイミングで <input type="file"> の DOM を作って、それを開かせています。なのでスマホアプリからの配信とかでは動かないです。Webページじゃないしね。

選ばれたJSONファイルを読み取って、それを zod でチェックして使う……みたいなことをしています。

設問文の表示

例えば以下の図。どちらが読みやすいでしょうか。

上の表示だと読みづらいですよね。設問文が1行で収まらないときの改行位置をどこにするのか、というのはそこそこ重要です。

すべてのクイズを僕が管理するのであれば、僕が1問ずつ目視で確認して、いい感じの位置に改行をいれるという方法も考えられるかもしれませんが、本作は自作クイズの仕組みがある以上、そういうわけにはいきません。このあたりは作問者ではなくゲーム自体が仕組みとして何かしらの手段を取る必要があります

今回は Google の budoux を使いました。

budoux を使うことで日本語などの文章の改行しても良さそうな部分を推定してくれます。いわゆる「今すぐダウンロー(改行)ド」みたいのを防ぐことができます。

import { Parser, jaModel } from 'budoux';

function parseChunkFromText(text: string) {
  const parser = new Parser(jaModel);
  const chunks =
    parser.parse(text)
      // 英文などスペースを途中に含む場合はスペースで分割する
      .flatMap((chunk) => chunk.split(/( +)/).filter((part) => part !== ''));

  return chunks;
}

parseChunkFromText('Cygamesが運営する、実在の競走馬をモチーフとしたスマートフォン向けゲームは?');
// => [
//  "Cygamesが",
//  "運営する、",
//  "実在の",
//  "競走馬を",
//  "モチーフとした",
//  "スマートフォン向けゲームは?"
// ]

こんな感じで文章を塊ごとに分割できるので、この区切りの部分で改行するように設置してあげています。

もちろん、この仕組みだけだと設問文の最後の「何?」みたいな部分だけ2行目になっちゃうなどの難点はまだありますが、まぁまぁ自動で行われる処理としてはいいところなんじゃないでしょうか…!

おわり

ニコ生早押しクイズセレクション、よろしくおねがいします!

クイズ作るのちょっと大変すぎるのでなかなかハードルあると思いますが、きっと楽しいと思うのでぜひチャレンジしてみてください。が、大量に問題作るのは本当に大変なので、まずは1回プレイできる7問でいいんじゃないかな。

ところで……

実は本作、だいたい3年前に作ってたゲームだったりします。

作るのに3年かかったわけではなく、ほぼ完成してたのに3年放置した、という意味です。なんで。
(時期的にはパズトリの後、ハインダッシュの前)

クイズシーンがほぼ作り上がって、あとはエントリー画面とか結果画面を作れば完成だな!というところで力尽きて放置しちゃってました……それを今年の1月11日に掘り起こしてゴニョゴニョしたり、ロゴ作ったりして1月17日に公開しました。つまりあと一週間頑張れば完成したものを3年も放置してたってことですね!マジでなんで!!!!!111111

いや、本当にゲームを「完成」させるのって難しいんですって……

たかが一週間、されどその一週間の作業を乗り切るためのモチベーション、パワー、そしてヒラメキ、これを揃えられなくてエターなっていくゲームが多数あると言われています[要出典]

でも、本作みたいに3年後に突然覚醒して何とかなることもあるので、みんな奇跡を信じてゲーム作っていこうな! 奇跡駆動開発!!