自キャラから弾を出す

シューティングゲームのように、自キャラから弾を発射する方法を紹介する。

前項までのプログラム画像ファイルデータファイル

一発だけ弾を発射する

ゲーム画面でスペースキーを押したとき、弾を発射するようプログラムを考える。弾は1発しか発射できず、画面から消えるまで次の弾を発射することはできないものとする(インベーダーのイメージ)。

自ショット用画像ファイルをダウンロードし、ソースファイルと同じフォルダに配置しておくこと。

1.自弾用テクスチャ・オブジェクトの準備

《NKC_DGraphics.cpp》

//=============================================================================
//  DirectX Graphics関係の自作関数群
//  Copyright NKC Game Staff(←自分の名前) 
//-----------------------------------------------------------------------------
#include "NKC_Common.h"

// グローバル変数
/* 他のソースからも利用されるもの */
LPDIRECT3DDEVICE8 gl_lpD3ddev = NULL;   // Direct3DDevice8インターフェイス
LPDIRECT3DTEXTURE8 gl_TXBack = NULL;    // 背景用テクスチャ
LPDIRECT3DTEXTURE8 gl_TXMenu = NULL;    // メニュー用テクスチャ
LPDIRECT3DTEXTURE8 gl_TXCursor = NULL;  // メニュー選択カーソル用テクスチャ
LPDIRECT3DTEXTURE8 gl_TXMyChara = NULL; // 自機用テクスチャ
LPDIRECT3DTEXTURE8 gl_TXMyShot = NULL;  // 自弾用テクスチャ
LPDIRECT3DTEXTURE8 gl_TXEnemy[ENEMYBMPMAX] = {NULL,NULL,NULL}; // 敵用テクスチャ
/* 自ソースでのみ利用するもの */
static LPDIRECT3D8 gl_lpD3d = NULL;     // Direct3D8インターフェイス
static D3DPRESENT_PARAMETERS gl_d3dpp;  // ディスプレイパラメータ
・
・
・
//-----------------------------------------------------------------------------
// 関数名 : CreateGameTexture()
// 機能概要: ゲーム画面用テクスチャの作成・再生成
//-----------------------------------------------------------------------------
void CreateGameTexture(void)
{
    HRESULT hr;
    char* szEnemyImg[] = {"enemy0.bmp","enemy1.bmp","enemy2.bmp"}; // 敵キャラファイル名
    int i;
    char buff[80];

    // 自機用テクスチャを生成
    hr = D3DXCreateTextureFromFileEx(
        gl_lpD3ddev,
        "mychara.bmp",                 // ファイル名
        0, 0, 0, 0,
        D3DFMT_A1R5G5B5,                // 色抜きを可能に
        D3DPOOL_MANAGED,
        D3DX_FILTER_LINEAR,
        D3DX_FILTER_LINEAR,
        D3DCOLOR_ARGB(255, 255, 255, 255),// 白色を透過色とする
        NULL,
        NULL,
        &gl_TXMyChara                   // テクスチャ名
    );
    if ( FAILED(hr) ) {
        MessageBox(hWnd, "mychara.bmp をテクスチャとして読み込めませんでした", "ERROR", MB_OK);
        return;
    }

    // 自弾用テクスチャを生成
    hr = D3DXCreateTextureFromFileEx(
        gl_lpD3ddev,
        "myshot.bmp",                     // ファイル名
        0, 0, 0, 0,
        D3DFMT_A1R5G5B5,                  // 色抜きを可能に
        D3DPOOL_MANAGED,
        D3DX_FILTER_LINEAR,
        D3DX_FILTER_LINEAR,
        D3DCOLOR_ARGB(255, 21, 141, 91),  // 透過色を指定
        NULL,
        NULL,
        &gl_TXMyShot                      // テクスチャ名
    );
    if ( FAILED(hr) ) {
        MessageBox(hWnd, "myshot.bmp をテクスチャとして読み込めませんでした", "ERROR", MB_OK);
        return;
    }

    // 敵用テクスチャを生成
    for ( i=0 ; i<ENEMYBMPMAX ; i++ ) {
        hr = D3DXCreateTextureFromFileEx(
            gl_lpD3ddev,
            szEnemyImg[i],                  // ファイル名
            0, 0, 0, 0,
            D3DFMT_A1R5G5B5,                // 色抜きを可能に
            D3DPOOL_MANAGED,
            D3DX_FILTER_LINEAR,
            D3DX_FILTER_LINEAR,
            D3DCOLOR_ARGB(255, 21, 141, 91),// 透過色を指定
            NULL,
            NULL,
            &gl_TXEnemy[i]                  // テクスチャ名
        );
        if ( FAILED(hr) ) {
            wsprintf(buff, "%s をテクスチャとして読み込めませんでした", szEnemyImg[i]);
            MessageBox(hWnd, buff, "ERROR", MB_OK);
            return;
        }
    }

}

