前回:
[テキストだけのエロゲーを作るvol.5 - 111のデジ同人感想]

111先生のお手本



クリックでゲームに飛びます

zipはここ。

今回はここまで進みました…、そう、デバック専用モードです。
まぁ待ちなさい、なぜこんな事になったのか説明するから。

テキストファイルを外部化だ




前回の次回予告:
プログラムにテキストを直に書き込んでる状態に、111は憤慨する。
「もっと使いやすくしよう!」


という訳で、”もっと使いやすくする”為の方策、それはテキストを外部ファイル化すること。
テキストだけの外部ファイルと、それを読み込むエンジンという関係にする。
これにより管理が簡単になる…というのが目論見(だった)。

短記法の外部テキストデータ。この頃はしゅっとした形に収められ、”冴えてるぜ俺”だったのだが…

だがここで、軽く問題が発生。
文章を表示するだけなら簡単だが、今までプログラムに一緒に埋め込んでいた、”画面をいったん全て消す”・”クリック待ちをする”等の演出をどう実現させるか。

今まではプログラムの中にあったこそ、自由にそういう処理も呼べた。
だが外部ファイル化すると、プログラムに命令を下せる形は、ごく限られてくる。

そこで考えたのが短記法。
テキストの文頭に、まず使わない文字列…、画面消去なら”@ @”、クリック待ちなら”@!@”が付いていたら、
意図を察し、その処理を漏れなく行うという形で解決。

ますますツクールの文章表示に近づいてきたが、ここまでは予想通りではあった。

テキストが読まれる条件、能力値の加減とかどうするの?(もっと上手いやり方があった気もする)


だがその時、次の大きな問題にも踏み込んでいたのだ。

つまり”文章を表示する時の演出”が欲しいように、”そもそも、その文章を表示する条件”・”その文章を表示した時のパラメーター変化”も欲しい。

テキストは外部化したが、その文章と対になる、発動条件はプログラム内のあちこちに散っている…では、結局、小手先が変わっただけではないか。

ここに来て、外部化すべきは『テキストデータ』というより、もっと広い、
文章・条件・パラメーター式を合わせ持った『イベントデータ』と言うべき物だ、と認識を改める。

例えばこんな感じだ。
texts : ["カメに治療費20を振り込みました…"] ,
params : function(){Player.money -= 20} ,
selecter : function(){
return (DB_FRIEND.is_has("kame"))
}


textsは表示する文章群(この例では一行で済んでいるが)、
selecterは文章群が表示される条件、
paramsは表示された際に、プレイヤーに与える値の変動。

この一揃いをイベントデータ形式とみなす。

仮にこれを読み込むなら、
if(obj.selecter()){
obj.params();
for(var i = 0 ; i < obj.texts.length; i++){
MIP.writeText(obj.texts[i]);
}
}

こんな感じで解釈できる。
(selecterで条件関数を実行ーその条件が合っているなら、paramsを実行。
そしてテキスト群が入っている、textsを順に取り出して表示。)

他のイベントデータも全てこの形なら、根幹はこの数行だけで済む。
あとは読み込むデータをすげ替えるだけで、どれでも行ける。
おお、エレガント。


…が、ゲームとは複雑怪奇で、厄介なものである。(やっぱり)

例えばテキスト群の中には、
”選択肢でyesを選んだら、お金があるかチェック。
お金があればAというイベント、なければBというイベント、
そもそもnoを選んでいたらCというイベント”
という物もある。

これをどうすれば解決できるか?

"『先行投資』お金50を払いますか?",
[ // 選択肢
{
head : "yes" ,
texts : [
{
texts : [
"「へへへ、他の奴らには言うなよ" ,
"@!@コマンド『アフィ活動』が可能になりました"
] ,
params : function(){
Player.money -= 50;
Player.select_menu.set("afi");
Player.readScene();
Player.afi_value = 1;
} ,
selecter : function(){
return (Player.money >= 50)
}
} ,
{
texts : [
"カメ「金が足りねー」" ,
"猫「ばれたか…"
] ,
selecter : function(){
return (Player.money < 50)
}
}
]
},
{
head : "no" ,
texts : [
"「俺がリッチになってから泣きついても遅いぞ」"
]
}
]


