語らいの一時オンライン

HSP3.3でインベーダーを作る!

<第1回>自機を作ってみる!

<第2回>敵を作ってみる!

<第3回>当たり判定とスピード変化処理を作ってみる!

完成版の実行ファイルはこちら

<第1回>自機を作ってみる!

2012/10/10

まずは自機の表示・移動・弾発射までを作ってみる

ゲーム作りの基本の一つと言えば、インベーダーゲームを挙げる方も多いでしょう。
元祖『スペースインベーダー』は社会現象を巻き起こしたゲームなので、コンピューターゲームをよく知らない方でも名前は聞いたことがあるはずです。
様々な亜流を生むほど大ヒットし、シューティングゲームの祖でもあり、その後の業界にも多大な影響を与えた作品です。
今回はそんなインベーダータイプのゲームを作っていきます。

第1回は自機を表示し、カーソルの左右で移動させることが出来、スペースキーで弾を発射することが出来るところまで作成します。
では、早速始めていきましょう!


今回も各行に注釈を入れていますので、分かりにくそうな部分に絞って解説します。
*main処理では開始時に「redraw 0」を入れ、実際の画面への描画をせず、自機と弾を内部処理で描画、その後「redraw 1」で実際の画面への描画を行っています。
今後大量の敵と敵の弾が表示されることになるので、結構画面の書き換えが多くなります。
直接描画処理を行っていては、画面のちらつきが目立つことになるため、このような処理にしています。
弾を撃つ処理は、スペースキーを押した時、画面上に弾が無ければ発射するようにしています。
「stick」命令を使用することで、スペースキーをジョイスティックのボタンのように扱うことが出来ます(トリガータイプと言います)。
スペースキーで弾を発射した後、押しっぱなしにしていても次の弾は発射されません。
プログラムの書き方を説明すると、「stick」命令の後に入力結果を入れる変数「st」を記述し、次の行で「st」が16なら(スペースキーが押されたら)という判定をしています。
もし、「stick」命令を使ってキーの押しっぱなしを検出したい場合には、「stick 変数,非トリガータイプにしたいキー」というように記述します。
「if s=16 & bl=0:bl=1:bx=x+3:by=y-16」という行では、スペースキーが押され、弾が出ていなければ、弾を出し、弾のX座標は自機+3、弾のY座標は自機-16という意味です。
左右に移動する処理では「stick」ではなく「getkey」を使用しています。
「getkey」は押しっぱなしのキーを検出するのに使用します。
左キーが押されたかどうかを変数「left」、右キーが押されたかどうかを変数「right」に格納します。
「x=limit(x,0,winx-16)」という部分は自機のX座標「x」の範囲を指定しています。
ここでは0からwinx-16までを範囲としています。
最後に弾を表示する処理ですが、まず、弾のフラグ「bl」が1なら処理します。
「by-=bsp:if by<-16:bl=0」では、弾のY座標「by」を弾の速度「bsp」ずつ更新し、「by」が-16より小さくなったら(つまり画面の上に消えたら)弾のフラグを0にしています。

		

*init title"Invader"//タイトルの表示 #define winx 320//画面のXサイズ #define winy 240//画面のYサイズ //自機の初期設定 x=153.0:y=220.0//自機の初期位置 bsp=4.0//弾の移動速度 screen 0,winx,winy,0//画面作成 cls 4//画面消去 pos 80,100:color 255,255,255:mes"Push space to start!"//スタート画面 *start stick st//入力設定 if st=16:goto *start2//スペースキーで次へ await//ウェイト goto *start//戻る *start2 cls 4//画面消去 pos 130,100:color 255,255,255:mes"Ready!" await 1000//ウェイト *main redraw 0//画面を描画しない color 0,0,0:boxf 0,0,winx,winy//黒で画面全体を塗り潰す //弾を撃つ stick s//スペースキーが押されているか判定する準備 if s=16 & bl=0:bl=1:bx=x+3:by=y-16//スペースキーが押されていて、画面上に弾がなければ弾が画面上にあるというフラグを1にして、弾の座標を自機の上に設定する //左右に移動する getkey left,37:if left=1:x-=2//カーソルキー左が押されていたら自機を左に移動する getkey right,39:if right=1:x+=2//カーソルキー右が押されていたら自機を右に移動する x=limit(x,0,winx-16)//自機のX座標の範囲を設定する //自機表示 color 255,255,255:pos x,y:mes"凸" //弾のフラグが立っていれば弾を表示する if bl=1:{ color 255,255,255:pos bx,by:mes"|"//弾を表示する by-=bsp:if by<-16:bl=0//弾のY座標を更新し、画面から消えたら弾が画面上にあるというフラグを0にする } await 2//ウェイト redraw 1//画面を描画する goto *main//戻る

