シューティング2(後半)

前章の続きです。

最初の予定では、自機・敵機共にダメージ制で自機弾に数種のウェポンを持っているとのことでした。

それについてはまた後ほど出てきますのでお楽しみに。

まずは前章最後に書く(ダウンロードできる)予定だったスクリプトを書きます。

#define WX 640
#define WY 480
#define SIZE 25
#define TSIZE 10
#define TMAX 40
#define TSPEED 8
#define MSPEED 5
#define ENMSPEED 10
#define ENMMAX 8
#define STAR 1000
#include "motion.as"
	dim count,1
	dim mx,1
	dim my,1
	dim cartridge,TMAX
	dim tx,TMAX
	dim ty,TMAX
	dim enemy,ENMMAX
	dim enmx,ENMMAX
	dim enmy,ENMMAX
	dim direction,ENMMAX
	dim tm1,30
	dim tm2,30
	mx=WX-SIZE/2
	my=WY-50
	tm1=1,1,1,1,1,2,2,2,2,2,1,1,2,1,2,2,1,1,3,3,1,3,1,3,3,1,2,2,3,4
	tm2.0  =  30,  70, 110, 130, 150, 200, 240, 275, 310, 340
	tm2.10 = 365, 415, 440, 460, 480, 500, 525, 540, 560, 590
	tm2.20 = 600, 620, 650, 675, 700, 730, 740, 750, 760, 850
	randomize
	buffer 2,WX,WY*2
	color 1,1,1 : boxf : color 255,255,255
	repeat STAR
		rnd tmp,WX
		rnd tmp.1,WY
		pset tmp,tmp.1
	loop
	pos 0,WY : gcopy 2,0,0,WX,WY
	buffer 3,256,SIZE*2
	color : boxf
	color ,,255
	font "MS 明朝",10
	pos 0,0 : mes "●"
	color 230,150
	pos 0,TSIZE : mes "●"
	color 200
	font "MS 明朝",SIZE
	pos TSIZE,0 : mes "▲"
	color ,200
	pos SIZE+TSIZE,0 : mes "▼"
	color ,150,150
	pos SIZE*2+TSIZE,0 : mes "▼"
	color 100,100
	pos SIZE*3+TSIZE,0 : mes "▼"
	color 230,230
	font "MS 明朝",SIZE*2
	pos SIZE*4+TSIZE,-5 : mes "▼"
	repeat 256
		color 255-cnt,cnt
		line cnt,SIZE*2-10,cnt,SIZE*2
	loop
	; ---------- ココまでが準備 ----------
	gsel 0
	gmode 2

*main
	redraw 0
	gosub *back
	gosub *body
	gosub *tama
	gosub *action
	gosub *enmgene
	gosub *enmaction
	redraw
	await 10
	count+
	goto *main

*back
	pos 0,0 : gcopy 2,0,-count\WY+WY,WX,WY
	return

*body
	pos mx,my : gcopy 3,TSIZE,0,SIZE,SIZE
	repeat ENMMAX
		if enemy.cnt=1 : pos enmx.cnt,enmy.cnt : gcopy 3,SIZE+TSIZE,0,SIZE,SIZE
		if enemy.cnt=2 : pos enmx.cnt,enmy.cnt : gcopy 3,SIZE*2+TSIZE,0,SIZE,SIZE
		if enemy.cnt=3 : pos enmx.cnt,enmy.cnt : gcopy 3,SIZE*3+TSIZE,0,SIZE,SIZE
		if enemy.cnt=4 : pos enmx.cnt,enmy.cnt : gcopy 3,SIZE*4+TSIZE,0,SIZE*2,SIZE*2-10
	loop
	return

*tama
	repeat TMAX
		if cartridge.cnt=1 {
			pos tx.cnt,ty.cnt : gcopy 3,0,0,TSIZE,TSIZE
			ty.cnt-=8
			if -TSIZE>ty.cnt : cartridge.cnt=0
		}
	loop
	return

