ヘビゲーム(基本構成)


まず、初期化の部分を考えます。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
	size = 40			// 自キャラサイズ(縦横共通)
	len  = 5			// 初期連結数
	dim dir, len
	dim posx, len
	dim posy, len
	repeat len
		dir.cnt = 4		// 進行方向:右
		posx.cnt = 6 - cnt	// 初期X位置
		posy.cnt = 2		// 初期Y位置
	loop
とりあえず、マップは内部に障害物なしで、1マス「40×40」ピクセル。 つまり、横幅は「ウィンドウ幅÷40=16マス」で、縦幅は「ウィンドウ高さ÷40=12マス」です。 キャラサイズは「マスサイズ×体の長さ」ということで、 デフォルトサイズは5マス分からでいきましょうか。 「キャラクタの頭と尻尾のマスが同じ」はアリといえばアリですけど、 最初から体のサイズが見てわかるほうがイイので、横に並べることにします。 キャラの進行方向も考えなくてはなりません。 ヘビゲームでのヘビは上下左右4方向のいずれかのみに進むことができるので、 stick命令に合わせて、左(=)上(=)右(=)下(=)の定数値を使用しましょう。 進行方向が例えば右から下に変わった時、頭から尻尾まで全てが下に変わるのではなく、 先頭1マスは下に変わっても、残りの4マスは右のままでなければオカシイですよね。 つまり、キャラは1つですが、進行方向を保持する数は5マス分必要ということになります。 これらをスクリプトにしてみると先に書いたような感じとなります。
 1
 2
	color 200, 255, 255
	boxf
続いて、ステージとなるマップの描画を行います。 マップ内にはとりあえず障害物なし…ということなので、 背景画像をベタ貼りするなり、boxf命令で塗りつぶしたりしましょう。
 1
 2
 3
 4
 5
	color 50, 150, 50
	repeat len
		i = posx.cnt * size, posy.cnt * size
		boxf i.0 + 1, i.1 + 1, i.0 + size - 1, i.1 + size - 1
	loop
ステージを描画した後はキャラクタを描画しましょう。 画像なりboxf命令なりで背景と区別できる色・模様のキャラクタを描画します。 キャラを各マスに配置するわけですが、 if命令で「どこどこのマスにキャラがいれば描画する」と調べていたのでは、 1回につきミリ秒単位程度の時間といえども毎回調べるのに時間がかかりますし、 マップサイズがもっと大きくなればなるほど、スクリプトのサイズが肥大化して、 何より、途中でマップサイズの変更があったりしても対応しにくくなります。 マス(1,0)の座標が(30,0)、マス(2,0)の座標が(60,0)の時、 マス(3,0)の座標は(90,0)になり、マス(N,0)の座標は(30×N,0)ですね。 同様に、マス(0,1)の座標が(0,30)、マス(0,2)の座標が(0,60)の時、 マス(0,3)の座標は(0,90)になり、マス(0,M)の座標は(0,30×M)となりますので、 if命令を使わずともキャラクタのいるマスのみを塗りつぶすことは可能です。 「boxf 横位置×マスサイズ, 縦位置×マスサイズ, (横位置+1)×マスサイズ, (縦位置+1)×マスサイズ
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
	// 先頭以外の進行方向・位置を変更
	repeat len - 1
		dir(len - cnt - 1) = dir(len - cnt - 2)
		posx(len - cnt - 1) = posx(len - cnt - 2)
		posy(len - cnt - 1) = posy(len - cnt - 2)
	loop
	// 先頭の進行方向を変更
	stick key, 5
	if key & 1 : dir = dir / 2 + (dir <= 1) * 8	// 1⇒8 2⇒1 4⇒2 8⇒4
	if key & 4 : dir = dir * 2 - (dir >= 8) * 15	// 1⇒2 2⇒4 4⇒8 8⇒1
	// 先頭の位置を変更
	posx += (dir = 4) - (dir = 1)			// [←] posx-1 [→] posx+1
	posy += (dir = 8) - (dir = 2)			// [↑] posy-1 [↓] posy+1
描画の次は、キャラの移動について考えてみましょう。 進路を右から下に変えたからといって、頭から尻尾までの全てが下に変わるわけではないと先に述べました。
















































































































































