【初投稿テスト】非同期ネットゲーム作ってみた

# 非同期ネットゲーム仕様書

## はじめに

本書は、RPGアツマールで開催される非同期ネットゲームコンテスト（2019年3月3日(日)23:59〆切）に向けて作成するゲームの仕様を書いた物である。

- マークダウン記法で記述するテキストデータである。
- UML部分はPlantUMLを使用する。

## マークダウン記法

本書で使用するマークダウン記法について、使い方と書き方を以下に記述する。

### 使い方

- PlantUMLというVSCodeの拡張機能と、描画用にGraphviz（要Java）を使う。
- VSCodeで、ctrl+k の後に v を押してプレビュー表示できる。
もしくは右上の虫眼鏡アイコンをクリックする。
- Wikipedia「Markdown」  
<https://ja.wikipedia.org/wiki/Markdown>
- 参考にした記事  
<https://www.eureka-moments-blog.com/entry/2018/12/17/121617>

### 書き方

マークダウン記法では、文章内の改行は無視される。
画面の右端で自動改行される。
明示的に改行させたい時は行末に空白2つを付ける。  
このようになります。  
ですよ。

- ハイフン＋スペース「- 」を行頭に付けると、箇条書きになる。
- 行頭に「●」が付く。
  - 行頭にスペース2個を付けると、インデントも可能。
- 本書では要件や仕様に関する記述は、基本的に箇条書きで記述している。

1. 連番による箇条書きは、番号＋スペースを行頭に付ける。
    1. 番号は「1. 」だけでも自動採番される
    1. だから、「1. 」の方が変更に強い
1. 改行が必要な時は、このように  
行末にスペース2個を付ける。
1. 番号の場合はスペース4個でインデントする。
1. 本書では処理の順序を表したい場合に使用する。

【注意】  
箇条書きや引用などの文章の塊がある場合、その塊の種類が変わる所で空白行を入れる。

> 「> 」は引用です。  
引用する時に開始行の先頭に使います。
このように表示されます。

表は以下のように記述する。

```text
|A|ヘッダ|てすと|項目名|
|---|:---:|---|---|
|AAA|B|テスト|表の内容|
|Aの内容2|センタリング|C|表の内容2|
|Aの内容03|表はこのように書く|Test|D|
```

この記述により、以下のように表示される。

|A|ヘッダ|てすと|項目名|
|---|:---:|---|---|
|AAA|B|テスト|表の内容|
|Aの内容2|センタリング|C|表の内容2|
|Aの内容03|表はこのように書く|Test|D|

```text
バッククォート3個「```」を開始行、終了行に書いて囲った
  この範囲は
自由に空白や改行できる。

開始側のバッククォート3個「```」の後に、テキストデータの形式を記述する。
この例では「```text」としているが、
「```javascript」「```plantuml」と書くと、その言語として表示される。

そのため
  ソースコードを
    書くのに最適。
