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

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

[HSP]第9回 完成ソース (シューティング・ゲームのミニ講座)

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

スクリプト言語の HSP については、公式ホームページの「HSPTV!」をどうぞ。


なお、このページはシューティング・ゲームのミニ講座シリーズの1つです。
今回は第9回「完成ソース」について説明します。(戻る)

スタート画面

スタート画面の役割は、次の項目を選択するのが一般的でしょう。

  • ゲームのタイトル表示
  • ゲームの開始
  • ゲームの設定
  • ゲームの説明
  • ゲームの終了

今までゲームを起動すると直ぐにゲームが開始されました。
心の準備もなしにゲームがスタートするのは、ちょっとビックリしますね。
そこで、簡単ながらゲームのスタート画面を付けて完成させましょう。
なお、今回がシューティング・ゲームのミニ講座シリーズの最終回になります。

画面デザイン

今回は簡単なスタート画面を目指してます。
そこで、ゲーム・タイトル、ゲーム開始、ゲーム終了の3要素を付けたいと思います。
さらに[SPC]キーが押されると「ゲームの開始」、[ESC]キーが押されると「ゲームの終了」の処理も行います。
このスタート画面は、次のようになると思います。

  1. スタート画面を表示する(ゲーム・タイトル、ゲーム開始、ゲーム終了)
  2. キー入力のループ待ちで(a)~(c)を行う

    1. stick 命令を実行
    2. [SPC]キーが押されてたら break 命令を実行して、ループを抜ける
    3. [ESC]キーが押されてたら end 命令を実行して、ゲーム終了

  3. 繰り返しを抜ける
  4. ゲームのメインに進む

上記のフローチャートをもとにスタート画面を作成すると次のようになると思います。

*Start
    screen 0,600,400,SCREEN_FIXEDSIZE
    title "シューティング・ゲームのミニ講座"
    redraw 0
    color:boxf
    color $00,$FF,$FF:font "HG明朝E",50:pos (600-50*11)/2,100:mes "シューティング・ゲーム"
    color $FF,$FF,$00:font "HG明朝E",30:pos (600-30* 6)/2,180:mes "~ミニ講座~"
    color $FF,$FF,$FF:font "HG明朝E",16:pos (600- 8*15)/2,250:mes "[SPC]キーで開始"
    color $FF,$FF,$FF:font "HG明朝E",16:pos (600- 8*15)/2,282:mes "[ESC]キーで終了"
    redraw 1
    repeat
        stick key
        if(key & $10):break
        if(key & $80):end
        await 20
    loop
*Main

どうですか?
すごくシンプルなスタート画面ですが、よりゲームらしくなりましたね。

上記の (key & $10) が[SPC]キーが押されてるかのチェックです。
押されていたら break 命令を実行して繰り返しを抜けます。
その後は *Main ラベルに進んでゲームが開始されます。

上記の (key & $20) は[ESC]キーが押されてるかのチェックです。
押されていたら end 命令を実行してウインドウが閉じます。
つまり、ゲームの終了を選択したことになります。

完成ソース

それでは第9回目の「完成ソース」を紹介します。

//------------------------------------------------------------------------------
// シューティング・ゲームのミニ講座 for HSP(Ver.3.3.2)
//==============================================================================
// 第9回「完成ソース」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
*Start
    screen 0,600,400,SCREEN_FIXEDSIZE
    title "シューティング・ゲームのミニ講座"
    redraw 0
    color:boxf
    color $00,$FF,$FF:font "HG明朝E",50:pos (600-50*11)/2,100:mes "シューティング・ゲーム"
    color $FF,$FF,$00:font "HG明朝E",30:pos (600-30* 6)/2,180:mes "~ミニ講座~"
    color $FF,$FF,$FF:font "HG明朝E",16:pos (600- 8*15)/2,250:mes "[SPC]キーで開始"
    color $FF,$FF,$FF:font "HG明朝E",16:pos (600- 8*15)/2,282:mes "[ESC]キーで終了"
    redraw 1
    repeat
        stick key
        if(key & $10):break
        if(key & $80):end
        await 20
    loop
*Main
    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-9.hsp
//------------------------------------------------------------------------------

どうですか?
全部で 257 行になりました。
コメント行が多いので実際には 202 行ですが…。

変数・配列の一覧

以下にミニ講座で使用してる変数と配列の一覧を表示します。
変数が 12 個、配列が 10 個の合計で 22 個です。
簡単なシューティング・ゲームですが 22 個も変数を使ってますね。

これは、多い方なのでしょうか、それとも少ない方なのでしょうか。
2次元配列にすれば、配列の個数を減らせるという噂もあります。
この方法は、また別の機会にでも紹介しましょう。

