今回は「弾を発射する」と言うタイトルにしましたが、何かを射出すると置き換えてもらっても構いません。
要はプレイヤーキャラクターから「何かが飛んでいく」方法について説明します。
最初から複雑な事はやりたくありませんので、弾は単発とします。
古いゲームだと「インベーダー」や「ギャラクシアン」のように画面内に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.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();
}
|
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です。