次回は敵を配置して、動かしていきます!
お楽しみに♪


HSP参考書色々


メインページへ戻る

<第2回>敵を作ってみる!

2012/10/13

敵と敵の弾の表示を作ってみる


今回は敵を配置し、敵が弾を撃って来るようにします。
敵の数は50匹、敵の弾は画面上に最大4つまでとします。
今回も各行にコメントを入れてありますので、ポイントを絞って解説します。
敵の初期設定のところでは、配列変数を設定しています。
ポイントとしては、X座標を51個用意しているところが挙げられます。
敵は50匹(0番から49番まで)なのですが、敵の移動ルーチン内で、生き残っている敵の中で一番端にいる敵を割り出す為に比較用の変数を作るため、最大数を50ではなく51にしています。
敵の右への移動ルーチンを見てみると、「ex.50=0:rightmax=50」のようにしているのですが、50番(50番目ではありません。0から始まるので順番的には51番目)の敵のX座標を0として、一番右にいるというフラグrightmaxを立てています。
X座標0は当然最も左側なので、この後のプログラムで「if ex.rightmax<ex.j」のように比較すると、この50番の敵は真っ先に一番右ではないと判断され、rightmaxフラグは下ろされます。
これは何の為にやっているのかというと、現在は全ての敵が必ずいるので問題ないのですが、次回当たり判定が実装され敵が消滅した時にはどの敵が生き残っているか分からないという状況になるからです。
例えば、0番の敵や49番の敵が消滅している可能性もあるので、比較する初期値を0番や49番にするわけにはいかないわけです。
当然中間の敵であっても同様です。
この問題を解決するには既存の敵ではないものと比較を開始する必要があるというわけです(あくまで今回の組み方に関しては)。 仮想的に設定した50番の敵との比較が終わってからは、現在生き残っている敵をどんどん比べていって、一番右側にいる敵の番号を割り出します。
そして、「if ex.rightmax>=300:for k,i+1,50:ex.k+=esp:next:di=1:for i,0,50:ey.i+=16:next」という処理で、一番右にいる敵のX座標が300以上になったら、他の敵のX座標も今回の移動分を移動させ、変数diを1にして方向転換、Y座標に16を足してプレイヤーに一段近づけるということをしています。

		

