夏休み勉強会 4日目

本日もTPSの続きを作っていく。


プレイヤーが弾を発射するようにする

弾クラスの枠組みを組んでいく

まずはプレイヤーが発射する弾がないと意味がないので、弾クラスの枠組みを組んでいこう。以下のコードをPlayerBullet.hに記述してくれ。

#pragma once

//	インクルード達
#include "Libraries/Development/Model3D.h"
#include "Libraries/Development/BoxCollider.h"

//	前方宣言
class CommonResources;

/// <summary>
/// プレイヤーの弾
/// </summary>
class PlayerBullet
{
public:

	//	モデルのディレクトリパスと名前
	static constexpr wchar_t MODEL_DIRECTORY[]	= L"Resources/Models/";
	static constexpr wchar_t MODEL_NAME[]		= L"PlayerBullet";

	//	モデルスケール
	static constexpr DirectX::SimpleMath::Vector3 MODEL_SCALE{ 0.1f, 0.1f, 0.1f };

	//	衝突判定の大きさ
	static constexpr DirectX::SimpleMath::Vector3 COLLIDER_SIZE{ 0.2f, 0.2f, 0.2f };

	//	生存時間
	static constexpr float ACTIVE_LIMIT = 5.0f;

	//	移動速度
	static constexpr float MOVE_SPEED = 15.0f;

public:

	//	衝突判定を取得
	const Develop::BoxCollider& GetCollider() const { return m_collider; }

	//	アクティブフラグの取得
	bool GetIsActive() const { return m_isActive; }

public:

	//	座標を設定
	void SetPosition(const DirectX::SimpleMath::Vector3& position);

public:

	//	コンストラクタ
	PlayerBullet();
	//	デストラクタ
	~PlayerBullet() = default;

	//	開始関数
	void Initialize(CommonResources* common);
	//	更新関数
	void Update(float deltaTime);
	//	描画関数
	void Render(const Develop::CameraObject& camera);
	//	終了関数
	void Finalize();

	//	起動関数
	void Boot(
		const DirectX::SimpleMath::Vector3& position,
		const DirectX::SimpleMath::Vector3& direction
	);

	//	削除関数
	void Erase();

private:

	//	モデル
	Develop::Model3D m_model;

	//	衝突判定
	Develop::BoxCollider m_collider;

	//	座標
	DirectX::SimpleMath::Vector3 m_position;

	//	方向
	DirectX::SimpleMath::Vector3 m_direction;

	//	アクティブタイマー
	float m_activeTimer;

	//	アクティブフラグ
	bool m_isActive;
};

続いてPlayerBullet.cppにも以下のコードを記述していこう。

#include "pch.h"
#include "PlayerBullet.h"

#include "../CommonResources.h"

//	名前空間の使用
using namespace DirectX;

/// <summary>
/// コンストラクタ
/// </summary>
PlayerBullet::PlayerBullet()
	:
	m_isActive(false),
	m_activeTimer(0.0f)
{
}

/// <summary>
/// 開始関数
/// </summary>
void PlayerBullet::Initialize(CommonResources* common)
{

}

/// <summary>
/// 更新関数
/// </summary>
/// <param name="deltaTime">フレーム間秒数</param>
void PlayerBullet::Update(float deltaTime)
{
	//	アクティブ状態かどうか
	if (m_isActive == false) { return; }
}

/// <summary>
/// 描画関数
/// </summary>
/// <param name="camera">カメラ</param>
void PlayerBullet::Render(const Develop::CameraObject& camera)
{
	//	アクティブ状態かどうか
	if (m_isActive == false) { return; }
}

/// <summary>
/// 終了関数
/// </summary>
void PlayerBullet::Finalize()
{
}

/// <summary>
/// 起動関数
/// </summary>
/// <param name="position">座標</param>
/// <param name="direction">方向</param>
void PlayerBullet::Boot(
	const DirectX::SimpleMath::Vector3& position,
	const DirectX::SimpleMath::Vector3& direction
)
{

}

/// <summary>
/// 削除関数
/// </summary>
void PlayerBullet::Erase()
{
	//	アクティブフラグをオフに
	m_isActive = false;
}

/// <summary>
/// 座標の設定
/// </summary>
/// <param name="position">座標</param>
void PlayerBullet::SetPosition(const DirectX::SimpleMath::Vector3& position)
{

}

枠組みが完成したら、一旦PlayerBullet.cppを離れて別の作業をしていく。

