Google Spreadsheet でゲームブックを作る

追記：[ソースコードを公開しました](https://github.com/misawa2048/Gamebook_GGJ/tree/master/gamebook)

# はじめに
2023/02/01 現在、[Global Game Jam 2023](https://globalgamejam.org/)真っ最中ですが、
チームメンバーにプログラマーがいない、とか、
自分は絵or音楽をやりたいけど、他の人が担当しているから雑用しか空いていない、とか、
そんな時はゲームブックでも作ってみるのはどうでしょう？

[サンプル](https://elix.jp/test/gamebook/?apikey=AKfycbwrT31aa89MyK6-4VWei_PYufr-FbZd-1c-AZK1E5F3OEYQPm9vgoZ6Uxdl5_ISa54x5g)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/59911/6c095e7e-5c9a-aae9-f3fd-46147b7cbee6.png)

ベースのシステムは[あります](https://github.com/misawa2048/Gamebook_GGJ/tree/master/gamebook)ので、
あとはGoogleSpreadSheetに文章やURLを入力していくだけでゲームブックを作ることができます。
Google SpreadSheet は複数人で同時に編集可能なので、
多人数でめいめいのパートやシナリオ分岐を作っていく、なんてこともできるんじゃないかと思います。

**誰かが作ってくれたスプレッドシートを共有して使うだけの場合は**
**[ゲームブックの内容をページ単位で記述する](#ゲームブックの内容をページ単位で記述する)まで読み飛ばしてもらって大丈夫です。**

それではGoogleSpreadSheet作成方法です。
GoogleSpreadSheetが使用できる環境であることを前提としています。

# 空のシートを作成する
まずは空のシートを作成します。デフォルトシート名は```masterSheet```としておきます。
別の名前でも構いませんが、その場合はurlのパラメータに渡す必要があります。

# 最初の行を記入する
1行目は
| label | text | buttons | image | se | params |
|:-:|:-:|:-:|:-:|:-:|:-:|

としておきます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/59911/44e4b63e-eb31-4117-79f4-8eff190bfc51.png)

# スクリプトを用意する
##### 拡張機能>AppsScriptを選択します
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/59911/ef7a0a7e-d70a-d247-6761-279e61e5b9f3.png)

##### コードを書き換える
最初から用意されているコードを削除し、下記をコピペします

```
const DEF_SHEET  = "masterSheet";
function doGet(e) {
  const sName = (typeof e === "undefined" || typeof e.parameter.sheet === "undefined")?DEF_SHEET:e.parameter.sheet;
  const sheet = SpreadsheetApp.getActive().getSheetByName(sName);
  let resultDat= read(sheet);
  const json = JSON.stringify({"items": resultDat,"sheet":sName}, null, 2);// Read
  return ContentService.createTextOutput(json).setMimeType(ContentService.MimeType.JSON);
}

function read(_sheet){
  const rows = _sheet.getDataRange().getValues();
  const keys = rows.splice(0, 1)[0];
  var resultDat = [];
  for(var i=0;i<rows.length;++i){
    const obj = {};
    rows[i].map((item, index) => {
      obj[String(keys[index])] = String(item);
    });
    resultDat.push(obj);
  }
  return resultDat;
}
```

変更前
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/59911/6ac483bb-b5e9-4d6a-c363-fe59416bb926.png)

変更後
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/59911/91d10a2d-e4b3-6385-773d-ce986370f9c8.png)

##### デプロイする
デプロイ->新しいデプロイを選択します
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/59911/65301457-af74-8afd-8026-28b5bdeb3052.png)

デプロイタイプをウェブアプリおよび実行可能APIにします
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/59911/6ace0828-0c76-64cd-d916-b61723c4309f.png)

アクセスできるユーザーを全員にします。
Googleアカウントを持っているユーザーのみ遊べればよい場合は
```Googleアカウントを持つ全員```にしてもよいかもしれません。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/59911/282f7067-df6c-1933-4338-4343a648af37.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/59911/f65b54fd-808b-6a17-211f-d2de8028816d.png)


アクセスを承認します
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/59911/751b89c0-48bc-4b70-fcfc-b73775b3208a.png)

デプロイ出来たら、デプロイIDをメモっておきます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/59911/f947e919-80cd-f163-5c8b-95a5734e192e.png)

また、ウェブアプリのurlにアクセスすると
```
{
  "items": [
    {
      "label": "",
      "text": "この世界は、誰かが見ている夢なのだ、とある人はいう。\nなるほど、そう言われてみればそんな気がしないでもない。\nそして、その誰かが眠りから醒めたのか、\nあるいは寝返りでもうって再び浅い眠りにでも入ったか、\nあなたが転生したこの世界もまた、誰かの夢の中なのかもしれない。",
      "buttons": "次へ{出発地}",
      "image": "https://storage.googleapis.com/memeplex-d5d45.appspot.com/0_1674657647c6621c00e258e289ad2a9be37a2c5dd530da3987d9b4b426a48ef401fc123db2f49c46e60fc2eda29aeeb6dc87a40961afb85ce9d6be831f6c095ed8c51f582b0.000000.jpg",
      "se": "",
      "params": ""
    },
    {
```
のようにJson文字列が返ってくるかと思います。