```

本書で使いそうなマークダウン記法の書き方は以上。  
なお、下の横線は「***」と記述する。

***

## ゲーム情報 (概要)

### ゲームの目的

- 花を育てる。
  - 当初予定では、「いちご狩り」をするゲームであったが、いちごの素材がないため断念した。
- 花をプレゼントして好感度を上げる。
- キャラクターの人気投票をする。

### ゲームタイトル

- 『花摘み【パケキャラ総選挙】』を仮タイトルとする。
  - 「花摘み」は品がないがインパクトはあると思われるので、仮タイトルとする。
- 最終的に、タイトルは『PKG16 総選挙』とした。

## ゲームの流れ (基本設計)

### 概要

- 放置系ゲームの要領で花を育て、花をパケキャラにプレゼントし、プレゼントの量でパケキャラの人気を競う。
- 「パケキャラ」とは、RPGツクールMVの「パッケージキャラクター」の事である。本ゲームではNPCである。
- 他人が育てた花を取る事が出来る。
- パケキャラごとに好感度ランキングを用意する。

### 花を育てる

放置系ゲーム要素は以下の流れとする。

1. 種を植える（プレイヤー操作）。
1. 種が草に育つ（放置）。
1. 草が花に育つ（放置）。
1. 花が実になる（放置）。
1. 実が種になる（放置）。
1. 種が草に育つ（放置）。繰り返し。

- 花壇は全プレイヤー共通の領域とする。
- 花壇の数はプレイヤーの数に応じて増やす。
  - 自動でマップの拡張できる？  
  しかし、1プレイヤーに1マップなどにするとマップ数が増えると困るので、自動で増やすのはやめた方が良さそう。
  - 花壇の数は何らかのタイミングで作者が手動で増やす。（増やせるように作る事）
- 花の成長状況を花の上に表示させたい。「あと何秒で次の状態になるか」を表示する。

### 花を摘む

- プレイヤーは、「花」または「実」を採取する事が出来る。
- 「花」と「実」以外は採取できない。
- 花壇は共通領域なので、他のプレイヤーが育てた「花」や「実」を採取する事が出来る。
- 花を摘んだ花壇は「空き」になる。プレイヤーが種を植えるまで「空き」になる。

### 花をプレゼントする。

- プレイヤーはパケキャラに「花」または「実」をプレゼントする事が出来る。
- プレイヤーがプレゼントする事で、「好感度」と「種」と「所持金」を得る事が出来る
（このゲームに公職選挙法のようなものは無い）。
- プレイヤーがプレゼントする事で、パケキャラの人気ポイントが上昇する。

### 種の入手

- プレイヤーはゲーム開始時に「種」をいくつか持っている。
- プレイヤーがパケキャラにプレゼントした時に、「種」と「所持金」をもらえる。
  - 「実」をプレゼントした時に、その「種」をもらえると面白いかも。
  出所がはっきりしているので。
- プレイヤーは所持金を使用して「種」を買う事が出来る。

### プレイヤーのデータ

- アイテム（植える前の「種」、採取した「花」または「実」）
  - 「種」と「花」＆「実」でアイテム種別を分ける。
- プレイヤーの実績を表す何か。
  - 実績アイテム（大事なもの）
- 経験値（レベル）
- 所持金
- 好感度（パケキャラそれぞれなので、16人分）  
  - ステータス画面を使うのと、アイテムの個数で表現するの、どっちが楽？
  - 変数を16個用意して、ステータス画面でその変数を表示する？

### アツマールAPIを使う場面

- 花壇はプレイヤー共有なので、何らかのAPIを使用する。
  - 使うAPIは、グローバルシグナルになりそう。
  - 花の成長はプレイヤーごとに行い、採取した時にシグナルを送る。
  - 採取シグナルを受け取ると、その花壇は「空き」になる。
  - タイミングによっては、同じ花壇で複数プレイヤーが同時に採取できるが、許容する。
  - APIの通信は『5秒に1回よりも緩やかにすること』との事なので、採取シグナル送受信のタイミングで、何らかの演出を行い、時間を稼ぐ。
- ランキング機能を使う。
  - パケキャラごとの好感度をランキングにする。
  - パケキャラ総選挙の順位はランキング機能を使わない。プレイヤーのランキングではないので。
- 総選挙の順位に、グローバルサーバー変数を使用する。
  - 1キャラにつき1変数を使用すると、16個のグローバルサーバー変数が必要になる。
  - グローバルサーバー変数では、トリガーを使用して値の変更を行う。

### パケキャラ

- RPGツクールMVのパッケージキャラ16体を使う。
- マップに表示する。
- プレゼントを渡す事が出来る。プレゼントは総選挙の票（人気ポイント）を兼ねている。
  - プレゼントは「花」または「実」である。
  - アイテム選択の処理でプレゼントを渡す。
- 好きな「花」「実」があり、それによってプレゼント時の好感度に差がある。
- 好きな「花」「実」の説明と、プレゼントを渡す場所は分けておく。
  - 好きな「花」「実」の説明はすぐに見れる場所に置く。
  - プレゼントを渡す場所は、パケキャラごとにマップを用意する。
  - プレゼントを渡してお礼を受け取ると、ランキングAPIの通信を行う。そのため、（プレゼントは1アイテムずつ選択するが）プレゼントに対してのお礼はまとめて受け取るのが良さそう。そのマップを出る時でもイイかも。

### メモ

- メッセージWindowに歩行キャラを表示させたい。名前だけでパケキャラを識別するのが難しいと思われるため。
  - アイコン化（32×32）するか、プラグインを用意するか。
  - IconSet.pngを下に拡張できるので、アイコン化するのが良さそう。

***

## 詳細設計

- 詳細設計の代わりに、JavaScriptのソースコードを記述する。

### Promise全般

```javascript
// promise() を使いたい場合
// thenはPromise受信時に実行される
// catchはエラー発生時に実行される
// ドット「.」区切りで連続して記述する
// 1行で書くとこのようになっている
promise().then(function(a){ 非同期処理 }).catch(function(b){ エラー処理 });
```

### グローバルシグナル送信

```javascript
window.RPGAtsumaru.experimental.signal.sendSignalToGlobal(data)
// catchはエラー発生時に実行される
// 引数errを受け取る
.catch(function(err){
    switch(err.code) {
    case "UNAUTHORIZED":
        // プレイヤーがログインしていない
        console.log('send error : UNAUTHORIZED');
        break;
    case "BAD_REQUEST":
        // ゲーム側で何か間違えているとき＝指定したボードIDが大きすぎるかマイナスの場合などに発生
        console.log('send error : BAD_REQUEST');
        break;
    case "INTERNAL_SERVER_ERROR":
        // サーバー側で何らかの問題＝通信不良やメンテ等で発生
        console.log('send error : INTERNAL_SERVER_ERROR');
        break;
    case "API_CALL_LIMIT_EXCEEDED":
        // 短時間にゲームAPIを利用しすぎて、一時的に利用を制限されている
        console.log('send error : API_CALL_LIMIT_EXCEEDED');
        break;
    }
});
```

### グローバルシグナル受信

```javascript
window.RPGAtsumaru.experimental.signal.getGlobalSignals()
// thenはPromise受信時に実行される
// Promiseの内容を引数vとして受け取る
// 非同期処理なので、いつ受信するかは分からない事に注意！
.then(function(v){
    KRD_checkSignal(v); // 別関数で処理する
})
// catchはエラー発生時に実行される
// 引数errを受け取る
.catch(function(err){
    switch(err.code) {
    case "INTERNAL_SERVER_ERROR":
        // サーバー側で何らかの問題＝通信不良やメンテ等で発生
        console.log('get error : INTERNAL_SERVER_ERROR');
        break;
    case "API_CALL_LIMIT_EXCEEDED":
        // 短時間にゲームAPIを利用しすぎて、一時的に利用を制限されている
        console.log('get error : API_CALL_LIMIT_EXCEEDED');
        break;
    }
});
```

***

## データフロー

![plantuml_2019-03-07T22-01-19.png](https://qiita-image-store.s3.amazonaws.com/0/373498/e234fc97-a518-8bbb-a681-3097e5288564.png)

```plantuml
@startuml
title 畑の処理 (収穫する)
actor プレイヤー
participant 畑
control 時間
control 受信ループ
actor NPC #RED
database シグナル
database サーバ変数
database ランキング
control 他の受信ループ #GREEN
actor 他プレイヤーの畑 #GREEN
受信ループ -> シグナル: チェック
受信ループ <-- シグナル: 変更なし
他の受信ループ -> シグナル: チェック
他の受信ループ <-- シグナル: 変更なし
プレイヤー -> 畑: 種植え
activate プレイヤー
activate 畑
畑 -> シグナル: 植えた
受信ループ -> シグナル: チェック
畑 <- シグナル: 植えた
他の受信ループ -> シグナル: チェック
シグナル -> 他プレイヤーの畑: 植えた
時間 -> 畑: 経過
畑 -> 畑: 成長
受信ループ -> シグナル: チェック
受信ループ <-- シグナル: 変更なし
他の受信ループ -> シグナル: チェック
他の受信ループ <-- シグナル: 変更なし
時間 -> 畑: 経過
畑 -> 畑: 成長
プレイヤー <- 畑: 収穫
deactivate プレイヤー
畑 -> シグナル: 収穫した
受信ループ -> シグナル: チェック
畑 <- シグナル: 収穫した
deactivate 畑
他の受信ループ -> シグナル: チェック
シグナル -> 他プレイヤーの畑: 収穫した
@enduml
```

![plantuml_2019-03-07T22-43-33.png](https://qiita-image-store.s3.amazonaws.com/0/373498/f23b3175-92f9-ab8b-e347-be643e43ed0b.png)

```plantuml
@startuml
title 畑処理 (収穫なし)
actor プレイヤー
participant 畑
control 時間
control 受信ループ
actor NPC #RED
database シグナル
database サーバ変数
database ランキング
control 他の受信ループ #GREEN
actor 他プレイヤーの畑 #GREEN
受信ループ -> シグナル: チェック
受信ループ <-- シグナル: 変更なし
他の受信ループ -> シグナル: チェック
他の受信ループ <-- シグナル: 変更なし
プレイヤー -> 畑: 種植え
activate プレイヤー
activate 畑
畑 -> シグナル: 植えた
受信ループ -> シグナル: チェック
畑 <- シグナル: 植えた
他の受信ループ -> シグナル: チェック
シグナル -> 他プレイヤーの畑: 植えた
時間 -> 畑: 経過
畑 -> 畑: 成長
受信ループ -> シグナル: チェック
受信ループ <-- シグナル: 変更なし
他の受信ループ -> シグナル: チェック
他の受信ループ <-- シグナル: 変更なし
時間 -> 畑: 経過
畑 -> 畑: 成長
時間 -> 畑: 経過
畑 -> 畑: 種に戻る
deactivate プレイヤー
受信ループ -> シグナル: チェック
deactivate 畑
受信ループ <-- シグナル: 変更なし
他の受信ループ -> シグナル: チェック
他の受信ループ <-- シグナル: 変更なし
@enduml
```

![plantuml_2019-03-07T22-43-54.png](https://qiita-image-store.s3.amazonaws.com/0/373498/cd3b2af4-9f66-83c1-5979-a7021d040396.png)

```plantuml
@startuml
title 投票処理
actor プレイヤー
participant 畑
control 時間
control 受信ループ
actor NPC #RED
database シグナル
database サーバ変数
database ランキング
control 他の受信ループ #GREEN
actor 他プレイヤーの畑 #GREEN
プレイヤー -> NPC: プレゼント(投票)
activate プレイヤー
プレイヤー <- NPC: お礼
プレイヤー -> サーバ変数:登録(投票)
プレイヤー -> ランキング:登録(好感度)
deactivate プレイヤー
@enduml
```

![plantuml_2019-03-07T22-44-13.png](https://qiita-image-store.s3.amazonaws.com/0/373498/d9310767-023b-4ab7-59d8-225284dae23b.png)

```plantuml
@startuml
title 集計処理 (投票結果)
actor プレイヤー
participant 畑
control 時間
control 受信ループ
actor NPC #RED
database シグナル
database サーバ変数
database ランキング
control 他の受信ループ #GREEN
actor 他プレイヤーの畑 #GREEN
プレイヤー -> サーバ変数:投票結果取得(要求)1キャラ目
プレイヤー <- サーバ変数:投票結果取得(応答)1キャラ目
プレイヤー -> サーバ変数:投票結果取得(要求)2キャラ目
プレイヤー <- サーバ変数:投票結果取得(応答)2キャラ目
プレイヤー -> サーバ変数:投票結果取得(要求)3キャラ目
プレイヤー <- サーバ変数:投票結果取得(応答)3キャラ目
プレイヤー -> サーバ変数:投票結果取得(要求)4キャラ目
プレイヤー <- サーバ変数:投票結果取得(応答)4キャラ目(以下16キャラまで)
@enduml
```

![plantuml_2019-03-07T22-44-26.png](https://qiita-image-store.s3.amazonaws.com/0/373498/1f0c1fb8-3dae-6753-b160-55dabf30bad2.png)

```plantuml
@startuml
title 集計処理 (好感度)
actor プレイヤー
participant 畑
control 時間
control 受信ループ
actor NPC #RED
database シグナル
database サーバ変数
database ランキング
control 他の受信ループ #GREEN
actor 他プレイヤーの畑 #GREEN
プレイヤー -> ランキング:好感度取得(要求)1キャラ分
プレイヤー <- ランキング:好感度取得(応答)1キャラ分
@enduml
```