「Winゲームかんたん制作委員会」様の「コミックメーカー3 質問用掲示板」で投稿した記事のログです。
 追う1から改造していく過程での考え方を書いています。ある意味メイキング。
 自分で新しい機能を開発する時などに、考え方の参考になる。かも。
 現在の追う3は更に改良を加えた物です。


>障害物
障害回避の移動調整について、ですが……。
上げたサンプルは、何も障害が無い場合でのすこぶる単純な基本の考えです。
ほんと、うっかり作ったので、採用されると思わなかったんです。(苦笑)

障害回避の前に、現段階での移動調整について。
「プレイヤー」では、上から順に、左上、左下、右上、右下のダイナミックレイヤーを作っています。
例えば左上のダイナミックレイヤーの作成は、

[X]=[プX]−1
[Y]=[プY]−1
IF[X]≠0
 IF[Y]≠0
  サブルーチンコール「XY」
  左上のダイナミックレイヤー作成
 ENDIF
ENDIF

としています。
[プX]=1〜11、[プY]=1〜15なので、1を引くと[X]=0〜10、[Y]=0〜14になります。しかし[X]=0または[Y]=0にダイナミックレイヤーを作ると、マップから出てしまいます。
なので[X]≠0かつプY≠0の時は左上を作成する、とだけ指定すれば事は足ります。
左下、右上、右下も同様です。

障害回避の為に、今の状態をいじって調整してみます。
例えば(4,4)に障害物がある時、(3,3)(3,5)(5,3)(5,5)から移動する時に(4,4)にはダイナミックレイヤーを作らないように調整が必要です。

(5,5)の時左上にダイナミックレイヤーを作らない為には、左上のダイナミックレイヤーの作成を、

[X]=[プX]−1
[Y]=[プY]−1
IF[X]≠0
 IF[Y]≠0
  IF[X]=4
   IF[Y]≠4
    サブルーチンコール「XY」
    左上のダイナミックレイヤー作成
   ENDIF
  ELSE
   サブルーチンコール「XY」
   左上のダイナミックレイヤー作成
  ENDIF
 ENDIF
ENDIF

とします。
[X]≠0かつ[Y]≠0の条件をクリアした中に、[X]=5−1=4、[Y]=5−1=4は含まれています。
[X]=4&[Y]≠4の時、つまり(4,1〜3・5〜)の時、左上を作成します。そして[X]=4以外の時も、左上を作成します。
しかしこの方法で左下、右上、右下もやっていくと、コマンドがつらつら続き、障害物を加える度に調整が必要で、面倒な上にバグの原因になりかねません。
そこで。
X、Yに障害物の数値が入った時は弾く、専用ページを作ります。
『弾く』方法は二つあります。サブルーチンコール先から戻らせるか、数値または文字列変数を変えてその値の時は動作をしないか。今回は両方を一緒に使ってみます。
新しいページ「プ止」と、数値変数[止]を作成して、以下のように変更します。

「プ止」
[止]=0
IF[X]=4
 IF[Y]=4
  [止]=1
 ENDIF
ENDIF
IF[止]=1
 〔サブルーチンから復帰〕
ENDIF
移動「XY」

「プレイヤー」
[X]=[プX]−1
[Y]=[プY]−1
IF[X]≠0
 IF[Y]≠0
  サブルーチンコール「プ止」
  IF[止]=0
   左上のダイナミックレイヤー作成
  ENDIF
 ENDIF
ENDIF
〜〜(左下、右上、右下も「XY」→「プ止」、IF[止]=0とENDIFを付け足す)〜〜