プレイヤーに弾の配列を管理させる

それでは実際にプレイヤーが弾を発射する機構を作っていこう。いろんなやり方があるが、今回はプレイヤーが弾を管理するやり方でやっていく。
ではPlayer.hに移動し、以下のインクルードを追加してくれたまへ。

#include "../PlayerBullet/PlayerBullet.h"

それでは追加したら、プレイヤーの弾配列も作成していこう。

//	プレイヤーの弾達
std::vector<PlayerBullet> m_bullets;

今回は固定長配列ではなく可変長配列を用いた管理の仕方を学んでいこう。   

固定長配列 : 要素の数が変わらない配列
可変長配列 : 要素の数が変わる配列

そして、配列を作ったらまずは、初期化を行っていく。Player.cppに行ってInitialize関数に以下のコードを記述してくれ。

	//	配列の中身を消す
	m_bullets.clear();

それではPlayer.cppを改造していく、まずは、弾の諸々の関数を呼び出すところから始めよう。
まずは、Update関数に以下のコードを記述してくれ。

	//	弾の更新
	for (int i = 0; i < m_bullets.size(); i++)
	{
		m_bullets[i].Update(deltaTime);
	}

続いてRender関数。

	//	弾の描画
	for (int i = 0; i < m_bullets.size(); i++)
	{
		m_bullets[i].Render(camera);
	}

最後にFinalize関数。

	//	弾の終了
	for (int i = 0; i < m_bullets.size(); i++)
	{
		m_bullets[i].Finalize();
	}

プレイヤーの弾を発射させる

それでは改めて、弾を発射する関数を作成していこう。Playerクラスに以下の関数を宣言&定義しよう。

	//	射出関数
	void Shot(const DirectX::SimpleMath::Vector3& direcition);

それではShot関数の中身を書いていこう。ということで、
手順は以下の通りだ。

  1. 配列の中に使っていない弾がないか調べる

  2. 使っていない弾がなければ新しく弾を生成する

  3. 発射する弾を起動させる

1.配列の中に使っていない弾がないか調べる

まずは、配列の中に使っていない弾がないかを調べていく。
以下のコードをShot関数の中に書いてくれ。

//	起動する弾のポインタ
PlayerBullet* bootBullet = nullptr;

//	使用済みの弾を探す
for (int i = 0; i < m_bullets.size(); i++)
{
	if (m_bullets[i].GetIsActive() == false)
	{
		//	起動する弾のポインタとして登録
		bootBullet = &m_bullets[i];
		//	ループを抜ける
		break;
	}
}

2.使っていない弾がなければ新しく弾を生成する

それでは、もし、使用済みの弾がなかった場合の処理を書いていこう。
この場合は新しく弾を作成して対応していこうと思う。
それでは引き続き以下のコードを記述してくれ。

	//	起動候補が見つからなかった
	if (bootBullet == nullptr)
	{
		//	新しい弾を生成
		m_bullets.push_back(PlayerBullet());

		//	新しい弾を起動する弾として登録
		bootBullet = &m_bullets.back();

		//	新しい弾の開始関数を呼ぶ
		bootBullet->Initialize(m_commonResources);
	}

3.発射する弾を起動させる

では発射する弾が決まったので実際に発射させていこう。
まずは発射方向と発射場所を求めて、にはPlayerBulletのBoot関数により起動させていく。ということで以下のコードを記述してくれ。

	//	銃口座標を算出
	SimpleMath::Vector3 muzzlePosition = MUZZLE_OFFSET;
	muzzlePosition = SimpleMath::Vector3::Transform(muzzlePosition, m_model.GetRotate());
	muzzlePosition += m_position;

	//	弾を起動する
	bootBullet->Boot(muzzlePosition, direcition);

プレイヤーがボタンを押したらShot関数が呼ばれるようにする

ということで、Spaceキーが押された瞬間、Shot関数が呼ばれる機構を実際に自分で作ってみよう。発射する方向はそのままPlayerが向いている方向に射出させるぞ。

プレイヤーの弾を作成していく

一旦自力で組んでみる

ということで、プレイヤーの弾を作成していく。まずは仕様の確認からしていこう。

  • 弾は射出された方向にまっすぐ飛んでいく

  • 弾は発射されてから5秒経つと消える

  • fpsが変わっても速度は一定