textsの中に、更に自分と同じ形のイベントデータを含めるようにした。
yesを選ぶ→それぞれのselecterにより、お金がある・無いのどちらかのイベントが実行される。
(迷ったが、選択肢は[ ]でくくったものを特別に判定する…というルールを適応)

実際に動かすコードはこれ。
(数行の根幹コードが、使い勝手を考えるとここまで肥大した)
query : function(data){
// selecterが無ければ全て実行、あれば条件チェック
if(!data.selecter || data.selecter.call(this) == true){
// 値セット
if(typeof data.params === 'function'){ // 関数かチェック
data.params.call(this);
}else{
Object.keys(data).forEach(function(key) {
Player[key] += data[key];
});
}
// texts、配列の一つずつが文字列か、配列(選択肢)か、関数かチェック
for(var i = 0 ; i < data.texts.length;i++){
var text = data.texts[i];
if(typeof text === "string"){
this._textContext(text);
}
else if($.isArray(text)){ // Arrayだった場合
// headほか選択肢処理、あとは再びquery解釈に掛ける
var that = this;
for(var j = 0 ; j < text.length;j++){
var obj = text[j];
MIP.selectMessage(obj.head , (function(_obj){
MIP._flowSave();
that.query(_obj);
MIP._flowLoad();
}).bind(null ,obj));
}
MIP.selectWait();
}else{ // オブジェクトだった場合
this.query(text);
}
}
}
}


textsとselecterとparamsという根幹(解釈装置)は変わらないが、
textsを順に読み込む途中で、(文章でなく)イベントデータがあった場合、それを取り出し、再び解釈装置に投げ込む。

そのイベントデータの中のtextsにもまたイベントデータがあったら、再びそれを取り出し、装置に投げ込む。
そうやって、textsにあるのは文章だけという状態になるまで、砕き続ける。

その濾過をへて残る最小単位は、お馴染みのselecterとparams、textsというイベントデータばかり。
もうtextsは文章だけと分かっているので、面倒なことは無い。
またお馴染みの、数行の根幹処理で考えられる……。



無数の壁が入り組んだ迷路も、一つ一つ順に解きほぐしていったら、構成要素は線しかない、ということ。
(『入れ子になった同形式のデータを、再帰構造で解決した』…のだ)

次回予告


…などと得意げになったが、実際問題としてそもそも管理しやすくしたかったのに、蓋を開けたら、文章の中に選択肢があって、選択肢の中には2つの文章。
とか扱いにくすぎ。

すわ、これが知恵に溺れるという状態なのではないか。

現在形。選択肢を取り扱おうとしたら……どう考えても入れ子になり過ぎっ!

しかしまぁ…解決策もあって、
そもそもプログラム的にスッキリするというのは、コンピューターに分かりやすい構造だということ。
イコール、人間に分かりにくいのも無理はない。

だったらどうするか、コンピューターに手助けさせれば良いのである。

つまりイベントデータを画一化し、概念的には”ただ読み込んで、表示する”という単純なフローに落とし込めたことで、色んな事がやりやすくなったのだ。

代表例が冒頭のデバッグ。
表示する予定のテキストを(条件を無視して)順に読み込んでいく、という事をしてる。

これがもし、プログラム中のあちこちにifとかを書き連ねてたら、条件をいちいちコメントアウトしたり、凄く手間が掛かる。
(今の規模ならまだそれも出来るが、もしイベント量が500とかになったらどうしよう?)

ところが今は、流れが根本で一元化。
だからごく数行手を加えれば、それが可能になるのだ。

エディター予想図

…とは言っても、イベントデータそのものが作りにくいのは変わらないじゃないか、という問題には、それもコンピューターにやらせればいい。
つまり、専用のイベントエディターを作り、文章か選択肢か選んで、表示テキストを書き込んで、「保存」ボタンを押したら、外部ファイルが出来る…とかすればいい。

…出来たらいいネ、次回には…(希望)


…うん、さくっとテキストだけゲーを作るつもりが、まずエンジン部分を作り、
次にテキストの外部ファイル化、そして遂に…専用のテキストエディターを作る話になってるという…