「プレイヤー」で[X][Y]にコマ数を入れ、[X]≠0かつ[Y]≠0の時は「プ止」をサブルーチンコール。
「プ止」では最初に[止]を0にしておく。
(4,4)にダイナミックレイヤーを作る準備をした[X][Y]の時は[止]=1にして、「XY」換算はしない。(これは換算の必要無いから、ですが、換算だけなら大して時間はかからないのでこのIF〜ENDIFは入れなくても構いません。半分私の好みで入れています)
(4,4)以外にダイナミックレイヤーを作る時は「XY」に移動して座標の換算をする。
「プ止」または「XY」でサブルーチンから復帰して「プレイヤー」に戻り、[止]=0の時はダイナミックレイヤーを作成して、[止]=1の時はダイナミックレイヤーは作成しない。

せっかくですから、[X]=0,12、[Y]=0,16も「プ止」ページに一括してしまいましょう。

「プ止」
[止]=0
IF[X]=0
 [止]=1
ENDIF
IF[X]=12
 [止]=1
ENDIF
IF[Y]=0
 [止]=1
ENDIF
IF[Y]=16
 [止]=1
ENDIF
IF[X]=4
 IF[Y]=4
  [止]=1
 ENDIF
ENDIF
(ここにIF[X]=○、IF[Y]=□、[止]=1、ENDIF、ENDIFを入れて新しい障害物の情報を足す)
IF[止]=1
 〔サブルーチンから復帰〕
ENDIF
移動「XY」

「プレイヤー」
[X]=[プX]−1
[Y]=[プY]−1
 サブルーチンコール「プ止」
 IF[止]=0
  左上のダイナミックレイヤー作成
 ENDIF
ENDIF
〜〜(左下、右上、右下も同様に)〜〜

すっきり。


〜敵の障害回避〜
例えば(4,4)に障害物がある時、(3,3)(3,5)(5,3)(5,5)から移動する時に(4,4)には移動しないように調整が必要です。
[プX]>[敵X]&[プY]>[敵Y]の時、敵は(3,3)から(4,4)に移動しようとしますが、(4,4)は進めません。
代わりに、(2,4)か(4,2)に進まなければなりません。
(3,5)(5,3)(5,5)でも同様に考えて、↑をちょっと変えてコマ数を入れていくと……

IF[プX]>[敵X]
 IF[プY]>[敵Y]
  IF[敵X]=3
   IF[敵Y]=3
    (2,4)か(4,2)
   ELSE
    [敵X]=[敵X]+1
    [敵Y]=[敵Y]+1
   ENDIF
  ELSE
   [敵X]=[敵X]+1
   [敵Y]=[敵Y]+1
  ENDIF
 ELSE
  IF[敵X]=3
   IF[敵Y]=5
    (2,4)か(4,6)
   ELSE
    [敵X]=[敵X]+1
    [敵Y]=[敵Y]−1
   ENDIF
  ELSE
   [敵X]=[敵X]+1
   [敵Y]=[敵Y]−1
  ENDIF
 ENDIF
ELSE
 IF[プY]>[敵Y]
  IF[敵X]=5
   IF[敵Y]=3
    (4,2)か(6,4)
   ELSE
    [敵X]=[敵X]−1
    [敵Y]=[敵Y]+1
   ENDIF
  ELSE
   [敵X]=[敵X]−1
   [敵Y]=[敵Y]+1
  ENDIF
 ELSE
  IF[敵X]=5
   IF[敵Y]=5
    (4,6)か(6,4)
   ELSE
    [敵X]=[敵X]−1
    [敵Y]=[敵Y]−1
   ENDIF
  ELSE
   [敵X]=[敵X]−1
   [敵Y]=[敵Y]−1
  ENDIF
 ENDIF
ENDIF

長っ。(苦笑)
一つの障害物に対して加えるだけでこの量では、多い・面倒・バグの温床。
なので、「プレイヤー」の改造のように、X、Yに障害物の数値が入った時は弾く、専用ページを作ります。
[敵X][敵Y]に入れる予定の、仮のコマ数を[X][Y]に入れて、それで判断するようにします。
新しいページ「敵止」「敵動」を作って、「敵動」には「敵」の後半の移動作業を切り取り&貼り付けてから改造します。

