この記事はtraP Advent Calendar 12月12日の記事です。
今個人的に作っているゲームでストーリー部分を作成した時に得た知見などを書いていきます。ストーリー部分はノベルゲームなどの立ち絵+テキストがあるようなものだと思ってもらえればいいです。
背景
- そこそこの表現力は欲しい
- テキスト担当は別の人
- ITに疎く、PCが使えない
- 実行前に文法や型のチェックをしたい
- ノベルゲームではないので吉里吉里等は使えない
以上を踏まえて、テキストを記述できるスクリプト(と呼べるかわかりませんが…)を設計しました。
仕様
とりあえずサンプルを以下に示します。
@define(tarou, 太郎, left)
@define(hanako, 花子, right)
tarou:
@wave(tarou)
こんにちは
hanako:
@wave(hanako)
こんにちは
tarou:
@change(tarou, laugh)
今日はいい天気ですね。
hanako:
@change(hanako, laugh)
ええそうですね。
まず @define
で登場キャラクターを定義します。1番目にid、2番目に表示されるキャラクター名、3番目に表示される位置を指定します。以下キャラクターを指定するときはこのidで指示します。
次の行からは実際のセリフを記述していきます。id:
(以下ラベル)から次のラベルまでがそのキャラクターのセリフです。セリフにはテキストの他に@wave
などのアクションと呼ばれるものを書くことが出来ます。[1]
恐らくこれでそこそこの表現力はあるはずです。もし足りなければアクションを追加すれば表現を増やせます。あまりややこしい表現は無理かもしれませんが。
実装
仕様が決まったので早速実装に入りますが、ゲーム側で直接スクリプトを読み込んで処理するのは危険です。というのも人間誰でもミスをするもので、アクション名のスペルミスはもちろん定義していないキャラクターを使おうとする、というような実行時に死ぬようなミスは予め検知したくなります。
ということでエラーチェッカー兼、ゲーム側で読みやすくなるようにJSONに変換するアプリケーションを作りたいのですが、これの使用者(テキスト担当)は初めに述べたようにPCを所持していないので
- スマートフォンのアプリ
- Webアプリ
- Slackなどのbot
のいずれかを作成することになります。スマートフォンのアプリは手間がかかるので省くとしてWebアプリかbotかで悩みましたが、生成したファイルをこちらが受け取る手間を考えるとbotの方が良いと判断しました(一度botを作ってみたいというのもありました)。
メンバー間の連絡にはSlackを使っているので(と言ってもLINEから完全に移行できていませんが…)、Slackのbotを作成しました。
こんな感じにメンションを飛ばすとbotが変換してくれます。
エラーも指摘してくれます。
botはRubyで実装してHeroku上で動かしています。Herokuは1つのアプリケーションだったら無料で24時間連続稼働できるのでおすすめです。
初めはHUBOTを使用してbotを作成していたのですが改行を含むメッセージを上手く取ることができなかったので、slack-ruby-clientを使用しました。
チェッカーですが、
def_action(:define, [:alphabet, :any, %w(left center right)])
def_action(:wave, [:id, :varg])
def_action(:change, [:id, :alphabet])
def_action(:show, [:id])
def_action(:hide, [:id])
というような感じでアクション名と引数の型か取りうる値を書いておいてそれを満たすかどうかをチェックしています。
あとは変換したファイルをゲーム側で読み込んで書いてある通りに処理すれば目的は達成です。幸い使用しているSiv3DというライブラリにJSONを読み込む機能があるのでそれを使用しました。
実際に使ってもらう
実際の使用感はどうなの?と気になるかと思われますが、自分で使ってみた分にはそこそこ快適でした。"そこそこ"と書いたのはbotという性質上仕方ないのですが、テキストを書く→変換→修正→変換というサイクルが少しやりにくいと感じました。
また、変換結果がメッセージとしてポストされるので、結果をコピーしてから手元のスクリプトファイルにペーストしないとゲームに反映されないというのも上のサイクルをよりやりにくくしていると感じました。
そこで以下のように修正しました。
- 編集しにくい
- ポストしたスクリプトを編集したら再変換するようにした
- 変換結果をコピーするのが面倒
- 変換結果をファイルとしてアップロードするようにした
それぞれ、slack-ruby-clientが対応しているので容易に対応することができました。
1はRealTime::Client
でメッセージを受け取る場合、編集した時は内容がdata.text
ではなくdata.message.text
となります。編集時はdata.subtype
がmessage_changed
となるのでそれによってメッセージの内容の取得元を変えれば編集時にもポストと同様の処理をすることができます。
client.on :message do |data|
text = if data.subtype == 'message_changed'
data.message.text
else
data.text
end
2はここを見てもらえればいいのですがHerokuで動かす場合は/tmp
でないとファイルが書き込めないみたいなので注意してください。Rubyの場合はTempfileを使うことができます。
Tempfile.create("prefix") do |f|
f.write(scenario.to_json)
f.close
web_client.files_upload(
channels: data.channel,
as_user: true,
file: Faraday::UploadIO.new(f.path, 'application/json'),
title: '変換済みシナリオ',
filename: 'scenario.json'
)
end
これらの改良をすることでだいぶ快適になりました。
まとめ
非ノベルゲーム向けの簡単なノベルエンジンとスクリプトを設計、実装しました。変換するプログラムをSlackのBotとして実装しましたが、なかなか使い勝手が良かったので是非とも参考にしていただければと思います。
明日はdainさんとwhiteonionさんです。
@define
をアクションとして扱うのに違和感(C言語などで関数呼び出しで変数宣言するようなものです)を持たれる人もいるかもしれませんが、ここでは簡易さを優先してアクションとしました。 ↩︎