日々、雑雑と。

いろいろなことを垂れ流し

難易度表のコンテンツをGoogleスプレッドシート+GASで管理してみる

BMS制作について Advent Calendar 2017 - Adventar
BMS制作について Advent Calendar 2017」の23日目の記事です。
「難易度表を作ってみたい」、「難易度表を簡単に管理したい」という方に向けて、Googleスプレッドシート+GASを使った管理しやすい難易度表を作るTIPsを紹介します。
Webアプリケーションを絡ませるため、若干敷居が高いですが、お覚悟を。f:id:zatsuzatsu:20171223012717p:plain

はじめに

難易度表とは

 難易度表とは、数あるBMSから抜粋した譜面を難易度順に並べたリストです。主に下記の理由で作られたりします。

  • 好きな楽曲・人気な楽曲を纏めたい
  • 特定の属性に縛った譜面を集めて練習したい
  • 自作差分・楽曲を難易度表の体裁で公開したい

難易度表の作成

 現在主流の難易度表フォーマットについては、以下のページが参考になります。難易度表を作るときは必ず目を通してください。
 このフォーマットに従ってコンテンツを揃えることで、難易度表として機能します。
第2通常難易度表:難易度表導入支援ツール(難易度表管理者様向け資料)bmsnormal2.syuriken.jp
 
 難易度表を公開するといった場合、Webサーバ上に3点のファイルを上げる必要があります。これらのファイルは同一サーバに置かれている必要はありません。

  • 難易度表のWebページ(*.html)
    • このファイルのアクセスURLは難易度表を読み込むときに真っ先に読み込まれます。
    • ↓のヘッダファイルへのリンクが入っています。
  • 難易度表のヘッダファイル(*.json)
    • 難易度表の概要とデータファイルへのリンクが入ってます。
    • ↓のデータファイルへのリンクが入っています。
  • 難易度表のデータファイル(*.json)
    • 難易度表の各譜面の情報が入っています。JSONのオブジェクト型配列として表現されます。

難易度表の管理

 難易度表は作ったら終わりではなく、今後も逐次更新されていくコンテンツです。ところが、更新の度に、データファイルのJSONを直で変更するのは効率悪いですし、なによりいつの間にかフォーマットを崩す危険性もあります。「JSONとして正しいフォーマットなのに難易度表として取り込まれない!!!」といった問題も十分に起こり得ます。この場合、問題の特定が非常に大変です。
 また、更新する度に毎回FTPでアップロードするの、面倒くさいです。変更したら即座に最新版の難易度表が読み込められる仕組みになってたらラクチンです。
 
 今回は、難易度表の管理方式をスマートにすべく、Googleスプレッドシート上で管理を行い、GASのWebアプリケーションで最新版の難易度表のJSONを取得できるようなシステムを作ってみました。

構成

f:id:zatsuzatsu:20171222232903p:plain
 これから構築する難易度表の構成は上図のようになります。難易度表のトップページとヘッダファイルは、従来通り任意のWebサーバ上に置きます。
 データファイルについては、まず難易度表管理者がGoogleスプレッドシート上に情報を入力して、シート形式で保持しておきます。次に、REST APIが叩かれたときにJSONに変換してデータ情報を返すようにします。
 RESTとはWebアプリケーションの形式の1つで、HTTPのリクエストに対してサーバ側で処理を行い、レスポンスで必要なデータを返すようなモデルです。REST形式で用意されたWebアプリケーションのメソッド群をREST APIと呼びます。詳しくは他のサイトを参照してください。

Googleスプレッドシート

 ブラウザから操作でき、他の人と同時編集が可能なオンライン表作成ツールです。基本的にExcelと操作は同じです。難易度表を管理するときにこのツールを使います。
www.google.com

Google Apps Script (GAS)

 同じく、Googleが用意しているWebアプリケーション作成ツールです。言語はJavaScriptですが、HTTPリクエスト時に呼び出される関数も既に用意されています。また、Googleスプレッドシート内にスクリプトを埋め込むことができ、シート内の情報に容易にアクセス可能です。
 今回はGASで「スプレッドシートからデータをJSON形式に変換する」機能と「REST APIが叩かれたらJSONを返す」Webアプリケーションを実装してみます。
developers.google.com

【システム構築】スプレッドシートREST API

Googleスプレッドシートでテーブル情報作成

 シートを新規作成し、難易度表に必要な情報+任意で欲しい情報を付け加えます。2つ以上の譜面を登録する場合は、カラムそのままで1つ下の行に追記していってください。難易度昇順でソートされているのが好ましいです。
f:id:zatsuzatsu:20171221021023p:plain
 上画像の中で使用しているパラメータはこんな感じです。levelとmd5は必須事項で、他は任意です。データ型は、後述するGASのアプリケーション内で全てString型に統一されるため、気にする必要はありません。

必須 カラム名 概要
level 譜面の難易度
md5 譜面のファイルハッシュ値(md5)
lr2_bmsid IR上で割り振られたID
title 楽曲タイトル(+譜面特有の名前)
artist 楽曲制作者
url 楽曲のダウンロードURL
url_diff 譜面のダウンロードURL
comment 注釈等

 

Googleスプレッドシートの公開

 一通り難易度表の情報を書いたら、Googleスプレッドシートを公開します。「ファイル」->「ウェブに公開」を選択後、以下の赤丸のところをクリックします。
f:id:zatsuzatsu:20171221022956p:plain
 また、スプレッドシートのIDは後ほど使うので、URLからIDを拾ってください(下図参照)。
f:id:zatsuzatsu:20171223000906p:plain

GASのエディタを開く

 次に、「ツール」->「スクリプトエディタ」を選択して、GAS(Google Apps Script)をのエディタ画面を開きます。
f:id:zatsuzatsu:20171221023302p:plain

