★プレイヤーキャラクター 弾を発射する★


今回は「弾を発射する」と言うタイトルにしましたが、何かを射出すると置き換えてもらっても構いません。

要はプレイヤーキャラクターから「何かが飛んでいく」方法について説明します。

最初から複雑な事はやりたくありませんので、弾は単発とします。

古いゲームだと「インベーダー」や「ギャラクシアン」のように画面内に1発しか撃てない弾です。

撃った弾が画面外に消えるか、目標に当たったら次の弾が撃てます。

また、弾の発射方向も「右」に限定します。


画像の準備


今回使用する画像は↓です。

画像サイズは「16×16ピクセル」です。

画像をダウンロードして「Shot.png」と言う名前で「Resource\\Textureフォルダ」に格納してください。


「GameBase.h」を開き、パス付ファイル名を定数化しましょう。

namespace KeyString
{
    const std::string TEXTURE_CHARACTER = "Resource\\Texture\\Character.png";

    const std::string TEXTURE_BACK = "Resource\\Texture\\Back.png";

    const std::string TEXTURE_SHOT = "Resource\\Texture\\Shot.png";

    const std::string FONT_GOTHIC = "MS ゴシック";
}

「SceneGame.cpp」を開き、「Start関数」で画像を追加します。

void SceneGame::Start()
{
    m_pEngine->AddTexture(TEXTURE_CHARACTER);

    m_pEngine->AddTexture(TEXTURE_BACK);

    m_pEngine->AddTexture(TEXTURE_SHOT);

    m_pEngine->AddFont(FONT_GOTHIC, 20);

    m_player.Initialize();
}

Shotクラスの作成


オブジェクトとしてプロジェクトフォルダに「Shot.h」と「Shot.cpp」を追加しましょう。

「Shot.h」を開き、最初に必要なデータとともにShotクラスを宣言します。

class Shot {
public:

    Shot();

    //初期化
    void Initialize();

    //描画
    void Draw(Engine *pEngine);

private:

    const int WIDTH;
    const int HEIGHT;

    const int SPEED;

    bool m_bShooting;

    int m_x;
    int m_y;

    RECT m_sour;
    RECT m_dest;
};

publicメンバ関数として、コンストラクタ関数、「Initialize関数」、「Draw関数」を用意しました。

privateメンバ定数として画像の幅と高さ、弾のスピードを用意しています。

また、privateメンバ変数として発射中フラグと座標、描画用のRECT構造体を用意しました。

注目したいのは「発射中フラグ(m_bShooting)」です。

弾は発射するまで画面には表示しません。

発射した時にフラグをONにして、描画や移動を行います。

弾が画面外に出たり、目標に当たったらフラグをOFFにします。


「Shot.cpp」を開き、先にコンストラクタ関数と「Initialize関数」を作りましょう。

Shot::Shot()
    : WIDTH(16)
    , HEIGHT(16)
    , SPEED(10)
{

}

//初期化
void Shot::Initialize()
{
    m_bShooting = false;
}

コンストラクタ関数で定数の初期値を設定し、「Initialize関数」でフラグをOFFにします。

「Initialize関数」では座標などの初期値は設定しません。

弾はプレイヤーキャラクターから発射されるので、発射した瞬間に初期座標が決まります


次に「Draw関数」の本体を「Initialize関数」の下に作ります。

//描画
void Shot::Draw(Engine *pEngine)
{
    if (m_bShooting) {

        SetRect(&m_sour, 0, 0, WIDTH, HEIGHT);

        SetRect(&m_dest, m_x, m_y, m_x + WIDTH, m_y + HEIGHT);

        pEngine->Blt(&m_dest, TEXTURE_SHOT, &m_sour);
    }
}

ポイントは「発射中フラグ」がONの時だけ描画のプログラムを動かす所です。


弾の発射


続いて、弾の発射を考えます。

弾は弾自身が発射のタイミングを決める訳ではありません。

プレイヤーキャラクターやその他のオブジェクトが発射のタイミングを決めます。

発射されたタイミングで、座標を決め発射中フラグをONにするのです。

そのための関数「Shoot関数」を追加しましょう。

「Shot.h」を開きプロトタイプ宣言を追加します。

    //初期化
    void Initialize();

    //発射
    void Shoot(const int x, const int y);

    //描画
    void Draw(Engine *pEngine);

引数には発射された場所の座標を設定しています。


「Shot.cpp」を開き、「Initialize関数」の下に「Shoot関数」を追加します。

//発射
void Shot::Shoot(const int x, const int y)
{
    if (!m_bShooting) {

        m_x = x;
        m_y = y;

        m_bShooting = true;
    }
}

ここでも「発射中フラグ」がポイントになります。

最初に書いたように画面内に1発しか撃てないようにしますので、弾が発射中の場合は次の弾が撃てないようにしています。

発射中で無ければ、受け取った座標を弾の初期位置として設定し「発射中フラグ」をONにします。