上記のように先頭の進路は右(=)から下(=)に変わると、 次の工程で、先頭+1が右(=4)から下(=8)に、その次に先頭+2が右(=4)から下(=8)に変わります。 また、前ターンの先頭位置が、今ターンの先頭+1の位置であり、次ターンの先頭+2の位置となります。 つまり、先頭位置さえどこになるかが決まれば、先頭以外の位置が自動的に決まるということです。 そのため、先に書いたように先頭位置の位置及び方向転換と、 先頭以外の位置及び進路決定をわけて書いているのです。 さて、[]キーで左方向、[]キーで右方向へ方向転換するわけですが、 ソレは、「[]キーで左へ進路を変える」「[]キーで右へ進路を変える」ワケではありませんね。 現在、進路が上のときに、[]キーを押されたら左へ、[]キーを押されたら右へ進みますが、 デフォルトの右方向に進んでいるときには、[]キーを押されたら上へ方向転換し、 []キーを押されたら下へ方向転換しなければなりません。 同様に、左方向に進んでいるときに、[]キーを押されたら下へ方向転換し、 []キーを押されたら右へ方向転換しなければなりません。 つまり、全パターンを挙げると以下のスクリプトのようになるわけですが、 先に書いたスクリプトの9、10行目のように式に当てはめることもできます。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
	// [←]キー
	if key = 1 {
		if dir = 1 : dir = 8	// 左から下に変更
		if dir = 2 : dir = 1	// 上から左に変更
		if dir = 4 : dir = 2	// 右から上に変更
		if dir = 8 : dir = 4	// 下から右に変更
	}
	// [→]キー
	if key = 4 {
		if dir = 1 : dir = 2	// 左から上に変更
		if dir = 2 : dir = 4	// 上から右に変更
		if dir = 4 : dir = 8	// 右から下に変更
		if dir = 8 : dir = 1	// 下から左に変更
	}
12、13行目の先頭位置変更も、式内に=が幾つもあってわかりにくいモノとなってますが、 if命令及び位置変更処理を下記のようにif命令による分岐を行わず、1つの式に収めたモノです。
 1
 2
 3
 4
 5
	// 進行方向へ移動
	if dir = 1 : posx--
	if dir = 2 : posy--
	if dir = 4 : posx++
	if dir = 8 : posy++
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
	// 壁への激突判定
	if posx < 0 | posy < 0 | posx >= ginfo_winx / size | posy >= ginfo_winy / size {
		i = -1 // 激突を示すフラグとして使用
	} else {
		// 自分自身への激突判定
		repeat len - 1
			i = cnt
			repeat len - i - 1, i + 1
				if posx.i = posx.cnt & posy.i = posy.cnt {
					i = -2 // 激突を示すフラグとして使用
					break
				}
			loop
			if i = -2 : break // 激突した
		loop
	}
	if i < 0 {
		if i = -1 : s = "壁" : else : s = "自分"
		dialog s + "に激突!", 1, "ゲームオーバー"
		gosub *init
	}
	return
当たり判定処理を付けてあげればゲームとして成り立ちますので、上記の処理を考えましょう。 まずは、壁とキャラとの接触判定が必要ですね。 今回は障害物がないので、ウィンドウの端を外壁に見立てて、 キャラの横または縦位置がマイナス値になればウィンドウよりも外に描画することになるのでアウト、 またはキャラがウィンドウ横幅または縦幅よりも大きいならウィンドウ外ということでアウト、 ということになりますので、上記スクリプト2行目の判定で壁にぶつかったかを調べることができます。 壁以外には自分自身との当たり判定があるからヘビゲームは難しくなっていくんですよね。 コレのチェック方は、「キャラが同じマスに入っていれば接触した」とみなすことができます。 先頭部分から順に繰り返して同一マスかをチェックしていくだけでOKです。 ただ、例えば「先頭+1と先頭+2が同じマスか」を調べるのは1回だけでイイですよね。 普通に繰り返してチェックする場合、1度チェックしたモノをムダにチェックする形となるので、 1度チェックしたモノは2度見ないよう、チェック範囲を徐々に狭めていきましょう。 壁と激突したときは−1、自分自身と激突したときは−2と分けていますが、 激突時のメッセージ用に分けているだけですので、 どこだろうがぶつかったらアウト、とするだけでよいなら分ける必要は全くありません。 行う処理はこれらだけです。 後は、ひたすら「キーの監視をして、押されたほうに曲がって、それを描画して…」と繰り返すだけ。 ただ、ひたすらスクリプトを書いていくのでは、ハードディスクが何テラバイトあろうと足らないわけですので、 当たり前ですが、繰り返し処理を使って書いてあげましょう。 この書き方ですが、1つのループにドカッと書くと拡張しにくいので、処理ごとに書くほうがベターです。 まとめてみたスクリプトはコチラとなります。