TRPGキャラクターシートの管理のこれから

この記事はTRPG なんでも Advent Calendar 2018の24日目の記事です。

こんにちは。まだまだTRPG初心者(クトゥルフPL2回、アマデウスPL1回)の@acomaguと申します。プログラミングをたまにします。

今回はキャラクターシート置き場についての話をします。自分はクトゥルフTRPGのキャラクターシートをキャラクター保管所さんで管理しているのですが、キャラクターシートを保存できるサイトは他にもいくつかあります。

…などなど。(クトゥルフ以外だとまたいろいろあるみたいですね)

まあ言うほど無いんですが、それでも選択肢があります。ただ、ほとんどの方は自分と同じくキャラクター保管所さんを使っているような気がしています(あくまで自分の観測範囲ですが)。理由を考えると、いろんなサイトで推奨されているとか、数多くのTRPGに対応している、技能値の割り振りがしやすいなどがあると思います。

自分もそこそこ満足していたのですが、何度かアクセスするうちに不満点も見えてきました。「連携できるツールが少ない」ことです。

チャパレさんなど一部のツールはキャラクター保管所さんからの読み込みに対応していますし、キャラクター保管所さんのほうではどどんとふ連携に対応しているのですが、他のほとんどのオンセツールなどでは自分で能力値と技能値を手入力する必要があります。また、クトゥルフWebダイスさんのようなキャラクター作成ツールからの読み込みにも対応していません。このあたりの連携ができるサービスが見つからないのは、キャラクター保管所さんがキャラシートに書き込むためのAPIを提供していないことが原因と考えられます。

「ダイスを振る → キャラクターシートに保存する → オンセツールに表示する」までを一度も手入力することなくやりたかったのです。

このあたりのしんどさを解決しようと以下のようなツールを作ってみたりもしました。

acomagu/cstoml: TRPGキャラクター保管所 -> TOML

キャラシートをパースしやすく人間も読み取りやすいTOMLに変換し、GitHubで管理しようという発想でした。しかし例えば能力値が変更された際にオンセツールが自動でGitHubにコミットを投げるというのも考えづらく、連携のしやすさという点ではこちらも貧弱なものでした。

また、単にキャラクターシートのデータを取得/更新できるだけではなく、リアルタイム性も担保したいと考えていました。というのも今後GMをやる上で、PL全員の能力値の現在値をまとめて閲覧できるビュワーがほしいと考えていたからです。単なる保存場所としてのWebサイトとは一線を画したものが必要でした。

そこまで考えて、私は Solid が解決策になるのではと気づきました。

Solidとは

ちょっと一言で表すのが難しいのですが、もし無理やりその一言を選ぶのならばやはり「データをユーザの手に取り戻す」でしょうか。

仕組みを大まかに説明すると、ユーザーはサービス提供企業それぞれに個人情報を提供するのではなく「Solid POD」と呼ばれる保管場所(クラウドあるいは自分のサーバ)上に情報を保存し、Solid上のサービスを使う際、どの情報をサービス側に提供するかを選択する。これにより、ユーザーが自分の情報を管理しやすくなるだけでなく、サービスを利用した際に生じる個人データもアプリと分離されてPODに保存されるので、サービス側も大量のデータを収集する必要がないという(Solid Explainedページより)。

気になったエンジニアの方はぜひ公式サイトのMake a Solid app on your lunch breakをやってみてほしいのですが、結構面白いです。

以上のような特徴により、Solidは今までWebアプリケーションの背後にあったユーザのデータを、別の場所(=POD)に切り離して管理できるようにします。

そしてこのSolidを単なる理想論から現実に近づける特徴が「標準化された技術でできている」ことです。

Solidは大雑把に言って2つの標準を使用しています。まず1つがWebIDです。ユーザは自由にPODを乗り換えることができますが、ユーザがPODを変えてもユーザのデータのURLは変えない仕組みと言えます。1

そしてもう1つがRDFです。これはアプリケーション間のデータの形式の差異の最小限にするためのものです。GoogleカレンダーがPODに正しく予定を保存してくれていれば、その情報がYahoo! カレンダーでもそのまま開けるということですね。

なるほど… ではこのSolidをTRPGのキャラクターシート置き場に利用すると、何が起こるのでしょうか。

キャラクターシートをSolid PODに保存すると何が起こるか

「キャラクターシート置き場にSolidを利用する」ということは「キャラクターシートをSolid PODに保存する」ことを意味します。

こうすることで何がどうなるのか、もしすべてのサービスがSolidに対応していたと仮定して、使い方をシュミレートしてみたいと思います。

  1. まず、ユーザはキャラクターシートを作成するために「クトゥルフWebダイス」さんのようなサイトでダイスを振り、能力値を決めます。ついでに技能値やキャラクター名も決めたところで保存ボタンを押すと、Solid PODに保存されます
  2. ユーザはそのまま好きなオンセサイトに移動すると「PODに保存されたキャラクターシート一覧」が更新され、先程作ったものが選べるようになっています

こんな感じです。書き込みも読み取りも、初回はWebIDの入力と権限の確認があるでしょう。

データとアプリケーションが粗結合になったことでそれぞれのサービスの相互の対応の可否を気にする必要がなくなりましたし、開発者としてもSolidのみに対応することでよくなりました。