「発射中フラグ」がONになれば、描画が出来るようになります。


発射のタイミング


今回はプレイヤーキャラクターから弾が発射されるように作ります。

と言う事は、プレイヤーキャラクターが発射のタイミングを持っている事になります。

ここは難しい所なのですが、「Shotクラス」の実体をどこに持たせるか考えなければなりません。

1つの案としては「Playerクラス」に「Shotクラス」を持たせる「包含」と言う考え方があります。

もう1つは全く別のクラスとして「Shotクラス」を用意する方法です。

今回は後者の方法を採用します。

「GameBase.h」を開き、ヘッダファイルをインクルードします。

#include "Object\\Player\\Player.h"
#include "Object\\Back\\Back.h"
#include "Object\\Shot\\Shot.h"

次に「SceneGame.h」を開き、「Shotクラス」の実体を追加します。

private:

    Player m_player;

    Back m_back;

    Shot m_shot;
};

先に初期化と描画の設定だけしておきましょう。

「SceneGame.cpp」を開き、「Start関数」から「Shotクラス」の「Initialize関数」を呼び出します。

void SceneGame::Start()
{
    m_pEngine->AddTexture(TEXTURE_CHARACTER);

    m_pEngine->AddTexture(TEXTURE_BACK);

    m_pEngine->AddTexture(TEXTURE_SHOT);

    m_pEngine->AddFont(FONT_GOTHIC, 20);

    m_player.Initialize();

    m_shot.Initialize();
}

続いて「SceneGame.cpp」の「Draw関数」から「Shotクラス」の「Draw関数」を呼び出します。

void SceneGame::Draw()
{
    m_back.Draw(m_pEngine);

    m_player.Draw(m_pEngine);

    m_shot.Draw(m_pEngine);
}


さてここからが問題です。

弾の発射タイミングはプレイヤーキャラクターが決めます。

ですからプレイヤーキャラクターに「弾を発射する」関数を追加しなければなりません。

プレイヤーキャラクターの行動を設定する関数は「Update関数」です。

今の所は↓のようになっています。

void Player::Update(Engine *pEngine)
{
    Move(pEngine);

    SpeedUp(pEngine);

    ChangeAnimation();
}

「移動する」関数、「スピードを変える」関数、「アニメーションを変える」関数が呼び出されています。

ここに「弾を発射する」関数(Shoot関数)を付け加えます。


「Player.h」を開き、プロトタイプ宣言を追加します。

    //移動
    void Move(Engine *pEngine);

    //スピードアップ
    void SpeedUp(Engine *pEngine);

    //アニメーションの変更
    void ChangeAnimation();

    //弾の発射
    void Shoot(Engine *pEngine, Shot &refShot);
};

弾を発射するにはキー入力が必要になりますので、「Engineクラス」のポインタを受け取ります。

また「Shotクラス」を受け取らなければ弾が発射出来ませんので、「Shotクラス」の参照も受け取ります。


「Playerクラス」から「Shotクラス」を使うためにはヘッダファイルのインクルードが必要になります。

が、参照を受け取っているので「前方参照」を書いておきましょう。

//-----------------------------------------------------------------------------
// クラスの宣言を書きます。
//-----------------------------------------------------------------------------

class Back;

class Shot;

class Player {
public:

    Player();

続いて「Player.cpp」を開き、「ChangeAnimation関数」の下に「Shoot関数」の本体を追加します。

void Player::Shoot(Engine *pEngine, Shot &refShot)
{
    if (pEngine->GetKeyState(DIK_Z)) {

        refShot.Shoot(m_x, m_y);
    }
}

同時押しの関係も考え「Zキー」を割り当てました。


次に「Player.cpp」の「Update関数」から「Shoot関数」を呼び出せるよう変更します。

「Update関数」は引数が「Engineクラス」のみですから、ここから「Shoot関数」を呼び出すとなると「Shotクラス」の参照がありません。

そこで、先に「Playerクラス」の「Update関数」の引数を増やします。

「Player.h」を開き、「Update関数」の引数を追加します。

    //初期化
    void Initialize();

    //更新
    void Update(Engine *pEngine, Shot &refShot);

    //描画
    void Draw(Engine *pEngine);

関数本体にも引数を追加し、「Shoot関数」を呼び出すよう変更します。

void Player::Update(Engine *pEngine, Shot &refShot)
{
    Shoot(pEngine, refShot);

    Move(pEngine);

    SpeedUp(pEngine);

    ChangeAnimation();
}

最後に「SceneGame.cpp」を開き、「Update関数」の「Playerクラス」の「Update関数」の引数を追加します。

void SceneGame::Update()
{
    m_player.Update(m_pEngine, m_shot);
}

実行してみましょう。

Zキーを押したら、キャラクターの左上に弾の画像が表示されるはずです。

弾は動きませんが、今はこれでOKです。


次へ

戻る

目次へ