「敵止」
[止]=0
IF[X]=4
 IF[Y]=4
  [止]=1
 ENDIF
ENDIF
〔サブルーチンから復帰〕

「敵」
IF[プX]>[敵X]
 [X]=[敵X]+1
ELSE
 [X]=[敵X]−1
ENDIF
IF[プY]>[敵Y]
 [Y]=[敵Y]+1
ELSE
 [Y]=[敵Y]−1
ENDIF
サブルーチンコール「敵止」
IF[止]=0
 移動「敵動」
ENDIF

「敵動」
[敵X]=[X](「敵」とは[敵X]と[X]を逆に!)
[敵Y]=[Y]
サブルーチンコール「XY」
〜〜〜

これで、(4,4)に進もうとした時、[止]=1になって、「敵動」には進めず、敵は移動出来ません。
次に、代わりの場所への移動を作ります。
袋小路は無い、という前提で考えると、(袋小路がある時用の改造も出来ますが、マップ全体が一望出来る状態でプレイヤーは袋小路に入らないだろうと考えると、個人的には袋小路の存在そのものの存在に違和感が……)
障害を回避して進む先は2箇所。(3,3)でしたら(2,4)か(4,2)です。
(2,4)は([敵X]−1,[敵Y]+1)、(4,2)は([敵X]+1、[敵Y]−1)。
(3,5)(5,3)(5,5)の場合も書き出していくと、法則性が見えてきます。
(3,3)→(+1,−1)or(−1,+1)
(3,5)→(+1,+1)or(−1,−1)
(5,3)→(−1,−1)or(+1,+1)
(5,5)→(−1,+1)or(+1,−1)
@[プX]>[敵X]の時にXが+1、<の時に−1、[プY]>[敵Y]の時にYが−1、<の時に+1。
A[プX]>[敵X]の時にXが−1、<の時に+1、[プY]>[敵Y]の時にYが+1、<の時に−1。
の2種類の分け方が出来ることがわかります。
ということは、自動移動で障害物にぶつかって[止]=1になった時は、@かAに進むようにすればいいのです。
しかしこの@Aで出した場所がマップから出てしまうところだったり、別の障害物があったりしたら?
それを防ぐ為に、@Aで[X][Y]を作ったら「敵止」で確認し、更に「敵止」にはマップから出てしまう時も[止]=1になるようにしておきます。
@もAも障害物が無い時は、よりプレイヤーに近い方を選びます。どちらを選んでもいい時は、ランダムに決めます。

 実際には、マップによっては入り組んだ一本道などがある場合、この判断だけではカバーしきれません。
 その時はまた、マップごとに調整を加えなければなりません……。
 とりあえず今回は、マップに関係無く、障害物の情報のみで自動的に進む方法を作っています。

@Aを出すページ「@」「A」を作り、「敵止」で確認したかを表す数値変数[確認]を作り、「敵」「敵止」を改造します。

「敵」
〜〜〜
IF[止]=0
 移動「敵動」
ENDIF
[確認]=0
移動「@」

「敵止」
[止]=0
IF[X]=0
 [止]=1
ENDIF
IF[X]=12
 [止]=1
ENDIF
IF[Y]=0
 [止]=1
ENDIF
IF[Y]=16
 [止]=1
ENDIF
IF[X]=4
 〜〜〜
ENDIF
〔サブルーチンから復帰〕

「@」
IF[プX]>[敵X]
 [X]=[敵X]+1
ELSE
 [X]=[敵X]−1
ENDIF
IF[プY]>[敵Y]
 [Y]=[敵Y]−1
ELSE
 [Y]=[敵Y]+1
ENDIF
IF[確認]=0
 サブルーチンコール「敵止」
 IF[止]=0
  [確認]=1
 ENDIF
 移動「A」
ENDIF
移動「敵動」

「A」
IF[プX]>[敵X]
 [X]=[敵X]−1
ELSE
 [X]=[敵X]+1