*action
	stick key,31,1
	if key&1 : mx-=MSPEED : if mx<0 : mx=0
	if key&2 : my-=MSPEED : if my<0 : my=0
	if key&4 : mx+=MSPEED : if WX-SIZE<mx : mx=WX-SIZE
	if key&8 : my+=MSPEED : if WY-SIZE<my : my=WY-SIZE
	if key&16 : if shottime=0 : shottime=5 : gosub *generation : else : shottime-
	return

*generation
	repeat TMAX
		if cartridge.cnt=0 : cartridge.cnt=1 : tx.cnt=SIZE-TSIZE/2+mx : ty.cnt=my-TSIZE : break
	loop
	return

*enmgene
	if tm3>29 : return
	if count<tm2.tm3|(tm3.1>=ENMMAX) : return
	repeat ENMMAX
		if enemy.cnt=0 {
			enemy.cnt=tm1.tm3
			rnd enmx.cnt,WX/2 : enmx.cnt+=WX/4
			enmy.cnt=0
			rnd direction.cnt,2 : if enemy.cnt=4 : direction.cnt+
			break
		}
	loop
	if tm3<30 : tm3+
	tm3.1+
	return

*enmaction
	repeat ENMMAX
		if enemy.cnt=0 : continue
		if enemy.cnt : motion cnt
		loop
	return

前章飛ばしてこの章から始めようとするといきなり長いスクリプトがコメントなしで来るので

着いてこられなくなっても知りません。素直に前章から始めてくださいね。

また初めの方にモジュールが結合されているのも前章知らない方は分からないことでしょう。

 

さてそれでは上記スクリプトに自機の当たり判定を入れていきましょう。

すべての敵弾とのチェックが必要ですので敵弾数分ループさせ当たり判定をしましょう。

まず準備コメントまでに下記の使用するものを追加してください。

#define ENMTMAX 60 ; 敵弾の最大数
	dim clear,1 ; クリアフラグ
	dim power,1 ; 自機耐久力
	dim enmtinfo,ENMTMAX ; 敵弾情報
	dim enmtx,ENMTMAX ; 敵弾X座標
	dim enmty,ENMTMAX ; 敵弾Y座標
	power=150

次にmain,tama,enmactionラベル内に下記を追加してください。

*main
	 :
	gosub *hitcheck ; 自機との当たり判定
	if clear : goto *gamestop ; ゲームオーバー=-1 , ゲームクリア=1
	 :

; repeat-loop間に追加しても構わないが重くなるのでループ外のほうが断然よい
*tama
	 :
	repeat ENMTMAX ; 敵弾の描画
		if enmtinfo.cnt=0 : continue
		pos enmtx.cnt,enmty.cnt : gcopy 3,0,TSIZE,TSIZE,TSIZE
		switch enmtinfo.cnt
			case 1 : enmty.cnt+=TSPEED : swbreak
			case 2 : enmtx.cnt-=TSPEED*2/3 : enmty.cnt+=TSPEED*2/3 : swbreak
			case 3 : enmtx.cnt+=TSPEED*2/3 : enmty.cnt+=TSPEED*2/3 : swbreak
			case 4 : enmty.cnt-=TSPEED : swbreak
			case 5 : enmtx.cnt-=TSPEED*2/3 : enmty.cnt-=TSPEED*2/3 : swbreak
			case 6 : enmtx.cnt+=TSPEED*2/3 : enmty.cnt-=TSPEED*2/3 : swbreak
			case 7 : enmtx.cnt-=TSPEED : swbreak
			case 8 : enmtx.cnt+=TSPEED
		swend
		if enmtx.cnt<0|(enmty.cnt<0)|(enmtx.cnt>WX)|(enmty.cnt>WY) : enmtinfo.cnt=0 ; 画面外なら消滅
	loop
	 :