*init title"Invader"//タイトルの表示 #define winx 320//画面のXサイズ #define winy 240//画面のYサイズ //自機の初期設定 x=153.0:y=220.0//自機の初期位置 bsp=4.0//弾の移動速度 //敵の初期設定 dim ex,51//敵のX座標。方向転換用に+1しておく。 dim ey,50//敵のY座標 dim ef,50//敵が生きているフラグ wt=0//ゲームスピード調整用 esp=1//敵の移動速度 di=0//敵の移動方向(0は右に移動する、1は左に移動する) dim ebx,50//敵の弾X座標 dim eby,50//敵の弾Y座標 dim ebf,50//敵の弾フラグ for i,0,50 ex.i=(i\10)*24//敵のX座標 if (i>=0 & i<10):ey.i=16//敵のY座標 if (i>=10 & i<20):ey.i=32//敵のY座標 if (i>=20 & i<30):ey.i=48//敵のY座標 if (i>=30 & i<40):ey.i=64//敵のY座標 if (i>=40 & i<50):ey.i=80//敵のY座標 ef.i=1//敵が生きているフラグ dead.i=0//敵が死んでいて消滅パターンを表示するフラグ d_co.i=0//敵の消滅パターン表示用カウンタ next wtmax=5//ゲームスピード screen 0,winx,winy,0//画面作成 cls 4//画面消去 pos 80,100:color 255,255,255:mes"Push space to start!"//スタート画面 *start stick st//入力設定 if st=16:goto *start2//スペースキーで次へ await//ウェイト goto *start//戻る *start2 cls 4//画面消去 pos 130,100:color 255,255,255:mes"Ready!"//ゲーム開始表示 await 1000//ウェイト *main redraw 0//画面を描画しない color 0,0,0:boxf 0,0,winx,winy//黒で画面全体を塗り潰す //弾を撃つ stick s//スペースキーが押されているか判定する準備 if s=16 & bl=0:bl=1:bx=x+3:by=y-16//スペースキーが押されていて、画面上に弾がなければ弾が画面上にあるというフラグを1にして、弾の座標を自機の上に設定する //左右に移動する getkey left,37:if left=1:x-=2//カーソルキー左が押されていたら自機を左に移動する getkey right,39:if right=1:x+=2//カーソルキー右が押されていたら自機を右に移動する x=limit(x,0,winx-16)//自機のX座標の範囲を設定する //自機表示 color 255,255,255:pos x,y:mes"凸" //弾のフラグが立っていれば弾を表示する if bl=1:{ color 255,255,255:pos bx,by:mes"|"//弾を表示する by-=bsp:if by<-16:bl=0//弾のY座標を更新し、画面から消えたら弾が画面上にあるというフラグを0にする } //ゲームスピード wt+=1:if wt>=wtmax:wt=0 //敵の移動 for i,0,50 //右へ移動する if di=0 & wt=0:{ ex.50=0:rightmax=50//一番右にいる敵の座標を調べる準備 for j,0,50 if ef.j=0:_continue//敵が消滅していたら次の敵 if ex.rightmax=300:for k,i+1,50:ex.k+=esp:next:di=1:for i,0,50:ey.i+=16:next//一番右にいる敵が右端まで行ったら、以降の敵を右に移動し、移動方向を左にして、全ての敵のY座標を更新 } //左へ移動する if di=1 & wt=0:{ ex.50=320:leftmax=50//一番左にいる敵の座標を調べる準備 for j,0,50 if ef.j=0:_continue//敵が消滅していたら次の敵 if ex.j20:pt=1-pt:p_co=0//敵アニメ for i,0,50 if ef.i=1:{//敵が生きていたら color 255,0,0:pos ex.i,ey.i if pt=0:mes"W"//敵表示(パターン0) if pt=1:mes"V"//敵表示(パターン1) } next //敵の弾 eb_count=0//画面上の敵の弾の数を数える準備 for i,0,50 eb_count+=ebf.i//画面上の敵の弾の数を数える next r=rnd(50) if eb_count<4 & ef.r=1 & ebf.r=0:{//敵の弾が4つより少なく、その敵が生きていて弾を発射していない場合 ebf.r=1//敵の弾のフラグを立てる ebx.r=ex.r+7//敵の弾のX座標を設定する eby.r=ey.r+14//敵の弾のY座標を設定する eb_count++//敵の弾のカウントを一つ増やす } for i,0,50 if ebf.i=1:eby.i+=2:if eby.i>320:ebf.i=0//敵の弾のフラグが立っていたらY座標を更新し、画面下に消えたらフラグを下げる if ebf.i=1:color 255,0,0:pos ebx.i,eby.i:mes"|"//敵の弾のフラグが立っていたら表示する next await 2//ウェイト redraw 1//画面を描画する goto *main//戻る

次回は当たり判定を追加し、ゲームスピードや敵の移動スピードに変化を付けゲームとして完成させます!
お楽しみに♪


HSP参考書色々


メインページへ戻る

<第3回>当たり判定とスピード変化処理を作ってみる!

2012/10/14


今回でいよいよインベーダーゲームが完成します。
前回までで自機、自機の弾、敵、敵の弾の表示までが終わりました。
今回は当たり判定の追加、スピード変化処理の追加、スコアの追加などを行い、ゲームとして遊べるものになります。

当たり判定を作ってみる

では、早速当たり判定を付けていきましょう。
当たり判定は二種類あります。
自機の弾が敵に当たる場合と敵の弾が自機に当たる場合です。
まず、敵の弾が自機に当たる場合を見ていきましょう。

for i,0,50
	if ebf.i=0:_continue
	if(abs(x-ebx.i+5)<=7 & abs(y-eby.i)<=14):gosub *gameover_sub:goto *init
next			

ループで0から50まで回しているのは、敵の数だけ調べる必要があるからです。
「ebf.i」というのは敵の弾があるというフラグなので、0の場合は「_continue」で次の弾をチェックします。
次の行の「abs」は絶対値を得る命令です。
自機のX座標「x」と敵の弾のX座標「ebx.i」に5を足したもの、自機のY座標「y」と敵の弾のY座標「eby.i」がそれぞれ7ドット、14ドット以内の距離であれば、ゲームオーバーのサブルーチンに飛んでゲームの初期化を行うということをしています。
このサンプルでは1回のミスでゲームオーバーにしていますが、自機のストックを増やす改造をしてみるのもいいと思います。
続いて、自機の弾が敵に当たる場合を見てみます。

for i,0,50
	if ef.i=0:_continue
	if(abs(ex.i-bx+5)<=7 & abs(ey.i-by)<=14):{
		ef.i=0
		dead.i=1
		bl=0
		by=320
		sc+=100:if sc>hi:hi=sc
		_break
	}
next