//-----------------------------------------------------------------------------
// 関数名 : ReleaseGameTexture()
// 機能概要: ゲーム画面用テクスチャ開放処理
//-----------------------------------------------------------------------------
void ReleaseGameTexture(void)
{
    int i;

    // テクスチャの開放
    RELEASE(gl_TXBack); // 背景
    RELEASE(gl_TXMyChara); // 自キャラ
    RELEASE(gl_TXMyShot); // 自弾
    for ( i=0 ; i<ENEMYBMPMAX ; i++ ) RELEASE(gl_TXEnemy[i]); // 敵キャラ
}

《NKC_DGraphics.h》

//-----------------------------------------------------------------------------
// File: myDGraphics.h
// Desc: myDGraphics.cppで使用するもののうち、他のソースでも使うものを宣言
//-----------------------------------------------------------------------------
・
・
・
//-----------------------------------------------------------------------------
// グローバル変数
//-----------------------------------------------------------------------------
extern LPDIRECT3DDEVICE8 gl_lpD3ddev;   // Direct3DDevice8インターフェイス
extern LPDIRECT3DTEXTURE8 gl_TXBack;    // 背景用テクスチャ
extern LPDIRECT3DTEXTURE8 gl_TXMenu;    // メニュー用テクスチャ
extern LPDIRECT3DTEXTURE8 gl_TXCursor;  // メニュー選択カーソル用テクスチャ
extern LPDIRECT3DTEXTURE8 gl_TXMyChara; // 自機用テクスチャ
extern LPDIRECT3DTEXTURE8 gl_TXMyShot;  // 自弾用テクスチャ
extern LPDIRECT3DTEXTURE8 gl_TXEnemy[ENEMYBMPMAX]; // 敵用テクスチャ
・
・
・

2.自弾用変数の宣言・初期化(Game.cpp)

//=============================================================================
//  ゲーム処理関係の自作関数群
//  Copyright NKC Game Staff(←自分の名前) 
//-----------------------------------------------------------------------------
・
・
・
// グローバル変数
/* 自ソースでのみ利用するもの */
//---- ステージ別情報
static int gl_StageNo;              // ステージ番号管理
//---- 背景
static TLVERTX BackVertex[4];       // 頂点情報配列
//---- 自機
static STATUS MyChara;              // 自キャラステータス情報
static STATUS MyShot;               // 自弾ステータス情報
//---- 敵
static STATUS EnemyInitData[ENEMYTYPEMAX]; // 敵キャラステータス情報初期化データ
static STATUS Enemy[ENEMYMAX];      // 敵キャラステータス情報
static DWORD gl_EnemyTime;          // 敵キャラ出現時間
static ENEMYOUTDATA EnemyOutData[ENEMYDATAMAX];// 敵キャラ出現データ
static int gl_EnemyCnt;             // 敵キャラ出現データのカウント
static char* szEnemyOutData[] = {"", "Enemy1.dat", "Enemy2.dat", "Enemy3.dat"}; // 敵キャラ出現データファイル名
・
・
・
//-----------------------------------------------------------------------------
// 関数名 : GameStageInit()
// 機能概要: ゲーム画面ステージ別初期化処理
//-----------------------------------------------------------------------------
void GameStageInit(void)
{
    FILE *fp;
    char buff[80];
    DWORD time;
    int type;
    int i;

    //--------------------------------------------------- 各変数の初期化
    // ステージ別に使用するテクスチャの作成
    CreateGameStageTexture(gl_StageNo);
    // ポリゴンの初期化
    /* 自キャラ */
    InitVertex(MyChara.Vertex, 272.0f, 380.0f, 368.0f, 476.0f, 255); // 頂点データ
    SetRect(&MyChara.HitRect, 10, 10, 10, 10);                       // 当たり判定矩形
    MyChara.MoveX = 2.0f;
    MyChara.MoveY = 2.0f;
    /* 自弾 */
    SetRect(&MyShot.HitRect, 2, 2, 2, 2);                        // 当たり判定矩形
    MyShot.MoveX = 0.0f;                                         // 移動量(X方向)
    MyShot.MoveY = -2.0f;                                        // 移動量(Y方向)
    MyShot.life = DEAD;                                          // フラグ
    /* 敵キャラ出現データのカウント */
    ・
    ・
    ・
}