名前 意味
スコアの管理
dim Score現在スコア
dim HiScore最高スコア
自機の管理
dim x自機の横軸
dim y自機の縦軸
dim fight自機の残機数
dim blast自機の爆発カウンタ
弾丸の管理
dim tamaF,10有無フラグ
dim tamaX,10弾丸の横軸
dim tamaY,10弾丸の縦軸
dim tamaTrigg弾丸の発生間隔
敵機の管理
dim enemyF,20有無フラグ
dim enemyX,20敵機の横軸
dim enemyY,20敵機の縦軸
dim enemyZ,20敵機の爆発カウンタ
dim enemyCycle敵機の発生間隔
流星の管理
dim starF,50有無フラグ
dim starX,50流星の横軸
dim starY,50流星の縦軸
dim starCycle流星の発生間隔
作業用の変数
dim keyキー入力
dim n整数型の一時変数
sdim msg文字型の一時変数

サブルーチン(命令,関数)の一覧

続いてミニ講座で使用してるサブルーチン(命令,関数)の一覧を表示します。
サブルーチンが 11 個、ユーザ定義命令が 1 個、ユーザ定義関数が 2 個の合計 14 個です。
簡単なシューティング・ゲームですが 14 個もサブルーチンなどを使ってます。

これは、多い方なのでしょうか、それとも少ない方なのでしょうか。
今回、移動と描画を一体化してるので、サブルーチンの数は少ない方だと思います。
なお、サブルーチンの個数は、分かりやすい程度に分割して決めます。
多くても少なくてもシューティング・ゲームのソース・コードを管理しづらくなります。

また、ミニ講座なので1つの巨大なファイルにしました。
本格的なシューティング・ゲーム(パワーアップ型)にする場合は、複数のファイルに分割して1本のゲームを作成します。
この方法は、また別の機会にでも紹介しましょう。

名前 処理
メイン部
*Init配列の確保、スコアの初期化、自機の初期化
*Startスタート画面の処理
*Mainゲームループの処理
自機の処理
*FightDraw自機の移動、描画、爆発、ゲームオーバー、コンティニュー処理
 FightCrash(x,y)自機の衝突判定
流星の処理
*StarBirth流星の発生
*StarDraw流星の移動、描画
弾丸の処理
*TamaBirth弾丸の発生
*TamaDraw弾丸の移動、描画
 TamaCrash(x,y)弾丸の衝突判定
敵機の処理
*EnemyBirth敵機の発生
*EnemyDraw敵機の移動、描画、爆発、当たり判定
その他
*TelopDrawスコア、最高スコア、残機の描画
 ScoreCalc scoreスコアの加算

上記のサブルーチン(命令,関数)の見分け方は、次のようになります。

  • 「*」マークがあるのがサブルーチン
  • 引数が付いてるのがユーザ定義命令(ScoreCalc)
  • カッコがあるのがユーザ定義関数(FightCrash、TamaCrash)

おわりに

今回は、シューティング・ゲームのミニ講座なので、敵機弾、アイテムは登場しませんでした。
また、文字ベースで自機、弾丸、敵機、爆風などを描いてるため見栄えは良くありませんでしたね。
しかし、シューティング・ゲームの全体的な仕組みは理解できたと思います。
最初から見た目のデザイン、ゲームの操作性、ボス・キャラ、ステージ・クリアを考えると「」がパンクすると思います。
そこでワザと文字ベースで自機、弾丸、敵機、爆風だけを登場させました。

今後の予定

パワーアップ型の「シューティング・ゲームの基礎講座」を書こうと思ってます。
こちらは、電子書籍(有料)とブログ(無料)の両方でネット上に紹介するつもりです。

ただし、内容は異なり、ブログ版では、配列を使った方法で、約50回ほどで紹介する予定です。
そして、電子書籍では、ファイル分割、モジュール利用、モジュール型変数を使った方法で紹介する予定です。
あと電子書籍は、1冊200円で前編・中編・後編の三部作を考えてます。

それでは、これでシューティング・ゲームのミニ講座は終わります。

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

コメント (2)   この記事についてブログを書く
« [HSP]第8回 残機の管理 (シ... | トップ | [HSP]第10回 影付き文字の... »
最新の画像もっと見る

2 コメント

コメント日が  古い順  |   新しい順
ありがとうございます (Knitter)
2018-05-16 22:47:48
綺麗なソースで、ゲームの基本的な書き方がよくわかりました。
ありがとうございます。
игра бесплатно без онлайн (Elmerbob)
2020-07-26 19:03:06
фильм игра смотреть онлайн бесплатно в хорошем http://chilp.it/154d7b1

コメントを投稿

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