0から50までループするのは同じですが、少々複雑になっています。
敵は50匹いるので、それぞれに処理をしてやらなければならないためです。
具体的には、敵が生きているというフラグ「ef.i」を0にする、敵の消滅パターンを表示するフラグ「dead.i」を1にする、自機の弾のフラグ「bl」を0にする、自機の弾のY座標「by」を320にして画面外に設定する、スコアに100を足し、ハイスコア以上であれば現在のスコアをハイスコアにするということを行っています。

スピード変化の処理を作ってみる

このサンプルには二種類のスピードが存在しています。
一つにはゲーム全体のスピード、もう一つはキャラクターのスピードです。
自機のスピードは一定なので、ゲームスピードと敵のスピードについて見ていきます。
まず、ゲームスピードですが、「wt」という変数を用意し、メインループを通る度にプラス1しています。
そして、その値が「wtmax」に達したら0に戻し、その時に敵キャラクターを移動させています。
「wtmax」は「wtmax=cl/8.0」という計算で算出させているので、敵の残りが少なくなるほど小さくなります。
要するに敵が少なくなればなるほどゲームスピードが上がるようにしているわけです。
次にもう一つの敵自体のスピード変化を見てみます。
敵の移動速度は変数「esp」で設定しています。
ゲームのクリア判定用に「cl」というフラグを用意しており、このフラグが0ならクリアになるので、「cl」はそのまま敵の残りの数を表しています。
「esp」は最初1から始まりますが、「if cl<5:esp=2」とすることで、敵の数が5より少なくなった時に2にし、「if cl=1:esp=4」とすることで敵の数が1になった時に4に設定しています。
本家『スペースインベーダー』にも同じような演出がありますね。
		