*enmaction ; repeat-loop間に追加すること!
	 :
		if enemy.cnt=3&(enmx.cnt-30<mx)&(enmx.cnt+30>mx) { ; 敵機3の弾発射
			rnd tmp,5
			if tmp=0 : tmp.1=1 : tmp.2=enmx.cnt : tmp.3=enmy.cnt : gosub *enmtgene
		}
		if enemy.cnt=4 { ; ボス敵の弾発射
			tmp=cnt
			rnd tmp.1,20
			if tmp.1=0 {
				repeat 8,1
					tmp.1=cnt
					tmp.2=enmx.tmp
					tmp.3=enmy.tmp
					gosub *enmtgene
				loop
			}
		}
	 :

そして最後に当たり判定のメインとなる下記3ラベルを作成します。

*hitcheck ; 当たり判定
	repeat ENMTMAX
		if enmtinfo.cnt=0 : continue
		if mx-TSIZE<enmtx.cnt&(mx+SIZE>enmtx.cnt)&(my-TSIZE<enmty.cnt)&(my+SIZE>enmty.cnt) {
			power-=2
			enmtinfo.cnt=0 ; 弾消滅
		}
	loop
	if power<1 : clear=-1 ; ゲームオーバー
	return

*enmtgene ; 敵弾生成
	repeat ENMTMAX
		if enmtinfo.cnt=0 : enmtinfo.cnt=tmp.1 : enmtx.cnt=tmp.2 : enmty.cnt=tmp.3 : break
	loop
	return

*gamestop ; ゲーム中断
	color : boxf : color 255
	font "",32,1
	if clear=-1 : pos 248,WY-32/2 : mes"GAME OVER" : else : pos 240,WY-32/2 : mes"GAME CLEAR"
	redraw
	stop

追加・変更する箇所が多くてどこを言っているのか分かりにくいですが許してください。

耐久力は作成していくゲームバランスを考えて任意の数値に増減してくださいね。

mainラベルの所で書いていますが、変数clearは-1で死亡、1でクリアとして代入されます。

tamaラベルのswitchについてはもう前章触れてありますのでいいですね。

enmactionラベルで当初予定の敵機3とボスが弾を発射するかというところです。

敵機3の場合は、X座標が自機付近に来た時だけ前方に発射するようになっています。

ボス敵の場合は自機の位置に関わらずどこでも8方向に弾を飛ばしてきます。

弾の移動についてはtamaラベルをご覧ください。

弾は同じですがswitchの条件に当たるenmtinfo.cntに入っている弾情報で進行方向が変わります。

弾の斜め移動は縦横と同じだけ進めると大分早いです。

円形に広げて行くには縦横の大体3分の2くらいがいいのではないでしょうかってことで2倍して3で割ってます。

当たり判定で弾との当たりをチェックをしています。

 

…しかし敵弾とだけでなく敵機本体との当たり判定も必要ですね。

せっかくのダメージ型ですし弾とはダメージを変える(増やす)方向で行きましょうかね。

弾は2ダメージに対し本体は3ダメージ与えるようにしましょう。作っていただく時は自由にしてくださいね。

そうそう。当たり判定をする時に敵の位置と大きさが分からなくてはチェックできません。

大きさは敵によって違う(ボスとザコの2種類だけだが)のでまず大きさを定義しておかなければなりませんね。

縦横の比率が同じでない場合、縦と横それぞれ定義しなければなりませんがここでは同じなのでいいでしょう。

準備コメント内に下記を入れて置いてください。

	dim eSIZE,5 ; 0=敵なし,1=ザコ敵1,2=ザコ敵2,3=ザコ敵3,4=ボス敵
	eSIZE=0,SIZE,SIZE,SIZE,SIZE*2 ; ザコ敵3種とも自機と同じ,ボス敵は自機の2倍

そして当たり判定をするhitcheckラベルのループ外に下記を追加してください。

	repeat ENMMAX
		if enemy.cnt=0 : continue
		tmp=enemy.cnt
		if mx - eSIZE.tmp < enmx.cnt & (mx + SIZE > enmx.cnt) & (my - eSIZE.tmp < enmy.cnt) & (my + SIZE > enmy.cnt) : power -= 3
	loop

 

これで自機に対する当たり判定が付きました。…がどれだけ当たったのか全く分かりません。

あとどれくらい当たっても大丈夫かを示すゲージが表示されている方がよい、ということで