ENDIF
IF[プY]>[敵Y]
 [Y]=[敵Y]+1
ELSE
 [Y]=[敵Y]−1
ENDIF
IF[確認]≠0
 移動「敵動」
ENDIF
サブルーチンコール「敵止」
IF[止]=1
 移動「@」
ENDIF
[確認]=2
IF[プX]>[敵X]
 [X]=[プX]−[敵X]
ELSE
 [X]=[敵X]−[プX]
ENDIF
IF[プY]>[敵Y]
 [Y]=[プY]−[敵Y]
ELSE
 [Y]=[敵Y]−[プY]
ENDIF
IF[X]>[Y]
 移動「@」
ENDIF
IF[X]<[Y]
 移動「A」
ENDIF
[止]=[@乱数]mod 2(modは左の数を右の数で割った余りを出します。2で割ると余りは必ず0か1)
IF[止]=0
 移動「@」
ENDIF
移動「A」

数値変数[確認]の数値が示す意味は、
0:@Aの確認をまだしていない、または@は[止]=1。
1:@は[止]=0。
2:@もAも[止]=0。
としています。
@×の時はAに行くのが決定なので、@で[確認]=0のままでAに移動し、Aを出して、[確認]=0≠1なので「敵動」へ。
@○A×の時は、@で[確認]=0→1になりAに移動し、Aでは[確認]=1なので「敵止」で確認し、[止]=1で@へ。@では[確認]=1なのですぐ「敵動」へ。
@○A○の時は、@でもAでも[止]=0なので[確認]=2に。プレイヤーとのX、Yの差を求めて、@とAで近い方を選択。@を選んだ時は[確認]=2なのですぐ「敵動」へ。Aを選んだ時も、[確認]=2なので[確認]=2≠1ですぐ「敵動」へ。
@○A○で@Aどちらでもいい時は、ランダムで選択。以下の動作は↑と同様。
何でXの差>Yの差で@に行くんだとか、その辺りは、えーと……ご自分でマップのコピーに書き込んで、検証してみてください。(汗) 分類が多すぎるので……。

これで、敵は障害物を回避しつつ、プレイヤーに近づいて行くことが出来ます。


よく見ると、「敵止」「プ止」のコマンドの並びが被っています。
移動出来ない場合は弾く、という目的は同じ。
そしてマップごとに障害物の情報を入力し、調整する。
なので、一部一緒にしちゃいましょう。
新しいブック『止』を作り、「敵止」をこのブックに移動して、名前は「1」にします。
ブック『止』にページ「止」を作り、マップによって変える数値変数[マップ]を作成。
「1」の一部を「止」に切り取り&貼り付けて改造し、「プ止」を改造します。

「プ止」
サブルーチンコール『止』「止」
IF[止]=1
 〔サブルーチンから復帰〕
ENDIF
移動「XY」

『止』「止」
[止]=0
IF[X]=0
 [止]=1
ENDIF
IF[X]=12
 [止]=1
ENDIF
IF[Y]=0
 [止]=1
ENDIF
IF[Y]=16
 [止]=1
ENDIF
IF[止]=1
 〔サブルーチンから復帰〕
ENDIF
IF[マップ]=1
 移動「1」
ENDIF
IF[マップ]=2
 移動「2」
ENDIF
………

『止』「1」
IF[X]=4
 IF[Y]=4
  [止]=1
 ENDIF
ENDIF
(ここにIF[X]=○、IF[Y]=□、[止]=1、ENDIF、ENDIFを入れて新しい障害物の情報を足す)
〔サブルーチンから復帰〕

後は、「敵」「@」「A」でサブルーチンコール「敵止」をサブルーチンコール『止』「止」に変更します。
追いかけっこを始める前、「プレイヤー」に移動する前に[マップ]=1〜の調整をしておけば、完成です。

これで、『止』の「1」「2」…を調整するだけで、プレイヤーも敵も各マップの障害回避が一挙に出来るようになりました。