【ティラノスクリプト】 文字列を解析してシナリオ実行!"eval_scenario"プラグインの紹介
こんにちは、箱詰九分です。
ティラノスクリプトって、javascript内で動的にシナリオを生成して実行できないかなーと思った時ありませんか?私はありました。ありますよね。
そこで、"eval_scenario"という、ティラノスクリプト用のプラグインを作成しました。配布もします。(ティラノスクリプトV5用です)
このプラグインは、文字列変数内に記述されたシナリオを解析して実行する[eval_scenario]タグをティラノスクリプトに追加します。セーブ・ロードにも対応しています。eval_scenario の終了後は、元のシナリオに続きに復帰します。また、eval_scenario 専用のボタン系タグも追加されます。
それが何になるの?普通にシナリオ書けばいいじゃん、と思われるかもしれませんが、例えば、以下のような場合にこのプラグインは便利です。
1. シナリオを javascript 上で動的に生成し、実行する。
2. 探索ゲーム、脱出ゲームなどで、ものを調べた際にちょっとしたシナリオを発生させる。
上記 1、標準機能だとそもそもシナリオの動的生成はできません。startTagで対応しようにも、工夫しないと挙動がおかしくなります。また、上記 2を普通に実装しようとすると、いちいちラベルにジャンプさせる処理が必要となるため、処理が複雑になるとシナリオの記述が煩雑になり、わけがわからなくなることが想定されます。
このプラグインはそういった問題を解消してくれます。
注意
[!]eval_scenario内では、サブルーチンとマクロは使えません。ご了承ください。ですが、ご安心ください。代替の方法はあります(この記事に記載あり)。
デモ
こんな感じです。実際には文字列内に記述されたシナリオを解析して実行していますが、普通にシナリオ進行できています。セーブ・ロードもしっかりできていますね。
(追記)プラグイン活用例
このプラグインを使って、シナリオ中の全てのタグ・テキストをランダム生成するという荒技を行いました。ソースコードも公開してますのでご参考までに。
配布場所
ご自由にお使いください。ライセンスはMITライセンスとします。
ちなみにデモのソースコードはこんな感じです。ご参考までに。
;ティラノスクリプトサンプルゲーム
*start
[cm ]
[clearfix]
[start_keyconfig]
[bg storage="room.jpg" time="100"]
;メニューボタンの表示
@showmenubutton
;メッセージウィンドウの設定
[position layer="message0" left=160 top=500 width=1000 height=200 page=fore visible=true]
;文字が表示される領域を調整
[position layer=message0 page=fore margint="45" marginl="50" marginr="70" marginb="60"]
;メッセージウィンドウの表示
@layopt layer=message0 visible=true
;キャラクターの名前が表示される文字領域
[ptext name="chara_name_area" layer="message0" color="white" size=28 bold=true x=180 y=510]
;上記で定義した領域がキャラクターの名前表示であることを宣言(これがないと#の部分でエラーになります)
[chara_config ptext="chara_name_area"]
;このゲームで登場するキャラクターを宣言
;akane
[chara_new name="akane" storage="chara/akane/normal.png" jname="あかね" ]
;キャラクターの表情登録
[chara_face name="akane" face="angry" storage="chara/akane/angry.png"]
[chara_face name="akane" face="doki" storage="chara/akane/doki.png"]
[chara_face name="akane" face="happy" storage="chara/akane/happy.png"]
[chara_face name="akane" face="sad" storage="chara/akane/sad.png"]
;yamato
[chara_new name="yamato" storage="chara/yamato/normal.png" jname="やまと" ]
#
evalScriptのデモ開始。[p]
[iscript]
//変数の中にシナリオを記述する。[eval]タグでやると文字列が変換されてしまうので、
//[iscript]の中じゃないとダメ。
//改行するときは改行コード'\n'を入れよう。
//文字列を'+'で連結してるのは、単に見やすさのため。なくてもいい。
//javascript から evalScenario を呼び出す際は、最初のタグの後のみ wait を入れる必要あり。
f.hoge = "[chara_show name='akane' time='1000']\n" +
"[wait time='1000']\n" +
"#あかね\n" +
"こんにちは。[p]\n" +
"私の名前はあかね。[p]\n" +
"[chara_mod name='akane' face='happy']\n" +
"いえーーーい。[p]\n";
f.moge = "[chara_mod name='akane' face='default' time=1000]\n" +
"実は、文字列変数の中に格納されたシナリオ情報が動いてるよ。[p]\n" +
"[chara_mod name='akane' face='happy']\n" +
"ふふ、すごいでしょ?[r]セーブロードにも対応してるから安心![p]\n" +
"[eval_scenario exp='f.soge']";
//一応、 evalScenario 中に evalScenario できる。需要ある?
f.soge = "[chara_mod name='akane' face='default' time=1000]\n" +
"このevalScenarioを使えば、探索ゲームとか脱出ゲームとかで...[p]\n" +
"ものを調べた時、一時的なシナリオを表示するのに役立ちそうかな?[p]\n" +
"[chara_mod name='akane' face='doki']\n" +
"あとは、シナリオをjavascriptで生成するとか!夢が広がるね![p]\n";
//javascript から起動するときは以下の関数を呼び出そう。
TYRANO.kag.eval_scenario.evalScenario(f.hoge);
[endscript]
;タグで呼び出すのは以下でできる。
[eval_scenario exp="f.moge"]
;eval_scenario の終了後は元のシナリオの続きに復帰します。
[chara_mod name="akane" face="default"]
#あかね
まあ、そんな感じです。[p]
[s]
導入方法
1. gitHubのリポジトリをダウンロードします。(右上の[code]ボタン->[Download Zip] で)
2. Zip を解凍します。
3. 解凍したフォルダ内の"eval_scenario"フォルダを、あなたのティラノスクリプトプロジェクトの"data/others/plugin/"以下にコピーします。
4. "first.ks" 内に以下を記述します。
[plugin name="eval_scenario"]
以上で導入完了です。
※ "make.ks"に何かを記述する必要はありません。
使い方
例えば、以下のように記載します。
[iscript]
f.hoge = "[chara_show name='akane' time='1000']\n" +
"#あかね\n" +
"こんにちは。[p]\n" +
"私の名前はあかね。[p]\n" +
"[chara_mod name='akane' face='happy']\n" +
"いえーーーい。[p]\n";
[endscript]
[eval_scenario exp="f.hoge"]
・ 変数の中にシナリオを記述した文字列を代入してください。[eval]タグで行うと文字列が変換されてしまうので、 [iscript]の中で記述する必要があります。
・ 改行するときは改行コード'\n'を入れます。
・ 文字列を'+'で連結してるのは、単に見やすさのためで、なくてもいいです。
また、以下のように、[iscript]内で直接実行することもできます。この場合、最初のタグの後ろのみ[wait]をいれてください。
[iscript]
f.hoge = "[chara_show name='akane' time='1000']\n" +
"[wait time='1000']" +
"#あかね\n" +
"こんにちは。[p]\n" +
"私の名前はあかね。[p]\n" +
"[chara_mod name='akane' face='happy']\n" +
"いえーーーい。[p]\n";
TYRANO.kag.eval_scenario.evalScenario(f.hoge);
[endscript]
余談ですが、[iscript]内で直接実行する際は、変数に代入せずにそのまま文字列を引数に渡しても大丈夫です。
[iscript]
TYRANO.kag.eval_scenario.evalScenario(
"[chara_show name='akane' time='1000']\n" +
"[wait time='1000']" +
"#あかね\n" +
"こんにちは。[p]\n" +
"私の名前はあかね。[p]\n" +
"[chara_mod name='akane' face='happy']\n" +
"いえーーーい。[p]\n"
);
[endscript]
タグ
[eval_scenario]
文字列変数内に記述されたシナリオを解析して実行します。
使用できるパラメータ:
exp : 解析する文字列変数を指定します。
storage : 復帰後に遷移する ks ファイルを指定します。指定がなければ呼び出し元の ks ファイルとなります。
target : 復帰後に遷移するラベルを指定します。指定がなければ呼び出し元の次のタグに遷移します。
[es_jump]
実行中の eval_scenario を中断して jump します。
eval_scenario 内では通常の jump タグを使用できない(使用するとバグる)ため、こちらを使用してください。
使用できるパラメータ:
storage : 遷移する ks ファイルを指定します。指定がなければ eval 中のシナリオを指します。
target : 遷移するラベルを指定します。指定がなければシナリオの先頭に遷移します。
[es_button]
button の eval_scenario バージョンです。ボタンを押すと exp に指定した文字列を eval_scenario します。fix 指定もできます。
role, savesnap パラメータは存在しません。
使用できるパラメータ:
exp : 解析する文字列変数を指定します。
storage : 復帰後に遷移する ks ファイルを指定します。指定がなければ呼び出し元の ks ファイルとなります。
target : 復帰後に遷移するラベルを指定します。指定がなければ呼び出し元の次のタグに遷移します。
その他使用できるパラメータは button と同じです。
[es_glink]
glink の eval_scenario バージョンです。ボタンを押すと exp に指定した文字列を eval_scenario します。
使用できるパラメータ:
exp : 解析する文字列変数を指定します。
storage : 復帰後に遷移する ks ファイルを指定します。指定がなければ呼び出し元の ks ファイルとなります。
target : 復帰後に遷移するラベルを指定します。指定がなければ呼び出し元の次のタグに遷移します。
その他使用できるパラメータは glink と同じです。
[es_clickable]
clickable の eval_scenario バージョンです。clickable と異なり、exp パラメータが追加されています。
指定した範囲を押すと exp に指定した文字列を eval_scenario します。
使用できるパラメータ:
exp : 解析する文字列変数を指定します。
storage : 復帰後に遷移する ks ファイルを指定します。指定がなければ呼び出し元の ks ファイルとなります。
target : 復帰後に遷移するラベルを指定します。指定がなければ呼び出し元の次のタグに遷移します。
その他使用できるパラメータは clickable と同じです。
※ボタン系のタグは、[s]タグで止めると押せるようになります。(この挙動は通常のボタンと同じ)。しかし、eval_scenario が終了した後は、この[s]タグの次のタグから再開されることに注意してください。(そういう仕様です。)
ボタン類の記述はこんな感じ。
[chara_mod name="akane" face="default"]
#あかね
ところで、ボタンはどういう感じになる?[p]
[es_button x=135 y=230 graphic="title/button_start.png" enterimg="title/button_start2.png" exp="f.choge" target="*tameshi"]
[es_clickable exp="f.choge" color="0xffffff" opacity="100" mouseopacity="200" target="*tameshi" x=100 y=200 width=100 height=100]
[es_glink color="blue" size="28" x="360" width="500" y="150" text="いける" exp="f.choge" target="*tameshi" ]
[es_glink color="blue" size="28" x="360" width="500" y="250" text="いけない" exp="f.nyoge"]
[s]
[chara_mod name="akane" face="default"]
#あかね
おわり![p]
[s]
eval_scenarioが実行される以外は、通常のボタンと同じです。
(追記)マクロ・サブルーチンの代替手法
eval_scenario内では、サブルーチンとマクロは使えません。とても悲しいことです。しかし、それと全く同じことを行う方法があります。それは、マクロ・サブルーチンのような使いまわしたい処理を、「文字列に格納する」、あるいは、そうした文字列を返す「関数を実装する」方法です。
例えば、ゲーム内で必ず通る場所(first.ksなど)に、下記のように記載します。
[iscript]
f.myMacro = "[cm]\n" +
"[clearfix]\n" +
"[start_keyconfig]\n" +
"[showmenubutton]\n" +
"#macro1 \n" +
"macro1を実行しました。[p]\n";
window.myMacro2 = function(name, top, left, angle) {
let tag_chara_move = "[chara_move name='"+ name +"' wait='false' top='"+ top +"' left='"+ left +"' ]\n";
let tag_round = "[keyframe name='round']\n" +
"[frame p='100%' rotate='"+ angle +"deg' ]\n" +
"[endkeyframe]\n" +
"[kanim name='"+ name +"' keyframe='round' time='600' ]\n";
return tag_chara_move + tag_round + "#macro2 \n macro2を実行しました。[p]\n";
}
[endscript]
"window. " というのは、グローバル関数(または変数)を宣言するための記述です。このようにして、使いまわしたい処理を文字列または文字列を返す関数として定義しておきます。(パラメータを必要とするマクロの場合は関数として実装しておきましょう。)
そして、eval_scenarioする文字列中の、マクロを使いたい箇所に下記のように差し込みます。
[iscript]
f.hoge = "[chara_show name='akane']\n" +
"#あかね \n" +
"マクロ実行したい。[p]\n" +
f.myMacro +
"#あかね \n" +
"はい。[p]\n" +
window.myMacro2("akane", 100, 800, 270) +
"#あかね\n" +
"...厳密にはマクロ実行じゃないけど。[r]\n" +
"まあ、やりたいことは実現できるかな?[p]\n";
[endscript]
[eval_scenario exp="f.hoge"]
するとこうなります。
厳密にはマクロ実行ではないのですが、同じ処理を使いまわす、という目的は達成できます。
不安
ひとつ不安があるのは、導入するとティラノ本体の関数(loadGameData)を上書きしちゃうので、バージョンによっては不具合起きるかも…なんかあったら教えてください。
終わりに
使い方次第では、シナリオを全部[iscript]内に書くとか、タグの順番を都度シャッフルしてシナリオ実行するとか、ボタンにシナリオ仕込むとか、そういう変態じみたことができます。結構便利なのでは?ぜひ使ってみてください。ありがとうございました。
著者 twitter
何か不具合があればお知らせください。