準備ラベルにあらかじめ用意していたゲージを追加します。

どこに追加してもいい(厳密にはどこでもよいわけではない)のですが、

自機との当たり判定直後すぐの方が分かりやすそうということでhitcheckラベルの最後にしましょう。

ゲージは幅256ドットありますので150ダメージで256ドットにするために256÷150をしています。

ようするに1ダメージ1.70666…ドット減るわけですが小数点以下は無視され1ダメージ1ドットです。

そのままだと1×150=150ドット分しか描画されずゲージの緑色部分まで見えません。

精度を上げるためにややこしいのですが初めの256を100倍した25600÷150で1ダメージ170にして、

最後に100で割ることで元の値の精度を上げた結果が求められるわけです。

別に100倍でなく10000倍にして10000で割ったりしてもいいです。精度が上がりますがあまり意味ないかも。

自機耐久力が256以下なので100倍しなくてもゲージが表示されるわけですが256より大きい場合に

1倍で計算していると必ず値は1未満になり小数点は無視され値は0となりゲージが描画されません。

*hitcheck
	 :
	pos 10,WY-20 : gcopy 3,0,SIZE*2-10,25600/150*power/100,10 ; 耐久力ゲージ
	return

 

続いて敵機の当たり判定を入れましょうね。

しかしその前に敵機の耐久力を決めてやらねばなりません。

まずはいつものように準備コメントより前に下記のスクリプトを入れてください。

	dim enmp,ENMMAX ; それぞれの敵機の残り耐久力
	dim epower,5 ; 敵機の初期耐久値
	epower=0,10,20,15,800 ; 別に10発,20発...分というわけではない

上のを入れたら敵生成時(enmgeneラベル)の「if enemy.cnt=0」のカッコ内にソレを設定してあげましょう。

	tmp=tm1.tm3
	enmp.cnt=epower.tmp

後はhitcheckラベルを下記のように書き換えましょう。

	 :
	repeat ENMMAX ; ココのループ内をこの様に書き換える
		tmp=enemy.cnt
		tmp.1=eSIZE.tmp
		tmp=cnt
		repeat TMAX
			if cartridge.cnt {
				if tx.cnt - tmp.1 < enmx.tmp & (tx.cnt + TSIZE > enmx.tmp) {
					if ty.cnt - tmp.1  < enmy.tmp & (ty.cnt + TSIZE  > enmy.tmp) {
						enmp.tmp-=5
						cartridge.cnt=0
					}
				}
			}
		loop
		tmp=enemy.cnt
		if mx - eSIZE.tmp < enmx.cnt & (mx + SIZE > enmx.cnt) & (my - eSIZE.tmp < enmy.cnt) & (my + SIZE > enmy.cnt) : power -= 3
		if enmp.cnt < 1 : tm3.1- : if enemy.cnt=4 : clear=1 : enemy.cnt=0 : else : enemy.cnt=0
	loop
	 :

 

この章初めでも書いたようにウェポンを2種類にするとのことでした。

真っ直ぐだけでなく広範囲に広がり真っ直ぐより与えるダメージは少な目ってことなので

それぞれの弾で与えるダメージも定義しなければなりません。準備コメントまでに

	dim dam,2
	dam=5,3 ; 0=真っ直ぐ弾のダメージ,1=広範囲弾のダメージ

を入れてtamaラベルの一番初めにある「repeat TMAX」のループを下記の様に書き換えてください。

	repeat TMAX
		switch cartridge.cnt
			case 1 : ty.cnt-=speed : swbreak
			case 2 : tx.cnt-=speed*2/3 : ty.cnt-=speed*2/3 : swbreak
			case 3 : ty.cnt-=speed : swbreak
			case 4 : tx.cnt+=speed*2/3 : ty.cnt-=speed*2/3 : swbreak
		swend
		if cartridge.cnt : pos tx.cnt,ty.cnt : gcopy 3,0,0,TSIZE,TSIZE
		if -TSIZE>ty.cnt|(-TSIZE>tx.cnt)|(WX<tx.cnt) : cartridge.cnt=0
	loop
	 :

