制作関連


隣接判定

はじめに

ここでの解説は、それなりにFTスクリプト知識を持った方を対象としております。
話の性質上、ずぶの素人の方にはお勧めできません。
初心者の方は、他に多数存在するFTイベントスクリプト解説サイトを見るか、デフォのスクリプトを覗くなりして、 基本的なスクリプト知識を会得してからご覧下さい。

隣接判定とは

たとえば、因縁のあるマスター同士がゲーム中で対峙した時、会話させたいと思いませんか?
ターンで条件指定して、時間の経過でも発生させる事が出来ますが、スクリプト例
この方法では、発生前に片方のマスターが死亡したりすると、おじゃんになってしまいます。
また、どんなに遠く離れていてもイベントが発生してしまう、という見た目の違和感も否定できません。

上記よりも、マスターが所在するエリア同士が隣接した時点で、イベント発生という形にした方が、 イベントを能動的にこなす事も出来るようになり、より雰囲気がでますよね。

  ゴートとムクガイヤ隣接会話の例

 ゴートが@に居る時
ムクガイヤがAかBにいる場合に発生
 ※ムクがプレアかルーニックでも発生

隣接判定とは、特定のマスター(あるいは人材)同士が、隣接したエリアに所在する事に よりイベントが発生するようにする為の、判定スクリプトの事を言います。

しかしFTのスクリプトには、隣接しているエリアを調べるコマンドは存在しません。
そのため、特殊なスクリプトを記述して、「隣接しているか否か」を手動で判定させる必要があります。 その判定方法は、次の項で説明します。

隣接判定の仕方

大きくわけて、3つの方法があります(200712月現時点)

1.ループをつかって、判定する方法(ふぁらんくす共同形式)
2.エリア番号で判定する形式
3.League形式

以下に、順を追って説明します。

1.ふぁらんくす共同形式
古くは、ふぁらんくす共同シナリオ 「決戦〜それぞれの思い〜」(以下ふぁらんくす共同)
から使われている判定方法です。
概要としては、まさにそのままの実装です。
例えば、AとBが隣接したエリアにいるかを調べる場合
エリアが
  1−2−3
  |/
  4