# ゲームブックの内容をページ単位で記述する

再びスプレッドシートに戻り、シートを埋めていきます。とりあえず数行だけ埋めて試してみるのが良いでしょう。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/59911/d46512cb-1186-fae6-3ab9-ba74d4d0fd2a.png)

それぞれの項目の説明です

| 項目 | 内容 |
|:-:|:-:|
|label   |ボタンを押したときの飛び先   |
|text   |メッセージ本体   |
|buttons   |メッセージの分岐ボタン   |
|image   |表示される画像のurl   |
|se   |再生される音のurl(.wav)  |
|params   |未使用（拡張用）   |

## ```image```
そのページで表示される画像のurlを記述します。
512x512が推奨サイズです。
AIで作画ができる[MEMEPLEX](https://memeplex.app/)などで作成するのもよいかとおもいます。
また、数十kB程度の小さい画像であれば [スプレッドシート内に埋め込む](https://qiita.com/ELIXIR/items/88dc1c0919de2f5c5135) ことができます。

## ```se```
そのページが表示された時に鳴らす音のurlを記述します。
JavaScriptのAudioオブジェクトを使用して鳴らしているのですが、
使用できるのは.wavのみで.mp3は使用できないようです。
今のところループや音量調整非対応なので、音を編集しないと使いどころが難しいかもしれません。
## ```text```
そのページで表示されるテキスト本体です。
改行したい場合はalt+enter で改行します。
文字列が長くなるとメッセージの文字サイズが変わってしまうようなので(.cssのせい？)、
4,5行程度で分割したほうが良いかもしれません。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/59911/8b25e433-b9ad-db07-7e55-e1a40edadd16.png)
## ```buttons```
分岐するボタンを列挙します。書き方には次の３種類があります。
##### {++}
```その生き物を追って森の奥へと向かった{++}```
とすると、次の行にジャンプします。次のメッセージに続けたいだけの時などに使用します。
##### {label名}
```その生き物を追って森の奥へと向かった{森へ}```
とすると、同じ文字列が記述されたラベルにジャンプします。
##### {番号}
```その生き物を追って森の奥へと向かった{4}```
とすると、直接指定した行にジャンプします。
行が増えるとわかりにくくなり、行の移動/整理などもしにくくなるので非推奨です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/59911/5ea658fc-c124-d7b1-b75c-448b333b398d.png)
また、
```これ以上進むのはやめたほうがよさそうだと感じ、<br>昇降機へと引き返した{家を出る}```
のように途中で&lt;br&gt;を入れることで、ボタンの文章に改行を入れることができます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/59911/2c1083fa-5556-9f6c-25e7-7247280b8067.png)

## ```label```
{label}で指定したボタンの飛び先として使用します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/59911/36bc4780-631a-43f4-297e-b92a498c56d3.png)

# ゲームブックの内容を確認する

ある程度行を埋めたら、
```https://misawa2048.github.io/Gamebook_GGJ/gamebook/?apikey=[デプロイID]```
にアクセスします。
[デプロイID]の部分には、デプロイした際に表示された
AKfycbzN...のような文字列を入れてください。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/59911/f947e919-80cd-f163-5c8b-95a5734e192e.png)

あとは、スプレッドシート更新
↓
```https://misawa2048.github.io/Gamebook_GGJ/gamebook/?apikey=[デプロイID]```
をリロード

でゲームブックが更新されます。

# その他の機能
```https://misawa2048.github.io/Gamebook_GGJ/gamebook/?apikey=[デプロイID]&sheet=[sheetname]&debug=true```

##### Google Spread Sheet 指定
```&apikey=[Deployment ID]```を指定することで、別のスプレッドシートを指定できます。  

##### シート名指定
```&sheet=[sheetname]```を指定することで、masterSheet以外のシート名を使用することができます。
テスト版、各言語版を作る時などに使えるのではないかと思います。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/59911/1733fe3d-6ef2-51c3-1bab-82d822da8d3a.png)

##### 画像リンク
```&imglink=true```としておくと、
[MEMEPLEX](https://memeplex.app/)の画像を使用している場合、画像をクリックすると
[MEMEPLEX](https://memeplex.app/)の当該の画像にリンクするようにできます。
##### 遷移確認（デバッグ支援）
```&debug=true```を指定することで、戻るボタンやページ遷移が表示されるようになり、デバッグしやすくなります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/59911/5547f6c1-efce-4712-5db4-b92046832b14.png)

##### 初期ページ指定（デバッグ支援）
```&page=[ページ番号]```を指定することで、指定したページからスタートすることができます。

##### 単ページ読み込み（デバッグ支援）
```&row=[行番号]```を指定することで、指定した行番号（spreadsheet基準）かのみ読み込んで表示することができます。```page```と同時に使用することはできません。


# 以上
面白いゲームブックができたらデプロイIDを教えてください！