これと、各関数の役割について改めて確認していこう。

  • Initialize関数:弾が生成されたときに呼ばれる関数。モデルの初期化や衝突判定の初期化などを行っていく

  • Update関数:弾の動きを表す関数。指定された方向にまっすぐ進んでいき、指定秒数経つと消えるプログラムもここで書く

  • Render関数:弾の描画を行う関数。モデルの描画を行う

  • Finalize関数:終了処理を記述する関数。今回はやることなし

  • Boot関数:弾が発射されるタイミングで呼ばれる関数。座標の初期化、方向の初期化、秒数タイマーの初期化などを行う。アクティブフラグもここでオンになる

  • SetPosition関数:座標を設定する関数。m_positionを設定するほか、モデルや衝突判定の座標も設定する

それでは実際に書いてみてくれ!
成功するとこんな感じになる。

答え合わせ

#include "pch.h"
#include "PlayerBullet.h"

#include "../CommonResources.h"

//	名前空間の使用
using namespace DirectX;

/// <summary>
/// コンストラクタ
/// </summary>
PlayerBullet::PlayerBullet()
	:
	m_isActive(false),
	m_activeTimer(0.0f)
{
}

/// <summary>
/// 開始関数
/// </summary>
void PlayerBullet::Initialize(CommonResources* common)
{
	//	コンテキストの取得
	ID3D11DeviceContext* context = common->GetDeviceResources()->GetD3DDeviceContext();
	//	共通ステートの取得
	CommonStates* states = common->GetCommonStates();

	//	リソース管理クラスの取得
	Develop::ResourceManager* resourceManager = common->GetResourceManager();

	//	モデルハンドルを取得
	Develop::ResourceManager::ModelHandle modelHandle;
	modelHandle = resourceManager->LoadModelCMO(MODEL_DIRECTORY, MODEL_NAME);

	//	モデルの初期化
	m_model.Initialize(context, states, modelHandle);

	//	モデルのスケール
	m_model.SetScale(MODEL_SCALE);

	//	衝突判定のサイズ
	m_collider.SetSize(COLLIDER_SIZE);

	//	アクティブタイマーを初期化
	m_activeTimer = 0.0f;

	//	アクティブフラグの初期化
	m_isActive = false;
}

/// <summary>
/// 更新関数
/// </summary>
/// <param name="deltaTime">フレーム間秒数</param>
void PlayerBullet::Update(float deltaTime)
{
	//	アクティブ状態かどうか
	if (m_isActive == false) { return; }

	//	アクティブタイマーの更新
	m_activeTimer -= deltaTime;

	//	時間が経ったら弾を消す
	if (m_activeTimer < 0.0f)
	{
		Erase();
	}

	//	移動ベクトルを作成
	SimpleMath::Vector3 moveVelocity = m_direction;
	moveVelocity *= MOVE_SPEED * deltaTime;

	//	座標を移動
	SetPosition(m_position + moveVelocity);
}

/// <summary>
/// 描画関数
/// </summary>
/// <param name="camera">カメラ</param>
void PlayerBullet::Render(const Develop::CameraObject& camera)
{
	//	アクティブ状態かどうか
	if (m_isActive == false) { return; }

	//	モデルの描画
	m_model.Render(camera);
}

/// <summary>
/// 終了関数
/// </summary>
void PlayerBullet::Finalize()
{
}

/// <summary>
/// 起動関数
/// </summary>
/// <param name="position">座標</param>
/// <param name="direction">方向</param>
void PlayerBullet::Boot(
	const DirectX::SimpleMath::Vector3& position,
	const DirectX::SimpleMath::Vector3& direction
)
{
	//	アクティブタイマーに時間を設定
	m_activeTimer = ACTIVE_LIMIT;

	//	アクティブフラグをオンにする
	m_isActive = true;

	//	方向を設定
	m_direction = direction;
	m_direction.Normalize();

	//	座標の設定
	SetPosition(position);
}

/// <summary>
/// 削除関数
/// </summary>
void PlayerBullet::Erase()
{
	//	アクティブフラグをオフに
	m_isActive = false;
}

/// <summary>
/// 座標の設定
/// </summary>
/// <param name="position">座標</param>
void PlayerBullet::SetPosition(const DirectX::SimpleMath::Vector3& position)
{
	m_position = position;

	//	モデルの座標を設定
	m_model.SetPosition(position);
	//	衝突判定の座標を設定
	m_collider.SetPosition(position);
}

今回はここで終了。前回が難しすぎたのでちょっと調整のために短めです。
すみません。

この記事が気に入ったらサポートをしてみませんか?