のような接続であるならば
・Aが1にいてBが2にいるならば隣接
・Aが1にいてBが4にいるばらば隣接
・Aが2にいてBが1にいるばらば隣接…(以下略
と、エリア隣接判定を行うスクリプトを最初に書き、 隣接会話を発生させようとする度に、AとBに対象の人物を代入し、 ループによって一つずつ処理していく方法です。
この方法は、現在隣接会話が実装されている全てのシナリオで
(著者の記憶の限り、ループのさせ方に違いはあるけど)使われています。
この結果から、現在においてふぁら共形式の影響力がどれだけ高いかが、分かります。


2.エリア番号で判定する形式
概要として、エリアを規則正しく配置し、AとBの所在エリア番号の差で判定します。
例えば. 隣接関係にあるエリアのエリア番号の差を4以内にします。
(=隣接関係にないエリア間の番号差を4以内にしません)
そうすればAとBの所在地エリア番号の差分だけで判断できます
しかし上記の事を実現させようとすると、空のエリアを多数用意する必要があります。
これは単純なスクリプト処理の範囲を超えてしまうので、この場では詳しく解説しません。

3.League形式
使う予定のないLeagueを利用して判定する方法。詳しくは後述します。

ふぁらんくす共同形式の問題点

ふぁらんくす共同形式は複数のシナリオで使われてる優秀な手です。しかし、一つの欠点があります。それはスクリプト内容が

ループスタート
隣接判定スクリプト
発生条件・AとBに代入
ループ
隣接してたらイベント発生
発生条件・AとBに代入
ループ
隣接してたらイベント発生…(以下略

こんな感じに組まれていく事になるので、隣接会話が多くなるにつれ、ループ処理が 比例して重くなってしまう点。
本家のふぁらんくす共同では、隣接判定を複数に分ける事により対応していますが、 それによりスクリプト全体の行数が増えてしまう結果となり、あまり効果的な対策とはいえません。 一部のシナリオでは、会話〜判定部分全体でループしてしまっているのがあり、それらのシナリオは、 スクリプト全体行数の割にターン開始時の処理が重くなってしまっています。
参考:ふぁらんくす共同の隣接判定部分を抽出したスクリプト

オリゾンテ大陸の場合

基本的にはふぁらんくす共同形式と同じですが、以下の二つの変更点を加えております。

1.判定部分の簡略化
2.(不完全だけど)判定部分とイベント内容の分離。

1.判定部分の簡略化
@ふぁらんくす共同形式が、複数のif(条件式)で一つのエリアの隣接判定をしているのを、 ||式を使って一つにまとめています。

Aif(隣接判定)が成立したときに代入される{Set c,1}を一つに。
ifが並列されている場合、その中のどれか一つのif式でも成立していれば、すぐ下にある{}の中身が実行される為 {Set c,1}は一つで十分なのです。

2.(不完全ですが)判定部分とイベント内容の分離。
ふぁらんくす共同の場合

ループスタート
隣接判定スクリプト
発生条件・AとBに代入
ループ
隣接してたらイベント発生
発生条件・AとBに代入
ループ
隣接してたらイベント発生…(以下略

オリゾンテ大陸の場合
ループスタート
隣接判定スクリプト
発生条件1・AとBに代入
ループ
発生条件2・AとBに代入
ループ…(以下略
発生条件1で隣接していたらイベント発生
発生条件2で隣接してたらイベント発生…(以下略

つまりふぁらんくす共同との違いは、最初に全てのループ処理を行うようにしている点です。
隣接判定で{Set c,1}の所を{Set c,1}に変えています。c>0なら以降の隣接判定を無視。
これにより、何番目の隣接判定が成立したか、 cの数値で判断が可能となっています。
間に長い会話イベントを挟まないおかげで、ループ時間が大幅に短縮されているはずです。

その割には、オリゾンテ大陸のイベントが重いと感じる方もいられると思いますが、
これはスクリプト全体行数のせいであり(45000超)、隣接会話などのループ処理が重いわけではありません。

参考:オリゾンテ大陸の隣接判定部分スクリプト
参考:ターン開始時の読み込み時間について

League(同盟)形式

最後になりましたが、League形式での判定方法の解説です。
これは個人的に画期的な方法だと思います。その理由は ふぁらんくす共同形式(とその派生)が隣接イベントの度にループと判定が必要なのに 対して、League形式ではループ処理を行う必要が、全くなくなるからです。

その設定方法は
League[111〜118][x]に各エリアの隣接情報を全て入れる事です。

League[111〜118][x]にエリアxの隣接エリアのNoを入れる
[x]は隣接情報を入れるエリアのNo
League[111〜118]はxに隣接しているエリア、つまりデフォのルートガルト(No29)を例にすると
League[111][29]には、29に隣接している1つ目のエリア(ルートガルト3区)のNo26を代入。
League[112][29]には、29に隣接している2つ目のエリア(ルートガルト2区)のNo27を・・・
と[118]までxに隣接しているエリアの番号を「Set League」で代入していきます。
隣接エリアがない場合は代入なし(League[115〜118][29]は0のまま)

隣接判定は(a,bを対象キャラの滞在エリアとして)
if(a==League[111][b] | a==League[112][b] | ・・・ | a==League[118][b] & a & b)
で判定する。
(たとえばデフォルトだと
 League[111][2]は1、League[112][2]は4、League[113][2]は5、League[114][2]は3
 に設定(League[115〜118][2]は設定せずに0のままでも良い)
 bが2のとき、
 if(a==League[111][b] | a==League[112][b] | ・・・ | a==League[118][b] & a & b & b!=100)
 を満たすのはaが1,4,5,3のときになる)
デフォのLeague形式スクリプト例

これは、隣接会話だけでなく、他のイベントにも利用できると思います。次回に続きます

League形式なら隣接会話以外にも利用できる?

 まず、2つの隣接判定の違いを考えて見ます。
 ふぁらんくす共同形式が、AとBが隣接した領地なのか「だけ」を判定するのに対し
League形式は、各エリア毎に「隣接するエリアNoそのもの」をLeagueに収納している点です。
 これがどういう利点になるかというと、League形式では、ある人材の隣接しているエリアも簡単に 調べる事が出来るという点です。
 たとえば、特定の人材が隣接した敵国エリア城壁を破壊する、 これは単純なようで作るのが難しいスクリプトです。ですがLeague形式を取り入れた場合簡単に作る事が出来ます。記述例
この方法は、ある2点が隣接しているかどうかだけを判定するふぁらんくす共同形式で行う事はできません。
 また、各エリア毎に隣接したエリアの状況を調べる事ができるので、 一つの例として「戦場でないエリア(隣接エリアに敵国がない)の町を発展させるスクリプト」記述例
といった感じのイベントも作成する事が可能になります。

以上のように、隣接したエリアそのものをLeagueに収納するLeague形式は、ふぁらんくす形式と比べて 汎用性が高いといえます。個人的に、これは大きな利点になると思います。

まとめ

それぞれの特徴を記述しますと

ふぁらんくす共同形式
 ||の活用や{Set c,1}を一つにまとめる事によって、 隣接判定部分のスクリプトは100行未満に出来ます。
 ただし、隣接イベントの度に、判定部分にループする必要があります。
 イベントの一つ一つにループで対応する為、隣接イベントが増えれば増えるほど スクリプト処理の負担が増加します。

League形式
 最初の隣接判定情報を、一つずつ「Set League」で代入する必要があります(ふぁらんくす共同形式のように||でまとめる事は出来ません)
そのため、隣接判定部分こそ長くなる(200行位?)という欠点はありますが、 隣接イベントの度にループ処理を行う必要がないため、隣接イベントが増えるほど、 ふぁらんくす共同形式と比べスクリプトが長いというデメリットは緩和されます。
(ループ処理「GoTo」や追加if条件を入れなくてもいいので)
 隣接会話以外にも、隣接判定情報を利用することができます。

 そして、ふぁらんくす共同形式が(オリゾンテみたいに)条件式を簡略化していなければ スクリプトの短さの面でもLeague形式がより有用です。

参考
まとめみたいなの?

 上記の理由により、隣接会話を少ししか使わない場合は、ふぁらんくす共同形式で良い感じ。 (でも誤差の範囲なのでLeague形式でも可)

 隣接判定を多用するか、マスターや人材の会話以外でもエリア隣接情報を使いたい場合はLeague形式。
 League形式はLeague[111〜118][1〜99]という大量のLeague数値を使う事になりますが、 特殊で本格的なイベントをつくるのでLeagueを温存したい、という明確な理由がなければ、 とりあえずLeague形式で隣接判定を作っても問題は無いといえるかも知れません。