「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」…を調整するだけで、プレイヤーも敵も各マップの障害回避が一挙に出来るようになりました。
|
|