そしてこのすぐ上でやった敵機と弾の当たり判定でhitcheckラベルを書き換えると書きました。

そこの二重ループ内に「enmp.tmp-=5」とあったのを次のように書き換えます。

				if cartridge.cnt=1 : enmp.tmp-=dam : else : enmp.tmp-=dam.1

後はウェポンの切り替えボタンの設定と切り替えた後弾の生成でどの弾になるかの部分だけです。

別に定義しなくても使えるのでいらないのですが(今までのも)準備コメントに明示的に定義しておきましょう。

	dim wepon,1 ; ウェポン番号(0=前方のみ,1=3WAY)

actionラベルにそのウェポン番号を変更するのを加えておきます。

	if key&32 : wepon=1-wepon ; ENTERキーで変更する

そしてメインの弾の生成部分であるgenerationラベルを下記のように書き換えれば完成です。

*generation
	tmp=2
	repeat TMAX
		if cartridge.cnt : continue
		tx.cnt=SIZE-TSIZE/2+mx : ty.cnt=my-TSIZE
		if wepon=0 : cartridge.cnt=1 : break
		cartridge.cnt=tmp
		tmp+
		if tmp>4 : break
	loop
	return

コレで与えるダメージが前方のみの5ダメージより弱い3WAYで3ダメージのウェポンが出来ました。

こういったアイテムをいろいろ追加していくと面白いですね。

 

…とコレで初めに予定していた縦スクロールのシューティングゲームが完成したわけです。

2,3面と追加していきアイテムを出現させたりとアイデアを取り入れて行ってください。

敵機の動きだけでなく弾の動きもモジュール化してもよろしいかと思います。

基本的な部分はどんどん使いまわしの出来る形にしていった方が楽です。

 

ココで完成したスクリプトの公開をしておきます。参考にご利用ください。コチラからどうぞ。

このままのものに機能追加していってもたいしたものが出来ませんのでいないとは思いますが、

初めにお読みくださいに書いている通り勉強として利用する以外の公開等は控えてください

別のサイトでの掲示板でそのまま公開して聞くということが以前ありましたが

そういう行為もやめてください(誰とは書きませんが)。

 

もし自機と敵機の接触で敵機にもダメージを与えたいのであればpower-=3だけでなく

 
		if mx - eSIZE.tmp < enmx.cnt & (mx + SIZE > enmx.cnt) & (my - eSIZE.tmp < enmy.cnt) & (my + SIZE > enmy.cnt) {
			power -= 3 : enmp.cnt -= 2
		}

としてみることで自機・敵機ともダメージを与える様になりますね。

 

敵機弾や敵機に接触した時、無敵時間がないため一回分のダメージは少なくても大量に食らうこととなります。

無敵時間を設けるにはpowerの減る所で次のようなinvncbl(変数名は何でもよい)を入れておきます。

			if invncbl=0 : power-=2 : invncbl=5

そして変数invncblは当たっていてもいなくても0でない時はターン毎に1ずつ減らしていきます。

つまりヒットの有無に関わらず絶対に通過する場所に下記のように減らすものを追加しておけばイイかと。

	if invncbl : invncbl- ; mainラベル内にでも追加しておけばOK!

コレは自機に対する無敵時間ですが敵機にも無敵時間を付けておかないと不平等です(笑

シューティングの難易度を易しくするには必要ないと思いますけどね。

 

あ、そうそう超重要お知らせがあります。

弾の連続連なりを避けるために変数shottimeにも同じようなトラップを仕掛けていましたが、

弾が発射しているときにショットキーが押されていたら変数shottimeを減らす、としていました。

それでは連打した時、弾が発射されない場合が出てしまいます。

それでは困るので上でやった変数invncblのように

キーが押されていてもいなくてもshottimeが0でないなら減らすようにしなくてはいけません

ダウンロードできるスクリプトでは修正していますがこの章,前章で出てきたものの修正はしていません。

ご迷惑をおかけしたことをお詫び申し上げます。