マルチプレイゲームを最小最速で作ってニコ生上で動かす


# はじめに

この記事は [ドワンゴ Advent Calender 2019](https://qiita.com/advent-calendar/2019/dwango)の記事です。

ニコ生では現在、ニコ生上で遊べるゲームについて、[RPGアツマールを経由したユーザー投稿](https://akashic-games.github.io/guide/shin-ichiba/export-atsumaru.html)を受け付けており、自分が作ったゲームをニコ生上で動作させることができます。
さらっと言いましたが、今のニコ生は**なんか自分の書いたスクリプトが動かせる**んです。
単純な押しボタンのようなものから、配信者と視聴者でバトルするマルチプレイゲームまで、**なんか割と作れます。**

そこで今回はニコ生ゲーム普及の一環として、表題の通り**すごく簡単なマルチプレイゲーム**を最小最速で作り、Akashic Engineを使うといかに**同期処理が楽であるか**を示したいと思います。


# 前提知識
ニコ生上でゲームを動作させるには、現状では弊社で開発しているjs向けフレームワーク、**Akashic Engine**を利用する必要があります。
https://akashic-games.github.io/

# 資料
マルチプレイゲームを作る方法について、過去に連載形式で数ヶ月かけて紹介していました。これを読むと作れるようになります。ほんとです。書いた本人がいうので間違いありません。
作りたくなった場合には以下の記事が参考になります。兎角亀毛作ってドンドン投稿してください。

- 第一回: [マルチプレイゲームを知ろう](https://github.com/akashic-contents/shin-ichiba-doc/wiki/%E3%83%9E%E3%83%AB%E3%83%81%E3%83%97%E3%83%AC%E3%82%A4%E3%82%B2%E3%83%BC%E3%83%A0%E3%82%92%E4%BD%9C%E3%82%8D%E3%81%861-%E3%83%9E%E3%83%AB%E3%83%81%E3%83%97%E3%83%AC%E3%82%A4%E3%82%B2%E3%83%BC%E3%83%A0%E3%82%92%E7%9F%A5%E3%82%8D%E3%81%86)
- 第二回: [簡単なゲームを作ってローカル処理に触れよう](https://github.com/akashic-contents/shin-ichiba-doc/wiki/%E3%83%9E%E3%83%AB%E3%83%81%E3%83%97%E3%83%AC%E3%82%A4%E3%82%B2%E3%83%BC%E3%83%A0%E3%82%92%E4%BD%9C%E3%82%8D%E3%81%862-%E7%B0%A1%E5%8D%98%E3%81%AA%E3%82%B2%E3%83%BC%E3%83%A0%E3%82%92%E4%BD%9C%E3%81%A3%E3%81%A6%E3%83%AD%E3%83%BC%E3%82%AB%E3%83%AB%E5%87%A6%E7%90%86%E3%81%AB%E8%A7%A6%E3%82%8C%E3%82%88%E3%81%86)
- 第三回: [JoinとLeaveとニコ生の話](https://github.com/akashic-contents/shin-ichiba-doc/wiki/%E3%83%9E%E3%83%AB%E3%83%81%E3%83%97%E3%83%AC%E3%82%A4%E3%82%B2%E3%83%BC%E3%83%A0%E3%82%92%E4%BD%9C%E3%82%8D%E3%81%863-Join%E3%81%A8Leave%E3%81%A8%E3%83%8B%E3%82%B3%E7%94%9F%E3%81%AE%E8%A9%B1)
- 第四回: [実際にゲームを考えて作ろう](https://github.com/akashic-contents/shin-ichiba-doc/wiki/%E3%83%9E%E3%83%AB%E3%83%81%E3%83%97%E3%83%AC%E3%82%A4%E3%82%B2%E3%83%BC%E3%83%A0%E3%82%92%E4%BD%9C%E3%82%8D%E3%81%864-%E5%AE%9F%E9%9A%9B%E3%81%AB%E3%82%B2%E3%83%BC%E3%83%A0%E3%82%92%E8%80%83%E3%81%88%E3%81%A6%E4%BD%9C%E3%82%8D%E3%81%86)

# 作成開始
ただいまの時刻は20:52です。なるべく早くできるように頑張ります。

## ゲームを考える
考えるところから入ります。今回作るゲームはマルチプレイゲーム、**つまり複数人参加型のゲーム**となっています。
この辺の定義については、前述の資料第一回にて少し考えました。

直近ニコ生で話題になったマルチプレイゲームとして、全員協力型ゲーム、塔伸ばしこと[ニコニコタワー](https://ch.nicovideo.jp/shin-ichiba/blomaga/ar1773346)がありましたが、**個人的にはもっとサツバツとしてほしい**ため

- 全員参加型
- 全員でバトル
- 最終的に順位が定まる

という要素を盛り込みます。といっても**最小最速**を自負する以上、ゲーム性はお察しください。

### 考えたゲーム
- 画面に表示されるボタンを最初に押した人だけにスコアが入る
- これをゲーム終了まで繰り返し、スコアが一番高い人が勝ち

簡単ですね。スコアの管理、ボタンのイベントハンドリング、画像の表示、同期が実装できればいけそうです。

## 素材を作る
お絵かきツールで画像を書きます。急ぎます。マウスがないので丸を書くのが難しいです。


## 開発環境やプロジェクトを整える
### 必要ライブラリのインストール
[公式の入門記事](https://akashic-games.github.io/tutorial/v2/1-introduction.html)に則り、必要なコマンド群をnpm iしていきます。

```
$ npm install -g @akashic/akashic-cli
$ npm install -g @akashic/akashic-sandbox
```
akashic-sandboxは主に動作確認に使います。

### プロジェクトを作る
適当なディレクトリにてakashic initコマンドを実行するとテンプレプロジェクトが作られて捗ります。TypeScriptで書きたいため以下のコマンドを叩きます。

```shell
$ akashic init -t typescript 
# 中略
prompt: width:  (320) 640
prompt: height:  (320) 360
prompt: fps:  (30) 
Now your new Akashic game has been generated. To run with akashic-sandbox:
  $ npm install
  $ npm run build
  $ npm start
See README.md (in Japanese) for more detail and make your Akashic game!
INFO: Done!

```
プロジェクトについていくつか質問をされます。ニコ生上でゲームを動作させる場合、**16:9の比率**でゲームを作ることを強く推奨します。別に無視してもいいのですがいい感じに引き伸ばされて16:9に収まるようにして実行されます。

空プロジェクトができたらその中でnpm i すれば、必要なものは一通り入ってきます。利用しないサンプル素材が同梱されているので以下のコマンドで消しておいてください

```shell
$ rm -rf audio/*
$ rm -rf image/*
```

## 画面にボタンを表示してイベントハンドラを書く
### 画像読み込みの準備
まずはここからです。ハローワールド代わりに画像を出して押しましょう。画像を出すために、プロジェクトのimageフォルダに描いた画像を叩き込んでおきます。
その後、scanコマンドを叩いて画像の情報をgame.jsonに取り込んでおく必要があります。

```shell
$ akashic scan asset                                                                                                                                                                                                                                                                                          
INFO: Removed the declaration for 'se' (audio/se). The correspondig files to the path are not found.
INFO: Removed the declaration for 'player' (image/player.png). The correspondig files to the path are not found.
INFO: Removed the declaration for 'shot' (image/shot.png). The correspondig files to the path are not found.
INFO: Added/updated the declaration for button (image/button.png)
INFO: Added/updated the declaration for button2 (image/button2.png)
INFO: Done!
```

さっき消した素材情報が消えて、代わりに叩き込んだ画像が追加されました。Akashic Engineの都合として、読み込む**画像の縦横比**や**音声ファイルの長さ**などを事前に知っておく必要があるため、この作業は素材が変更されるたびに必要です。

### シーンの初期化と記述
Akashic Engineでは「シーン」と呼ばれる纏まりに対して素材やイベントなどを紐づけて扱います。余談ですが、enchant.jsやphaser.jsのようなメジャーっぽいゲームエンジンでも似たような感じです。

シーンで使う素材類は、シーンをnewした時にロードが開始され、loadedイベント発火で利用できるようになるので、以下のようなコードが画像を出すシーンの最小構成になります。


```ts
    scene.loaded.add(() => {
        const darkButton = new g.Sprite({
            scene: scene,
            src: scene.assets["button"],
            touchable: true
        });
        scene.append(darkButton);
        darkButton.pointDown.add((e) => {
            // 誰かによってボタンが押されるとここが実行される
        });
    });
```
これをものすごく伸ばしていくことでゲームを作っていきます。ひとまずボタンの処理と、マルチプレイ時の同期処理だけ書いてみましょう。先ほどのsceneのloadedハンドラに追記して行きます。

```ts
    scene.loaded.add(() => {
        const font = new g.DynamicFont({game: g.game, fontFamily: g.FontFamily.Serif, fontColor: "black", size: 32});
        // スコア表示TOP5用のラベル
        const rankingScoreLabels = initializeRankingLabels(scene, font);

        const darkButton = new g.Sprite({
            scene: scene,
            src: scene.assets["button"],
            touchable: true
        });
        scene.append(darkButton);

        darkButton.pointDown.add((e) => {
            // 押した人を取得
            const winnerId = e.player.id.slice(-5);
            // scoreを加算する
            if (scoreTable[winnerId] == null) {
                scoreTable[winnerId] = 1;
            } else {
                scoreTable[winnerId] += 1;
            }

            // ラベル更新
            updateRankingLabels(scoreTable, rankingScoreLabels);
        });

    });
    g.game.pushScene(scene);
```

こんな感じになりました。マルチプレイゲームの動作確認は、buildからのserveです。

```shell
$ npm run build
$ akashic serve
```

デフォルトではlocalhost:3300でサーバーが立ち上がるので、ブラウザでアクセスしてみてください。複数窓ひらけば複数人プレイしているのと同じ状況を作れます。

![スクリーンショット 2019-12-11 23.19.59.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/40020/517e3313-98c7-5847-6c95-593495592a2e.png)

このような感じで、左側のボタンを押すと自分のスコアが加算され、上位５名が表示される、という挙動が実現できました。
このあたりでおや？　と思った方がいるかもしれませんが、sceneのハンドラ内では**特に何も書いていないのに複数の窓で勝手に状態が同期している**ことがわかると思います。

これがAkashic Engineでマルチプレイゲームを作る際の最大の特徴で、**何も考えずに作るとまるっと同期されます。**
何も考えずに書いていくと複数人で触れるボタンが出来上がるわけです。
もう少し書くと、誰かがブラウザでボタンを押すと、その時のpointDownイベントが**押した人も含めて**同じタイミングで実行されます。
メインループのタイミングとイベントと**乱数のシード**がAkashic Engineによって勝手に同期することにより、全員の環境で同じ挙動が再現される = 同期される、というワケです。

# ニコ生に登録する準備をする
完成品はこちらです。
https://github.com/orzngo/minimum-multiplay-game

ゲームとしての出来はひどい（ほんとはもうちょっと作りたかったけど時間がない。。。）ですが、何はともあれマルチプレイゲームと呼べるべきものは出来たため、ニコ生で動かしましょう。公式ドキュメントを参考にgame.jsonを編集します。

https://akashic-games.github.io/guide/shin-ichiba/index.html

具体的にはenviromentを以下のようにしておきます。

```json
	"environment": {
		"sandbox-runtime": "2",
		"niconico": {
			"supportedModes":["multi"]
		}
	}
```

出来たらRPGアツマール投稿用のzipを出力します。

```shell
$ akashic export html --output button.zip --atsumaru
```

出来上がったゲームをRPGアツマールに投稿します。このあたりでニコニコのアカウントが必要になるため、持っていない方は[今すぐ登録](https://account.nicovideo.jp/register?sec=dwango_advent_calender_2019)しましょう！


<img width="1053" alt="スクリーンショット 2019-12-11 23.42.21.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/40020/fb4d9437-980d-1f65-59ee-664a25e3fdcd.png">

なんかこんな感じになります。アツマール上でも遊べるようにゲームを作ればいいのですが、マルチプレイゲームを作ると多くの場合**アツマールでまともに動かない**ため、限定公開した方がみんな幸せになると思います。

# ニコ生に登録してニコ生上で動かす！

で、この状態で右下のその他メニューから、市場に登録申請すれば完了です。まもなくニコニコ新市場で利用できるようになるでしょう。

<img width="389" alt="スクリーンショット 2019-12-11 23.47.12.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/40020/96be59c3-7386-936d-b97e-99abcbd36756.png">

こんな感じで、ニコ生の右側に表示される、ニコニコ新市場**自作ゲームタブ**に登録されました。だいたい1分以内に登録されると思います。

これでニコ生上で動くようになりました。一般公開されているため、どの番組でもゲームが動作可能です。利用するか否かを最終決定できるのは**配信者**なので、ダイレクトマーケティングしまくりましょう！


# 終わりに
なんとか時間いっぱい、ニコニコ新市場の登録まで終わりました。
本当は音を鳴らしたりする予定だったのですが、予想以上に時間がかかってしまい、マジの最小構成になってしまいました。終了条件もないため、ゲームとしての価値はほぼ無ですね……

とはいえ僕が一番言いたかった、**Akashic Engineでなんも考えずに作ると全部他人と同期する**、ということについては示せた気がしています。
将棋みたいな全情報公開ゲームはこれだけで作れますが、放送者と視聴者でロールを分けて対戦する、みたいなゲームはこの記事だけでは難しいかもしれません。資料として貼った連載記事を読んでいただければと思います（ダイマ）

[PR]
企画として自作ゲームコンテストというものをやっています。
https://ch.nicovideo.jp/shin-ichiba/blomaga/ar1768903

マルチプレイゲームを作っている人はまだ少なく、入賞できる可能性が高いです。作ろう！