スクリプトの作成

 エディタにはデフォルトでコード.gsという何もしない関数が用意されたファイルがあるかと思います。そのファイルに、以下のソースコードをコピペしてください。なお、ssIdとsheetNameの初期化は作成した各Googleスプレッドシートの情報に基づいて適宜埋めてください。

  • コード.gs サンプル
/* --------------------------------
 GETメソッドのエントリポイント
--------------------------------- */
function doGet(e) {
  var ssId = "~~~~~"; // GoogleスプレッドシートのID
  var sheetName = "~~~~~"; // シート名
  var data = _getData(ssId, sheetName);
  if (e.parameters.hasOwnProperty("callback") == false) {
    return _createContent(null, data);
  }
  return _createContent(e.parameter.callback, data);
}

/* --------------------------------
 シート内の難易度情報をJSON形式に変換
--------------------------------- */
function _getData(id, sheetName) {
  const sheet = SpreadsheetApp.openById(id).getSheetByName(sheetName);
  const rows = sheet.getDataRange().getValues();
  const keys = rows.splice(0, 1)[0];
  var obj = {};
  return rows.map(function(row) {
    row.map(function(item, index) {
      obj[keys[index]] =  item.toString();
    });
    return obj;
  });
}

/* --------------------------------
 レスポンス内容の生成(JSON/JSONP対応)
--------------------------------- */
function _createContent(callback, returnObject) {
  if(callback != null) {
    return ContentService.createTextOutput(callback + '(' + JSON.stringify(returnObject) + ')').setMimeType(ContentService.MimeType.JAVASCRIPT);
  } else {
    return ContentService.createTextOutput(JSON.stringify(returnObject)).setMimeType(ContentService.MimeType.JSON);
  }  
}

Webアプリケーションの公開

 スクリプトを作成(コピペ)したら、上記メニューから「公開」->「ウェブアプリケーションとして導入」を選択してください。
 その後、下図のように設定して公開ボタンを押すと、Webアプリケーションが駆動して、かつ公開されます。
f:id:zatsuzatsu:20171223001923p:plain

 
 WebアプリケーションのURLにひとまずブラウザからアクセスしてみると、例として以下のようなJSONが得られます。(場合によってはダウンロードされたり、場合によってはブラウザに表示されたりします)

[
    {
        "level": "12",
        "md5": "3bcd7327066ff5872d414ec6d72d841a",
        "lr2_bmsid": "254795",
        "title": "YInMn Blue [CHAOS]",
        "artist": "siqlo",
        "url": "http://manbow.nothing.sh/event/event.cgi?action=More_def&num=220&event=116",
        "url_diff": "https://www.dropbox.com/s/4x3o85nvn81ln63/yinmn_ox_bms.zip?dl=1",
        "comment": "停止・ソフラン"
    }
]

 levelとmd5の項目だけしっかり格納されているのを確認してください。なお、万が一予めIRに登録していなかった場合、後ほどの難易度表読み込み時にエラーが起こるので、気をつけてください。
 

【構築】Webサーバ

 次にWebサーバに難易度表のヘッダと、読み込み用htmlファイルを用意します。

ヘッダファイル(*.json) の作成

 data_urlは前述したGASWebアプリケーションのURLです。name, symbol, data_urlはMUSTですので、漏れなく記述してください。level_order(難易度のソート)はオプションですが、ないと表記時のレベル順序が保証できなくなるため、つけておいたほうが無難です。

{
    "name": "CHAOS難易度表",
    "symbol":"混沌",
    "data_url":"https://script.google.com/macros/s/AKfycbzD5km1Uegw2NTA2Ncr-KZk663JQPKrnYS_-C8lcqwGovJhHGo/exec"
}

難易度表ページ(*.html)の作成

 今回は、最低限難易度表読み込みができることだけを考えて実装します。head内にヘッダ部への参照(URL)を埋め込みます。それだけなので、ブラウザには何も見えません。前述したGASのWebアプリケーションを叩いて、動的にテーブルを生成することで難易度表を可視化することが可能ですが、それを含めると話が長くなるため今回は割愛します。

<html>
    <head>
        <meta name="bmstable" content="http://zatsuzatsu.sakura.ne.jp/chaos-table/header.json"/>
    </head>
</html>

 最後に、ヘッダ部と、難易度表ページをどこかのWebサーバ上にアップロードして公開してください。その後、導入支援ソフトで読み込んで確認してみてください。問題なく読み込めたら構築は完了です! 後はシートの中身を変更するだけで、難易度表の更新ができます。

サンプル

 試しにBeMusicSeeker等で読み込んでみてください。
 ※ブラウザからだと何も見えません。
http://zatsuzatsu.sakura.ne.jp/chaos-table/

注意

 本記事で紹介した構成は、一見便利そうですが、スプレッドシート+GASには以下のデメリットがあります。

  1. アクセスする度にシートからJSONへの変換が走るため、レスポンスまで時間を要する。
  2. 管理者が編集中にアクセスされるとエラーが返ってくる・難易度表として読み込まれない可能性有

 レスポンスの遅さは寛大なプレイヤー様に許してもらいましょう。
 同時アクセス時の難易度表の読み込み失敗は、保存を意図したタイミングで行って回避したいところですが、スプレッドシートの自動更新の変更方法が不明なので、物理的な解決方法は今のところなさそうです。。。せめて更新タイミングを事前に告知しておくぐらいが関の山かと思います。

おわりに

 Googleスプレッドシート+GASで難易度表の管理をしてみる記事でした。快適な難易度表ライフをお楽しみください。
 これからも雑雑とよろしくお願いします。

おまけ

 今回作ったCHAOS難易度表に登録したBMS第一号

[BMS PLAY] YInMn Blue [CHAOS]
 混沌12です。