goo blog サービス終了のお知らせ 

プログラミングのメモ帳(C/C++/HSP)

日々のプログラミングで気づいた点や小技集を紹介します。(Windows 10/XP/Vista、VC2017、HSP)

[HSP]第8回 残機の管理 (シューティング・ゲームのミニ講座)

2013年12月01日 05時28分08秒 | HSP講座

スクリプト言語の 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 *TamaDraw
gosub *TelopDraw
redraw 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: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

変更箇所は敵機が破壊された部分で、ユーザ定義命令の ScoreCalc 命令を引数付きで呼び出してます。
また、今回は敵機1匹当たり 100 ポイントのスコアを加算します。
それからスコア加算のユーザ定義命令も作成しましょう。

#deffunc ScoreCalc int _score_
    Score+=_score_
    if(Score>HiScore):HiScore=Score
    return

上記のように現在スコアが最高スコアを上回った場合は、最高スコアに現在スコアを代入してます。
これで「スコアの加算」は終わりです。

スコアの表示

続いて、スコアの表示を追加しましょう。
この「スコアの表示」も数字で表すか、グラフィックで表すかの2通りありますね。
今回は、数字でスコアを表すことにしましょう。
それでは、残機数を描画する TelopDraw に、現在スコア、最高スコアも一緒に描画します。

*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

どうですか?
このテロップ機能(残機数・スコア・最高スコア)を追加しただけで、以外にもゲームらしくなりましたね。
これで「スコアの表示」は終わりです。

コンティニューの処理

自機の残機が 0 のときに、自機が破壊されるとゲームオーバーとなり、今までは強制的に終了してました。
そこで、ゲームオーバーになった後、ゲームを終了するか、もう一度ゲームをするかのコンティニュー機能を付けてみましょう。
考え方は、次のようにすれば良いでしょう。

  1. 「はい」「いいえ」のダイアログを表示する
  2. 「はい」を選択した場合は、コンティニュー処理(a)~(c)を行う

    1. スコアの初期化(現在スコア)
    2. 自機の初期化(横軸、縦軸、残機数、爆発カウンタ)
    3. 配列の初期化(弾丸、敵機、流星)

  3. 「いいえ」を選択した場合は、プログラムを終了する

それでは、上記のコンティニュー処理をもとに、自機の描画部(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
//------------------------------------------------------------------------------

これで「残機の管理」は終わります。
次はミニ講座の最終回なのでスタート画面を付けた「完成ソース」を紹介します。(次を読む)

←前へ] [目次] [次へ→

コメント    この記事についてブログを書く
  • X
  • Facebookでシェアする
  • はてなブックマークに追加する
  • LINEでシェアする
« [HSP]第7回 流星の表示 (シ... | トップ | [HSP]第9回 完成ソース (シ... »
最新の画像もっと見る

コメントを投稿

HSP講座」カテゴリの最新記事