スクリプト言語の HSP については、公式ホームページの「HSPTV!」をどうぞ。
なお、このページはシューティング・ゲームのミニ講座シリーズの1つです。
今回は第8回「残機の管理」について説明します。(戻る)
残機の管理
残機の管理とは、自機が敵機と衝突して、破壊されたときに1つ残機を失います。
そして、残機数が 0 のときに敵機と衝突して破壊されるとゲームオーバーになるのが一般的でしょう。
まずは、自機の管理変数を見てみましょう。
dim x dim y dim fight dim blast
上記の4つの変数は次の意味があります。
- x は、自機の横軸
- y は、自機の縦軸
- fight は、自機の残機数
- blast は、自機の爆発カウンタ
残機数の fight は、既に FightDraw 内で記述してます。
しかし、初期化してないためゲーム開始時は、残機数の fight は 0 ですね。
このままだと自機が敵機と衝突して、破壊されると直ぐにゲームオーバーになってました。
そこで、まず最初にゲーム開始時の自機の初期化を追加しましょう。
自機の初期化
自機の初期化は、次のようにすれば良いでしょう。
*Init ;自機の管理 ; ;省略 ;;自機の初期化 x=(600-50)/2 y=(400-50)-16 fight=3 blast=0*Main
上記の x、y にゲーム開始時の自機の座標をセットしてます。
今までは、両方とも 0 だったので左上隅に自機が表示されました。
そのため場合によっては、敵機と衝突して直ぐにゲームオーバーになってましたね。
これを防ぐには、ゲーム開始時に画面下部の中央に表示されるようにセットします。
画面の横幅である 600 ドットから自機の横幅の 50 ドットを引いて、2で割れば画面中央の座標が求まります。
画面の縦幅である 400 ドットから自機の縦幅の 50 ドットを引いて、さらに下部の隙間である 16 ドットを引いてます。
これにより、画面下部から 16 ドット上に自機が表示されます。
あとは残機数の fight に 3 をセットして、爆発カウンタの blast は 0 をセットします。
これで「自機の初期化」は終わりですね。
残機の表示
続いて、残機の表示を行いましょう。
この「残機の表示」は数字で表すか、マークで表すかの2通りありますね。
今回は、マークで残機を表すことにしましょう。
また、マークといっても色々あります。
自機マークとか、♥マークとかがあります。
今回は、自機マークの「山」文字で表すことにしました。
それでは、残機数を描画する TelopDraw を作成します。
*TelopDraw msg="" repeat fight msg+="山" loop font MSGOTHIC,20,1 color $00,$FF,$00:pos 0,(400-20):mes msg font MSGOTHIC,50 return
上記のように msg 変数に、自機マークである「山」文字を fight 個数分だけ連結します。
この自機マーク文字列を画面左下隅の描画します。
このとき、文字のフォント・サイズを変更してます。
さらにゲームループでも TelopDraw を呼び出す必要があります。
*Main screen 0,600,400,SCREEN_FIXEDSIZE font MSGOTHIC,50 randomize repeat redraw 0 stick key,%11111 gosub *EnemyBirth gosub *StarBirth gosub *StarDraw gosub *FightDraw gosub *EnemyDraw gosub *TamaDrawgosub *TelopDrawredraw 1 await (1000/60) loop stop*TelopDraw ; ;記述 ; return上記のように TelopDraw を呼び出す場所は、描画ルーチンの最後です。
このようにすると、画面消去→流星→自機→敵機→弾丸→テロップの順に描画されて、テロップが一番上に表示されます。
この順番を間違うとテロップ文字の上に自機や敵機が移動してしまい不自然になると思います。
これで「残機の描画」は終わりです。スコアの管理
スコアの管理は、プレイヤーの現在スコアと最高スコアの2つを用意すれば良いでしょう。
まずはスコアの管理変数を見てみましょう。dim Score dim HiScore何とも分かりやすい変数名です。
ちょっと当たり前すぎましたね。(笑)
これを初期化部に追加しますよ。*Init;スコアの管理 dim Score dim HiScore;自機の管理 ; ;省略 ;;スコアの初期化 Score=0 HiScore=9900;自機の初期化 x=(600-50)/2 y=(400-50)-16 fight=3 blast=0 *Mainスコアの加算
続いて、スコアを加算する場所を考えます。
今回は、敵機が1種類しか登場しませんので、敵機が破壊された場所に直接スコア加算しましょう。
改良する部分は EnemyDraw サブルーチンです。*EnemyDraw foreach enemyF if enemyF(cnt){ if(enemyZ(cnt)==0){ enemyY(cnt)+=3:if(enemyY(cnt)>400) :enemyF(cnt)=0:continueif FightCrash(enemyX(cnt),enemyY(cnt)) :enemyZ(cnt)=60:ScoreCalc 100:continue if TamaCrash(enemyX(cnt),enemyY(cnt)) :enemyZ(cnt)=60:ScoreCalc 100:continuepos enemyX(cnt),enemyY(cnt) color $00,$FF,$FF:mes "Ж" } else:if(enemyZ(cnt)>1){ enemyZ(cnt)-- color $FF,$00,$00:pos enemyX(cnt),enemyY(cnt) if(enemyZ(cnt)\10<5):mes "※":else:mes "*" } else{ enemyF(cnt)=0 } } loop return変更箇所は敵機が破壊された部分で、ユーザ定義命令の ScoreCalc 命令を引数付きで呼び出してます。
また、今回は敵機1匹当たり 100 ポイントのスコアを加算します。
それからスコア加算のユーザ定義命令も作成しましょう。#deffunc ScoreCalc int _score_ Score+=_score_ if(Score>HiScore):HiScore=Score return上記のように現在スコアが最高スコアを上回った場合は、最高スコアに現在スコアを代入してます。
これで「スコアの加算」は終わりです。スコアの表示
続いて、スコアの表示を追加しましょう。
この「スコアの表示」も数字で表すか、グラフィックで表すかの2通りありますね。
今回は、数字でスコアを表すことにしましょう。
それでは、残機数を描画する TelopDraw に、現在スコア、最高スコアも一緒に描画します。*TelopDraw msg="" repeat fight msg+="山" loopy(1)=0:x(1)=(20) y(2)=0:x(2)=(600-20)-(16*11) x(3)=0:y(3)=(400-20)font MSGOTHIC,20,1color $FF,$FF,$00:pos x(1),y(1):mes strf("Score:%08d",Score) color $FF,$FF,$00:pos x(2),y(2):mes strf("HiScore:%08d",HiScore) color $00,$FF,$00:pos x(3),y(3):mes msgfont MSGOTHIC,50 returnどうですか?
このテロップ機能(残機数・スコア・最高スコア)を追加しただけで、以外にもゲームらしくなりましたね。
これで「スコアの表示」は終わりです。コンティニューの処理
自機の残機が 0 のときに、自機が破壊されるとゲームオーバーとなり、今までは強制的に終了してました。
そこで、ゲームオーバーになった後、ゲームを終了するか、もう一度ゲームをするかのコンティニュー機能を付けてみましょう。
考え方は、次のようにすれば良いでしょう。
- 「はい」「いいえ」のダイアログを表示する
- 「はい」を選択した場合は、コンティニュー処理(a)~(c)を行う
- スコアの初期化(現在スコア)
- 自機の初期化(横軸、縦軸、残機数、爆発カウンタ)
- 配列の初期化(弾丸、敵機、流星)
- 「いいえ」を選択した場合は、プログラムを終了する
それでは、上記のコンティニュー処理をもとに、自機の描画部(FightDraw)を改良してみましょう。
*FightDraw if(blast==0){ if(key&1):x-=5:if(x<0):x=0 if(key&2):y-=5:if(y<0):y=0 if(key&4):x+=5:if(x>550):x=550 if(key&8):y+=5:if(y>350):y=350 if(key&16):gosub *TamaBirth color $00,$FF,$00:pos x,y:mes "山" } else:if(blast>1){ blast-- color $FF,$00,$00:pos x,y if(blast\10<5):mes "※":else:mes "*" } else:if(fight){ fight-- blast=0 x=(600-50)/2 y=(400-50)-16 } else{dialog "もう一度、ゲームを行いますか?",3,"ゲームオーバー" if(stat==7):end ;スコアの初期化 Score=0 ;自機の初期化 x=(600-50)/2 y=(400-50)-16 fight=3 blast=0 ;配列の初期化 dim starF,50 dim tamaF,10 dim enemyF,20} returnこれで「コンティニュー機能」は一通り終わりです。
残機の管理
それでは第8回目の「残機の管理」ソースを紹介します。
//------------------------------------------------------------------------------ // シューティング・ゲームのミニ講座 for HSP(Ver.3.3.2) //============================================================================== // 第8回「残機の管理」by 科学太郎 //------------------------------------------------------------------------------ //-------------------------------------- // メイン部 //-------------------------------------- *Init ;スコアの管理 dim Score ;現在スコア dim HiScore ;最高スコア ;自機の管理 dim x ;自機の横軸 dim y ;自機の縦軸 dim fight ;自機の残機数 dim blast ;自機の爆発カウンタ ;弾丸の管理 dim tamaF,10 ;有無フラグ dim tamaX,10 ;弾丸の横軸 dim tamaY,10 ;弾丸の縦軸 ;敵機の管理 dim enemyF,20 ;有無フラグ dim enemyX,20 ;敵機の横軸 dim enemyY,20 ;敵機の縦軸 dim enemyZ,20 ;敵機の爆発カウンタ ;流星の管理 dim starF,50 ;有無フラグ dim starX,50 ;流星の横軸 dim starY,50 ;流星の縦軸 ;スコアの初期化 Score=0 HiScore=9900 ;自機の初期化 x=(600-50)/2 y=(400-50)-16 fight=3 blast=0 *Main screen 0,600,400,SCREEN_FIXEDSIZE font MSGOTHIC,50 randomize repeat redraw 0 stick key,%11111 gosub *EnemyBirth gosub *StarBirth gosub *StarDraw gosub *FightDraw gosub *EnemyDraw gosub *TamaDraw gosub *TelopDraw redraw 1 await (1000/60) loop stop //-------------------------------------- // 自機の描画 //-------------------------------------- *FightDraw if(blast==0){ if(key&1):x-=5:if(x<0):x=0 if(key&2):y-=5:if(y<0):y=0 if(key&4):x+=5:if(x>550):x=550 if(key&8):y+=5:if(y>350):y=350 if(key&16):gosub *TamaBirth color $00,$FF,$00:pos x,y:mes "山" } else:if(blast>1){ blast-- color $FF,$00,$00:pos x,y if(blast\10<5):mes "※":else:mes "*" } else:if(fight){ fight-- blast=0 x=(600-50)/2 y=(400-50)-16 } else{ dialog "もう一度、ゲームを行いますか?",3,"ゲームオーバー" if(stat==7):end ;スコアの初期化 Score=0 ;自機の初期化 x=(600-50)/2 y=(400-50)-16 fight=3 blast=0 ;配列の初期化 dim starF,50 dim tamaF,10 dim enemyF,20 } return //-------------------------------------- // 流星の発生 //-------------------------------------- *StarBirth if(starCycle):starCycle--:return foreach starF if(starF(cnt)==0){ starF(cnt)=1 starX(cnt)=rnd(600) starY(cnt)=0 break } loop starCycle=3 return //-------------------------------------- // 流星の描画 //-------------------------------------- *StarDraw color $00,$00,$00:boxf foreach starF if starF(cnt){ starY(cnt)+=2:if(starY(cnt)>=400):starF(cnt)=0:continue color $FF,$FF,$00 pset starX(cnt),starY(cnt) } loop return //-------------------------------------- // 弾丸の発生 //-------------------------------------- *TamaBirth if(tamaTrigg):tamaTrigg--:return foreach tamaF if(tamaF(cnt)==0){ tamaF(cnt)=1 tamaX(cnt)=x tamaY(cnt)=y break } loop tamaTrigg=8 return //-------------------------------------- // 弾丸の描画 //-------------------------------------- *TamaDraw foreach tamaF if tamaF(cnt){ tamaY(cnt)-=8:if(tamaY(cnt)<-50):tamaF(cnt)=0:continue pos tamaX(cnt),tamaY(cnt) color $FF,$FF,$00:mes ":" } loop return //-------------------------------------- // 敵機の発生 //-------------------------------------- *EnemyBirth if(enemyCycle):enemyCycle--:return foreach enemyF if(enemyF(cnt)==0){ enemyF(cnt)=1 enemyX(cnt)=rnd(600/50)*50 enemyY(cnt)=-50 enemyZ(cnt)=0 break } loop enemyCycle=30 return //-------------------------------------- // 敵機の描画 //-------------------------------------- *EnemyDraw foreach enemyF if enemyF(cnt){ if(enemyZ(cnt)==0){ enemyY(cnt)+=3:if(enemyY(cnt)>400) :enemyF(cnt)=0:continue if FightCrash(enemyX(cnt),enemyY(cnt)) :enemyZ(cnt)=60:ScoreCalc 100:continue if TamaCrash(enemyX(cnt),enemyY(cnt)) :enemyZ(cnt)=60:ScoreCalc 100:continue pos enemyX(cnt),enemyY(cnt) color $00,$FF,$FF:mes "Ж" } else:if(enemyZ(cnt)>1){ enemyZ(cnt)-- color $FF,$00,$00:pos enemyX(cnt),enemyY(cnt) if(enemyZ(cnt)\10<5):mes "※":else:mes "*" } else{ enemyF(cnt)=0 } } loop return //-------------------------------------- // テロップの描画 //-------------------------------------- *TelopDraw msg="" repeat fight msg+="山" loop y(1)=0:x(1)=(20) y(2)=0:x(2)=(600-20)-(16*11) x(3)=0:y(3)=(400-20) font MSGOTHIC,20,1 color $FF,$FF,$00:pos x(1),y(1):mes strf("Score:%08d",Score) color $FF,$FF,$00:pos x(2),y(2):mes strf("HiScore:%08d",HiScore) color $00,$FF,$00:pos x(3),y(3):mes msg font MSGOTHIC,50 return //-------------------------------------- // スコアの加算 //-------------------------------------- #deffunc ScoreCalc int _score_ Score+=_score_ if(Score>HiScore):HiScore=Score return //-------------------------------------- // 自機の衝突判定 //-------------------------------------- #defcfunc FightCrash int _x_,int _y_ if(blast==0)and(abs(x-_x_)<50)and(abs(y-_y_)<50){ blast=60 return 1 } return 0 //-------------------------------------- // 自機弾の衝突判定 //-------------------------------------- #defcfunc TamaCrash int _x_,int _y_ n=0 foreach tamaF if tamaF(cnt){ if(abs(tamaX(cnt)-_x_)<50)and(abs(tamaY(cnt)-_y_)<50){ tamaF(cnt)=0 n=1 break } } loop return n //------------------------------------------------------------------------------ // End of lesson-8.hsp //------------------------------------------------------------------------------これで「残機の管理」は終わります。
[←前へ] [目次] [次へ→]
次はミニ講座の最終回なのでスタート画面を付けた「完成ソース」を紹介します。(次を読む)
※コメント投稿者のブログIDはブログ作成者のみに通知されます