また今までは、例えばオンセなどでは「外部サービスからのインポート」と「自サービスのDBからの読み出し」を別の機能として実装していたのが、「Solidからの読み出し/保存」に統一されることで開発の負担も減ることが予想されます2

更にSolidはリアルタイム更新対応です3

そしてそしてすべての情報は予め定まった「キャラクターシートの規格」に沿って保存されるため、他のどんなアプリケーションでも同様にデータを認識することができ、ユーザは好きなサービスに乗り換えやすくなることでしょう

…「キャラクターシートの規格」なんてあるんでしたっけ?

「クトゥルフTRPGキャラクターシート」のためのRDFスキーマを定義した話(本題)

そう、ユーザのプロフィールやカレンダーの予定に関する規格はあっても、TRPGのキャラクターシートの規格はなかったんですよね()

そこで少し頑張って書いてみました。

Turtle形式のソースコードはこちら:

RDFに関して書き出すと長くなるので言及しませんが、一応定義してみた情報は以下になります。

以上です。RDF(S)では「あるクラスが持つべきプロパティ」は定義せず、「あるクラスが持てるプロパティ」をどんどん新しく追加していけるという仕組みなので、ここにない情報もキャラクターに追加できます。例えばキャラクター名にはfoaf:name、職業や趣味にはwai:playsvcard:has titleが相応しいでしょう。ここではクトゥルフ神話TRPG専用の、足りない語彙のみを定義しています。

例として、私のキャラクターのすーちゃんをこのRDFを利用して記述すると以下のようにできます。

@prefix foaf: <http://xmlns.com/foaf/0.1/>.
@prefix vcard: <http://www.w3.org/2006/vcard/ns#>.
@prefix ccs: <https://ontology.acomagu.me/ccs#>.
@base <https://rdf.acomagu.me/ccs/1>.

<#character> a ccs:Character;
	foaf:name "すー"@ja;
	vcard:title "アパレルショップスタッフ"@ja;
	ccs:str 10;
	ccs:con 8;
	ccs:pow 12;
	ccs:dex 13;
	ccs:app 15;
	ccs:siz 12;
	ccs:int 10;
	ccs:edu 13;
	ccs:hp 10;
	ccs:mp 12;
	ccs:san 60;
	ccs:has_skill <#fistPunch>;
	ccs:has_skill <#firstAid>;
	ccs:has_skill <#looksmith>;
	ccs:has_skill <#spotHidden>;
	ccs:has_skill <#craft>;
	ccs:has_skill <#disguise>;
	ccs:has_skill <#persuade>;
	ccs:has_skill <#art>;
	ccs:has_skill <#accounting>;
	ccs:has_skill <#psychology>.

<#fistPunch> a ccs:FistPunch
	ccs:chance 80;
	ccs:kind "パンチ"@ja.

<#firstAid> a ccs:FirstAid
	ccs:chance 60.

<#looksmith> a ccs:Looksmith
	ccs:chance 41.

<#spotHidden> a ccs:SpotHidden
	ccs:chance 85.

<#craft> a ccs:Craft
	ccs:chance 35;
	ccs:kind "衣服"@ja.

<#disguise> a ccs:Disguise
	ccs:chance 31.

<#persuade> a ccs:Persuade
	ccs:chance 75.

<#art> a ccs:Art
	ccs:chance 15;
	ccs:kind "衣服"@ja.

<#accounting> a ccs:Accounting
	ccs:chance 20.

<#psychology> a ccs:Psychology
	ccs:chance 65.

いくつかの情報は抜け落ちてしまっています。必要最低限という感じですね。

RDFのいいところは、オブジェクト指向と違い元のクラス定義を変えなくとも、インスタンスのプロパティはどこまででも増やしていいというところです。もし巷に必要なプロパティがなければ自分で定義することもできます。

…とはいいつつこのスキーマももうすこしちゃんと体制を整えてバージョンを上げるつもりですので、もし定義内容に改良点等あればぜひ教えていただければと思います 🙏

本当はこれを使ったアプリケーションまで書きたかったんですがいかんせんRDF自体を理解するのにとても難儀してしまいこんな記事になってしまったことをお許しください… 自分でも近いうちに完成させますが、こちらを使ってアプリケーションを作ってみたという方がいれば泣いて喜びますし、拡張RDFを作ってみたとかクトゥルフTRPG以外のものでやってみたとかでも全力で参考にさせていただくのでもしあればご連絡ください 🙇

Solid入門をもう一度貼っておきます→ Make a Solid app on your lunch break

まとめ

あと今回の話には関係ないんですが音がなるDiscordダイスBotもよろしくお願いします→acomagu/dicebot: ダイスを振るDiscord Bot

ここまで読んでくださってありがとうございました!!!


  1. まだよくわかっていないのですが、正確には更に認証&認可の仕組みも備えているようです。 [return]
  2. 実際にはそううまく行かないことが多いでしょうが、少なくとも「Solidへエクスポートする」ボタンだけは蔓延らないことを心から願っています。常に最新のデータがSolidに無い状態でないとそのメリットは半減すると考えています。 [return]
  3. WebSocketか何かでListenできるみたいです。 [return]