3.自弾発生・描画・移動処理(Game.cpp)

・
・
・
// プロトタイプ宣言
/* 自ソース(Game.cpp)内でのみ利用するもの */
static void SetEnemyInit(LPSTATUS, float, float, float, float, LONG, LONG, LONG, LONG, float, float, int, int);
                                    // 敵キャラ発生データ初期化処理
static void SetEnemy(void);         // 敵キャラ発生処理
static void SetMyShot(void);        // 自弾発生処理
static void BackDraw(void);         // 背景描画
static void MyCharaDraw(void);      // 自機描画
static void EnemyDraw(void);        // 敵キャラ描画
static void MyShotDraw(void);       // 自弾描画
static void MyCharaMove(void);      // 自機移動
static void EnemyMove(void);        // 敵キャラ移動
static void MyShotMove(void);       // 自弾移動
static void CharaHitCheck(void);    // キャラクタ同士の当たり判定
・
・
・
//-----------------------------------------------------------------------------
// 関数名 : GameFrame()
// 機能概要: スタート画面処理
//-----------------------------------------------------------------------------
void GameFrame(void)
{
    //--------------------------------------------------- 前処理
    SetEnemy(); // 敵キャラの出現
    SetMyShot(); // 自弾の発生

    //--------------------------------------------------- 描画
    BackDraw(); // 背景
    MyCharaDraw(); // 自機
    EnemyDraw(); // 敵
    MyShotDraw(); // 自弾

    //--------------------------------------------------- 移動処理
    MyCharaMove(); // 自機
    EnemyMove(); // 敵キャラ
    MyShotMove(); // 自弾

    //--------------------------------------------------- 当たり判定
    CharaHitCheck(); // キャラクタ同士
    ・
    ・
    ・
//-----------------------------------------------------------------------------
// 関数名 : SetMyShot()
// 機能概要: 自弾発生処理
//-----------------------------------------------------------------------------
static void SetMyShot(void)
{
    // スペースキーが押されていなかったら、何もしない
    if ( !(gl_KeyTbl[VK_SPACE] & 0x80) ) return;

    // 発射中でなければ、自キャラの位置から発射する
    if ( MyShot.life == DEAD ) {
        InitVertex(MyShot.Vertex, MyChara.Vertex[0].x, MyChara.Vertex[0].y - 8, MyChara.Vertex[0].x + 8, MyChara.Vertex[0].y, 255);
        MyShot.life = 1;
    }
}
・
・
・
//-----------------------------------------------------------------------------
// 関数名 : MyShotDraw()
// 機能概要: 自弾描画
//-----------------------------------------------------------------------------
static void MyShotDraw(void)
{
    if ( MyShot.life == DEAD ) return;

    gl_lpD3ddev->SetTexture(0, gl_TXMyShot);
    gl_lpD3ddev->DrawPrimitiveUP(D3DPT_TRIANGLEFAN, 2, MyShot.Vertex, sizeof(TLVERTX));
}
・
・
・
//-----------------------------------------------------------------------------
// 関数名 : MyShotMove()
// 機能概要: 自弾移動
//-----------------------------------------------------------------------------
static void MyShotMove(void)
{
    if ( MyShot.life == DEAD ) return;

    Move(MyShot.Vertex, MyShot.MoveX, MyShot.MoveY);
    if ( MyShot.Vertex[2].y < gl_rcScreen.top ) MyShot.life = DEAD;
}
・
・
・

4.敵キャラとの当たり判定(Game.cpp)

・
・
・
//-----------------------------------------------------------------------------
// 関数名 : CharaHitCHeck()
// 機能概要: キャラクタ同士の当たり判定
//-----------------------------------------------------------------------------
static void CharaHitCheck(void)
{
    int i;

    for ( i=0 ; i<ENEMYMAX ; i++ ) {
        if ( Enemy[i].life > DEAD ) {
            /* 自キャラと敵キャラ */
            if ( HitCheck2(MyChara.Vertex, MyChara.HitRect, Enemy[i].Vertex, Enemy[i].HitRect) ) {
                GameRelease();
                g_FrameNo = START_INIT;
            }
            /* 自ショットと敵キャラ */
            if ( MyShot.life > DEAD ) {
                if ( HitCheck2(MyShot.Vertex, MyShot.HitRect, Enemy[i].Vertex, Enemy[i].HitRect) ) {
                    MyShot.life = DEAD;
                    Enemy[i].life = DEAD;
                }
            }
        }
    }
}
・
・
・

※ここまでの修正を行い、ゲーム画面においてスペースキーを押すと弾が一発だけ発射されるかを確かめる。

キーを押している間、一定間隔で弾を撃つ

スペースキーを押している間、弾が連射できるようにプログラムを修正する。スタート画面におけるカーソルの移動(08−6)と同様、そのままでは連続押しになってしまうので、発射後、一定時間内は次の弾が出ないような時間制御が必要になる。

1.キャラクタ構造体の修正(NKC_Common.h)

/* キャラクタ情報構造体 */
typedef struct _STATUS {
    TLVERTX Vertex[4];                      // 頂点情報配列
    RECT HitRect;                           // 当たり判定矩形情報
    float MoveX, MoveY;                     // 移動量
    int type;                               // タイプ
    LPDIRECT3DTEXTURE8 Texture;             // テクスチャ
    int flg;                                // フラグ(表示:1 非表示:0)
    DWORD timing;                           // タイミング
    DWORD time;                             // 時間
} STATUS, *LPSTATUS;

2.自ショット用構造体変数を配列にする(Game.cpp)

//=============================================================================
//  ゲーム処理関係の自作関数群
//  Copyright NKC Game Staff(←自分の名前) 
//-----------------------------------------------------------------------------
#include "NKC_Common.h"

// マクロの定義
#define STAGEMAX    3       // ステージ数
#define ENEMYMAX    100     // 敵キャラの最大数
//#define ENEMYTIME   1000    // 敵キャラの出現タイミング
#define ENEMYDATAMAX 100    // 敵キャラデータ件数の最大
#define ENEMYDATAEND 99999  // 敵キャラデータ終了フラグ
#define ENEMYTYPEMAX 5      // 敵キャラの種類の最大
#define MYSHOTMAX    10     // 自ショットの最大数

// グローバル変数
/* 自ソースでのみ利用するもの */
//---- ステージ別情報
static int gl_StageNo;              // ステージ番号管理
//---- 背景
static TLVERTX BackVertex[4];       // 頂点情報配列
//---- 自機
static STATUS MyChara;              // 自キャラステータス情報
static STATUS MyShot[MYSHOTMAX];    // 自弾ステータス情報
//---- 敵
static STATUS EnemyInitData[ENEMYTYPEMAX]; // 敵キャラステータス情報初期化データ
static STATUS Enemy[ENEMYMAX];      // 敵キャラステータス情報
static DWORD gl_EnemyTime;          // 敵キャラ出現時間
static ENEMYOUTDATA EnemyOutData[ENEMYDATAMAX];// 敵キャラ出現データ
static int gl_EnemyCnt;             // 敵キャラ出現データのカウント
static char* szEnemyOutData[] = {"", "Enemy1.dat", "Enemy2.dat", "Enemy3.dat"}; // 敵キャラ出現データファイル名
・
・
・

3.自ショット関係の処理を修正する(Game.cpp)

・
・
・
//-----------------------------------------------------------------------------
// 関数名 : GameStageInit()
// 機能概要: ステージ開始時初期化処理
//-----------------------------------------------------------------------------
void GameStageInit(void)
{
    ・
    ・
    ・
    //--------------------------------------------------- 各変数の初期化
    // ステージ別に使用するテクスチャの作成
    CreateGameStageTexture(gl_StageNo);
    // ポリゴンの初期化
    /* 自キャラ */
    InitVertex(MyChara.Vertex, 272.0f, 380.0f, 368.0f, 476.0f, 255); // 頂点データ
    SetRect(&MyChara.HitRect, 10, 10, 10, 10);                       // 当たり判定矩形
    MyChara.MoveX = 2.0f;                                        // 移動量(X方向)
    MyChara.MoveY = 2.0f;                                        // 移動量(Y方向)
    MyChara.timing = 500;                                        // 弾を撃つタイミング
    MyChara.time = 0;                                            // 弾を撃ったときの時間
    /* 自弾 */
    for ( i=0 ; i<MYSHOTMAX ; i++ ) {
        SetRect(&MyShot[i].HitRect, 2, 2, 2, 2);                 // 当たり判定矩形
        MyShot[i].MoveX = 0.0f;                                  // 移動量(X方向)
        MyShot[i].MoveY = -2.0f;                                 // 移動量(Y方向)
        MyShot[i].life = DEAD;                                   // フラグ
    }
    /* 敵キャラ出現データのカウント */
    ・
    ・
    ・
}
・
・
・
//-----------------------------------------------------------------------------
// 関数名 : SetMyShot()
// 機能概要: 自弾発生処理
//-----------------------------------------------------------------------------
static void SetMyShot(void)
{
    int i;

    // スペースキーが押されていなかったら、何もしない
    if ( !(gl_KeyTbl[VK_SPACE] & 0x80) ) {
        MyChara.time = 0;
        return;
    }

    // 弾を撃つ時間になったかどうかを調べる
    if ( gl_nowTime - MyChara.time < MyChara.timing ) return;

    // 発射可能な弾を調べ、発射中でなければ、自キャラの位置から発射する
    for ( i=0 ; i<MYSHOTMAX ; i++ ) {
        if ( MyShot[i].life == DEAD ) {
            InitVertex(MyShot[i].Vertex, MyChara.Vertex[0].x, MyChara.Vertex[0].y - 8, MyChara.Vertex[0].x + 8, MyChara.Vertex[0].y, 255);
            MyShot[i].life = 1;
            MyChara.time = gl_nowTime;
            return;
        }
    }
}
・
・
・
//-----------------------------------------------------------------------------
// 関数名 : MyShotDraw()
// 機能概要: 自弾描画
//-----------------------------------------------------------------------------
static void MyShotDraw(void)
{
    int i;

    gl_lpD3ddev->SetTexture(0, gl_TXMyShot);
    for ( i=0 ; i<MYSHOTMAX ; i++ ) {
        if ( MyShot[i].life > DEAD ) gl_lpD3ddev->DrawPrimitiveUP(D3DPT_TRIANGLEFAN, 2, MyShot[i].Vertex, sizeof(TLVERTX));
    }
}
・
・
・
//-----------------------------------------------------------------------------
// 関数名 : MyShotMove()
// 機能概要: 自弾移動
//-----------------------------------------------------------------------------
static void MyShotMove(void)
{
    int i;

    for ( i=0 ; i<MYSHOTMAX ; i++ ) {
        if ( MyShot[i].life > DEAD ) {
            Move(MyShot[i].Vertex, MyShot[i].MoveX, MyShot[i].MoveY);
            if ( MyShot[i].Vertex[2].y < gl_rcScreen.top ) MyShot[i].life = DEAD;
        }
    }
}
・
・
・
//-----------------------------------------------------------------------------
// 関数名 : CharaHitCHeck()
// 機能概要: キャラクタ同士の当たり判定
//-----------------------------------------------------------------------------
static void CharaHitCheck(void)
{
    int i, j;

    for ( i=0 ; i<ENEMYMAX ; i++ ) {
        if ( Enemy[i].life > DEAD ) {
            /* 自キャラと敵キャラ */
            if ( HitCheck2(MyChara.Vertex, MyChara.HitRect, Enemy[i].Vertex, Enemy[i].HitRect) ) {
                GameRelease();
                g_FrameNo = START_INIT;
            }
            /* 自ショットと敵キャラ */
            for ( j=0 ; j<MYSHOTMAX ; j++ ) {
                if ( MyShot[j].life > DEAD ) {
                    if ( HitCheck2(MyShot[j].Vertex, MyShot[j].HitRect, Enemy[i].Vertex, Enemy[i].HitRect) ) {
                        MyShot[j].life = DEAD;
                        Enemy[i].life = DEAD;
                    }
                }
            }
        }
    }
}

※ここまでの修正を行い、弾が0.5秒おきに一発ずつ、最大10発が撃てることを確認する。また、連射にも対応していることを確認する。


BACK(タイプ別にキャラクタ画像を変える) NEXT(敵キャラから弾を出す)