*init title"Invader"//タイトルの表示 #define winx 320//画面のXサイズ #define winy 240//画面のYサイズ //自機の初期設定 x=153.0:y=220.0//自機の初期位置 bsp=4.0//弾の移動速度 //敵の初期設定 dim ex,51//敵のX座標。方向転換用に+1しておく。 dim ey,50//敵のY座標 dim ef,50//敵が生きているフラグ dim dead,50//敵が死んでいて消滅パターンを表示するフラグ dim d_co,50//敵の消滅パターン表示用カウンタ wt=0//ゲームスピード調整用 esp=1//敵の移動速度 di=0//敵の移動方向(0は右に移動する、1は左に移動する) dim ebx,50//敵の弾X座標 dim eby,50//敵の弾Y座標 dim ebf,50//敵の弾フラグ for i,0,50 ex.i=(i\10)*24//敵のX座標 if (i>=0 & i<10):ey.i=16//敵のY座標 if (i>=10 & i<20):ey.i=32//敵のY座標 if (i>=20 & i<30):ey.i=48//敵のY座標 if (i>=30 & i<40):ey.i=64//敵のY座標 if (i>=40 & i<50):ey.i=80//敵のY座標 ef.i=1//敵が生きているフラグ dead.i=0//敵が死んでいて消滅パターンを表示するフラグ d_co.i=0//敵の消滅パターン表示用カウンタ next //スコア sc=0 screen 0,winx,winy,0//画面作成 cls 4//画面消去 pos 80,100:color 255,255,255:mes"Push space to start!"//スタート画面 *start stick st//入力設定 if st=16:goto *start2//スペースキーで次へ await//ウェイト goto *start//戻る *start2 cls 4//画面消去 pos 130,100:color 255,255,255:mes"Ready!"//ゲーム開始表示 await 1000//ウェイト *main redraw 0//画面を描画しない color 0,0,0:boxf 0,0,winx,winy//黒で画面全体を塗り潰す //弾を撃つ stick s//スペースキーが押されているか判定する準備 if s=16 & bl=0:bl=1:bx=x+3:by=y-16//スペースキーが押されていて、画面上に弾がなければ弾が画面上にあるというフラグを1にして、弾の座標を自機の上に設定する //左右に移動する getkey left,37:if left=1:x-=2//カーソルキー左が押されていたら自機を左に移動する getkey right,39:if right=1:x+=2//カーソルキー右が押されていたら自機を右に移動する x=limit(x,0,winx-16)//自機のX座標の範囲を設定する //自機表示 color 255,255,255:pos x,y:mes"凸" //弾のフラグが立っていれば弾を表示する if bl=1:{ color 255,255,255:pos bx,by:mes"|"//弾を表示する by-=bsp:if by<-16:bl=0//弾のY座標を更新し、画面から消えたら弾が画面上にあるというフラグを0にする } //ゲームスピード wt+=1:if wt>=wtmax:wt=0 //敵の移動 for i,0,50 //右へ移動する if di=0 & wt=0:{ ex.50=0:rightmax=50//一番右にいる敵の座標を調べる準備 for j,0,50 if ef.j=0:_continue//敵が消滅していたら次の敵 if ex.rightmax=300:for k,i+1,50:ex.k+=esp:next:di=1:for i,0,50:ey.i+=16:next//一番右にいる敵が右端まで行ったら、以降の敵を右に移動し、移動方向を左にして、全ての敵のY座標を更新 } //左へ移動する if di=1 & wt=0:{ ex.50=320:leftmax=50//一番左にいる敵の座標を調べる準備 for j,0,50 if ef.j=0:_continue//敵が消滅していたら次の敵 if ex.j20:pt=1-pt:p_co=0//敵アニメ for i,0,50 if ef.i=1:{//敵が生きていたら color 255,0,0:pos ex.i,ey.i if pt=0:mes"W"//敵表示(パターン0) if pt=1:mes"V"//敵表示(パターン1) } if dead.i=1:color 255,0,0:pos ex.i,ey.i:mes"*":d_co.i++:if d_co.i=10:dead.i=0//敵の消滅パターン表示 next //敵の弾 eb_count=0//画面上の敵の弾の数を数える準備 for i,0,50 eb_count+=ebf.i//画面上の敵の弾の数を数える next r=rnd(50) if eb_count<4 & ef.r=1 & ebf.r=0:{//敵の弾が4つより少なく、その敵が生きていて弾を発射していない場合 ebf.r=1//敵の弾のフラグを立てる ebx.r=ex.r+7//敵の弾のX座標を設定する eby.r=ey.r+14//敵の弾のY座標を設定する eb_count++//敵の弾のカウントを一つ増やす } for i,0,50 if ebf.i=1:eby.i+=2:if eby.i>320:ebf.i=0//敵の弾のフラグが立っていたらY座標を更新し、画面下に消えたらフラグを下げる if ebf.i=1:color 255,0,0:pos ebx.i,eby.i:mes"|"//敵の弾のフラグが立っていたら表示する next //スコア表示 color 255,255,255 pos 10,0:mes"SCORE:"+sc pos 120,0:mes"HI-SCORE:"+hi //自機の弾の当たり判定 for i,0,50 if ef.i=0:_continue//敵が消滅していたら次の敵 if(abs(ex.i-bx+5)<=7 & abs(ey.i-by)<=14):{//自機の弾が敵に当たったら ef.i=0 //敵のフラグを0にする dead.i=1 //消滅パターン表示フラグを1にする bl=0 //自機の弾のフラグを0にする by=320 //自機の弾のY座標を画面外に設定 sc+=100:if sc>hi:hi=sc//スコア加算、ハイスコア更新 _break } next //敵の弾の当たり判定 for i,0,50 if ebf.i=0:_continue//敵の弾がなければ次の弾 if(abs(x-ebx.i+5)<=7 & abs(y-eby.i)<=14):gosub *gameover_sub:goto *init//敵の弾が自機に当たったらゲームオーバーサブルーチンに飛び、ゲーム初期化 next //クリア判定 cl=0//クリア判定フラグを初期化 for i,0,50 cl=cl+ef.i//全ての敵の生きているかどうかのフラグを足す next if cl=0:color 0,0,0:boxf 0,0,319,219:pos 130,100:color 255,255,255:mes"Clear!":redraw 1:await 2000:goto *init//敵が全滅していたらクリア表示し、初期設定に戻る wtmax=cl/8.0//敵の残りが少ないほどゲームスピードをアップする if cl<5:esp=2//敵の数が5より少なくなったら敵の移動速度を2にする if cl=1:esp=4//敵の数が1になったら敵の移動速度を4にする //ゲームオーバー判定 for i,0,50 if ef.i=0:_continue//敵が消滅していたら次の敵 if ey.i>=220:gosub *gameover_sub:goto *init//敵が下に達したらゲームオーバー表示し、初期設定に戻る next await 2//ウェイト redraw 1//画面を描画する goto *main//戻る //ゲームオーバーサブ *gameover_sub color 0,0,0:boxf x,y,x+15,y+15 //自機を消去 color 255,255,255:pos x,y:mes"*" //自機の爆発パターンを表示 pos 120,100:color 255,255,255:mes"Game Over!" //ゲームオーバー表示 redraw 1 //画面を描画する await 2000 //ウェイト return

一応の完成ではありますが、UFOを追加してみたり、敵の種類を増やしてみたり色々改造してオリジナルゲームに仕上げてみて下さい!


HSP参考書色々


メインページへ戻る

コンタクト


掲示板

メール

リンク用バナー

バナー
バナー