【ティラノスクリプト備忘録】SLG的な会話・シナリオイベント発生システムを組む
「育成・恋愛シミュレーションゲームで良くある、
『○日目にイベントシナリオ××が発生』のような
イベント発生システムが作りたい!」
けど作り方がよく分からないので色々試行錯誤した、という覚書です。
こういう感じの処理を作ったぞ!という覚え書きです。
こんな感じの、「発生条件に応じたイベント(今回であれば日数)を任意のタイミング(今回であれば行動選択の前)で発生させる」、という感じの処理です。
1日目のイベントと2日目のイベントを呼び出していて、3日目は発生条件を満たすイベントが無いのでスキップ。
制作日数:2~3日(連想配列とかオブジェクトの勉強時間含め)
1. あらまし
1.1. イベント発生システムが欲しいぜ!
現在、シミュレーションベースなゲームをティラノスクリプトで作れないか?とシステム面を組んでます。
ゲームフローのイメージは下記のような感じ↓
ホーム画面で毎日の行動を選択
行動の結果によって能力値などのパラメータ変化
日数や能力値などの諸条件によってシナリオイベント発生
そこで、③の機能を実現するにあたって、
『イベントをデータで管理し、発生条件に一致するイベントのシナリオを呼び出す』ような、
イベント判定・呼び出し処理が欲しいと思ったので覚書です。
1.2 実装を考えると処理をなるべく楽にしたい
条件分岐と言えばif文やswitch文、パラメータcond="条件式"の出番。
と考えて、ティラノスクリプト・ティラノビルダーのタグ(switch文の場合はプラグイン)でシナリオファイルにベタ打ちでもできなくもないが、その場合下記のイメージになる。
;発生条件を満たすイベントかを判断して、該当するシナリオファイル&ラベルを呼び出す
;イベント1が発生する場合
[if exp="イベント1の発生条件"]
[jump storage="イベント1が記載されているシナリオファイル".ks target="*イベント1のラベル"]
;イベント2が発生する場合
[elsif exp="イベント2の発生条件"]
[jump storage="イベント2が記載されているシナリオファイル".ks target="*イベント2のラベル"]
;発生条件を満たすイベントが無い場合
[else]
[jump storage="ホーム画面処理ファイル".ks target="*イベント終了後のラベル"]
[endif]
あと、タグリファレンスより、下記のようなこともあるので、あんまり[if]みたいな処理の中に[jump]とか[call]と書きたくない。
(これはcondとか上手く使えば、多分ティラノのタグでもクリアできると思う。製作テクニックとかに載ってそう)
というわけで、イベントを上手い感じに変数で管理+JavaScriptの処理とかでどうにか簡易化できないか、という感じです。
2. 実際に書いたイベント発生システム
2.1 イベント処理のざっくりフロー
上記ホーム画面フローの、青色の前or後イベント判定分岐と前or後イベント呼び出し処理をもう少し整理したものが下図。
ティラノのシナリオファイルはこんな感じの管理です。
2.2 実際に書いたコード
実際に書いたコードはこんな。
①home.ks:イベント判定・呼出し処理
;home.ks
;前略
;事前にhensu.ksを読み込んでおく
;前イベント判定処理
[iscript]
//イベントデータf.eventから、前イベントデータ(bef)のキー名(ev_1,ev_2)を取得して、find()で{}内の条件を満たすイベントを探す
tf.curt_ev = Object.keys(f.event.bef).find(function(key){
return (f.event.bef[key].read == false && //イベントが未読である
f.event.bef[key].isvalid == true && //イベントが有効である
f.event.bef[key].flag() == true //イベント発生条件を満たす
);
})
[endscript]
;中略
;イベント呼出し処理
;前イベントが発生しなければシナリオ呼出しスキップ
[jump target="*select" cond="tf.curt_ev==undefined"]
;前イベントが発生する場合はイベント呼出し
[eval exp="tf.rabel = '*'+f.event.bef[tf.curt_ev].id"]
[call storage="event_bef.ks" target="&tf.rabel"]
*select
;以降のホーム画面処理を記述
②event_bef.ks:イベントシナリオ
;event_bef.ks
;前イベントシナリオファイル
;イベントデータ内のidプロパティと同一のラベル名で管理
;regu_01:1日目発生:プロローグ
*regu_01
[chara_show name="akane" ]
#あかね
今はイベントid:[emb exp="f.event.bef[tf.curt_ev].id"]、イベント名「[emb exp="f.event.bef[tf.curt_ev].name"]」が発生中![p]
シナリオファイル:event_bef.ksの、ラベル:*[emb exp="tf.curt_ev"]を呼び出しているよ![p]
発生条件は「1日目であること」![r]
このイベントにはプロローグ的シナリオが入る予定![p]
それじゃ、ホームに戻るね![p]
#
[chara_hide name="akane"]
[return]
;regu_02:2日目発生:二日目です
*regu_02
[chara_show name="akane" ]
#あかね
今はイベントid[emb exp="f.event.bef[tf.curt_ev].id"]、イベント名「[emb exp="f.event.bef[tf.curt_ev].name"]」が発生中![p]
シナリオファイル:event_bef.ksの、ラベル:*[emb exp="tf.curt_ev"]を呼び出しているよ![p]
発生条件は「2日目であること」![r]
このイベントには2日目的なのゲームシステム説明シナリオが入る予定![p]
それじゃ、ホームに戻るね![p]
#
[chara_hide name="akane"]
[return]
③hensu.ks
;hensu.ks
;イベントデータの他に、判定処理に使いたい変数なども記述し、読み込んでおく
;イベントデータを定義
[iscript]
//befで前イベントの箱を作る
f.event={
bef:{
//イベント1
ev_1:{
id:"regu_01", //id名
type:"must", //イベントタイプ
name:"プロローグ", //イベント名
read:false, //既読フラグ
isvalid:true, //有効フラグ
flag:function(){
return(f.day == 1);
} //個別の発生条件
},
ev_2:{
id:"regu_02",
type:"must",
name:"2日目ですが",
cond:2,
read:false,
isvalid:true,
flag:function(){
return(f.day == 2);
}
}
}
}
[endscript]
2.3 コードの解説①:イベントデータを作る
シナリオイベントのデータはどう作るか。
イベントごとに「ラベル名」「発生条件」「発生済みか否か」「前イベントか後イベントか」などの様々な情報を記録したい……。
ということで、どうしたらいいか色々調べた結果、
オブジェクトでイベントの情報を管理してます。
f.event={
bef:{
//イベント1
ev_1:{
id:"regu_01", //id名
type:"must", //イベントタイプ
name:"プロローグ", //イベント名
read:false, //既読フラグ
isvalid:true, //有効フラグ
flag:function(){
return(f.day == 1);
} //個別の発生条件
},
//中略
}
}
オブジェクトとは何ぞや、という説明は割愛しますが、ざっくりというと情報を色々詰め込める箱みたいなもの(と理解していています)。
今回であれば、
ようなイメージです。
※後々使う予定のプロパティ(type)も入れているが、省略可能。
今回であれば、ev_1が1日目に発生するイベント、ev_2が2日目に発生するイベント、と設定しています。
以下、イベントデータev_xx:{}内の要点説明
//ev_1から、発生条件flagのみ抜粋
flag:function(){
return(f.day == 1); //return()内に発生条件を記載、複数は||や&&で指定。
} //個別の発生条件
flag:発生条件をfunction()で設定しています。
発生条件をどう設定したらいいか分からなかったので、色々試行錯誤した結果、function()のreturn()内にイベントの発生条件(今回であれば日数f.dayが1日目であること)を記述し、条件満たす場合はtrue、そうでなければfalseを返す、ように設定しました。
このflagをイベント発生処理で使います。
//ev_1から、発生条件flagのみ抜粋
id:"regu_01", //id名
id:idなどの一意な値を設け、シナリオファイルのラベル名と揃える
イベントシナリオファイル(event_bef.ks)の該当イベントのラベル名と一致させて、イベント発生処理で使います。
2.4 コードの解説②:発生条件を満たすイベントを取得する
;前イベント判定処理
[iscript]
//イベントデータf.eventから、前イベントデータ(bef)のキー名(ev_1,ev_2)を取得して、find()で{}内の条件を満たすイベントを探す
tf.curt_ev = Object.keys(f.event.bef).find(function(key){
return (f.event.bef[key].read == false && //イベントが未読である
f.event.bef[key].isvalid == true && //イベントが有効である
f.event.bef[key].flag() == true //イベント発生条件を満たす
);
})
[endscript]
発生条件を満たすイベントがあるかを判定する処理
tf.curt_evという変数に、発生条件を満たすイベントのkey(ev_1,ev_2などのこと)を入れる処理です。
①Object.key()という呪文をイベントデータが入っている箱(f.event.bef)に唱えて、箱の中に入っている個々のイベントのラベル名(key、今回であればev_1やev_2)をゲットしています。
②find()という呪文をfunction()という呪文とセットで唱えます。
※find()は結果が一個しか入らないので、複数の結果を入れたい場合はfilter()を使う
function()のreturn()内に「イベントが未読である(read==false)」「イベントが有効である(isvalid==true)」「イベント発生条件を満たす(flag()==true)」などのを記述することで、イベント一個一個が発生条件を満たすかどうかが調べられます。
この処理の結果、tf.curt_evは、1日目はev_1、2日目はev_2、3日目以降はundefinedが取得できるようになっています。
;イベント呼出し処理
;前イベントが発生しなければシナリオ呼出しスキップ
[jump target="*select" cond="tf.curt_ev==undefined"]
;前イベントが発生する場合はイベント呼出し
[eval exp="tf.rabel = '*'+f.event.bef[tf.curt_ev].id"]
[call storage="event_bef.ks" target="&tf.rabel"]
*select
;以降のホーム画面処理を記述
発生するイベントに対応するシナリオファイルを呼出す処理
[call]で発生イベントのシナリオ・ラベルを呼び出す際に、ラベル名の指定にtf.curt_evを使います。(f.event.bef[tf.curt_ev].idがラベル名になる)。
① イベントが発生する場合、tf.curt_evに発生するイベントのkey名(ev_xx)が入っている。
② イベント(ev_xx{}内)のidがイベントシナリオのラベル名と一致するように設定してある。
;イベントシナリオ event_bef.ks 抜粋
;イベントデータ内のidプロパティと同一のラベル名で管理
*regu_01 ;イベント1 ev_1のシナリオ ev_1のidと一致
;中略
[return]
*regu_02 ;イベント2 ev_2のシナリオ ev_2のidと一致
;中略
[return]
;イベントデータの設定
;hensu.ks 抜粋
f.event={
bef:{
//イベント1
ev_1:{
id:"regu_01", //id名 イベントシナリオのラベル名と一致
//中略
},
//イベント2
ev_2:{
id:"regu_02", //id名 イベントシナリオのラベル名と一致
//中略
}
}
}
また、発生する前イベントがない場合はイベントシナリオ呼出しの[call]をスキップするよう、
[call]の前に[jump]を置き、cond="条件"で前イベントが何も発生しない場合(tf.curt_ev=undefined)に実行するように指定します。
上記のシナリオファイルを用意することで、冒頭でお見せしたような処理が実行できるはず……。
3. まとめ・次やること
3.1 やったことの要点まとめ
3.2 次やること・まだ足りない点
今回の処理だけでは下記のような点が足りないので、これから改造していきます。
本記事が何かしら、制作の足しになれば。
ここもっと良くなるとか、何かありましたらご指摘くださると幸いです。
おまけ。「今回の備忘録で作ったシステムを使って、こういうゲーム作ろうって考えてます!」の紹介です。