弾幕STGにおけるオブジェクトの参照渡しと返り値について

フォーラム(掲示板)ルール
フォーラム(掲示板)ルールはこちら  ※コードを貼り付ける場合は [code][/code] で囲って下さい。詳しくはこちら
鈴狼

弾幕STGにおけるオブジェクトの参照渡しと返り値について

#1

投稿記事 by 鈴狼 » 4ヶ月前

初めまして、最近C++言語の勉強を始めたばかりの初心者です。
ここに書き込むのは初見となります。何か不作法があれば指摘して頂けると助かります。

[龍神録プログラミングの館2]を参考にしながら、簡単な弾幕STGを作っています。
ですが、分からないところがあり、自分の力だけでは解決できそうにありません。
皆様のご助力を願います。よろしくお願い致します。

環境は、[OS:windows7][コンパイラ:VisualStudio2017][ライブラリ:DXライブラリ]を使用しています。

今、取り組んでいるコードは以下になります。

GameScene.h

コード:

#pragma once

#include "AbstractScene.h"
#include <memory>
#include "Player.h"
#include "Board.h"
#include "EnemyManager.h"
#include "BulletManager.h"

class GameScene : public AbstractScene
{
public:
	const static char* ParameterTagStage;//パラメータのタグ「ステージ」
	const static char* ParameterTagLevel;//パラメータのタグ「レベル」

	GameScene(IOnSceneChangedListener* impl, const Parameter& parameter);
	virtual ~GameScene() = default;

	void update() override;
	void draw() const override;

private:
	std::shared_ptr<Player> _player;				//自機
	std::shared_ptr<Board>  _board;					//ボード
	std::shared_ptr<EnemyManager> _enemyManager;	//敵管理
	std::shared_ptr<BulletManager> _bulletManager;	//弾管理
};
GameScene.cpp

コード:

#include "../include/GameScene.h"
#include <DxLib.h>

using namespace std;

const char* GameScene::ParameterTagStage = "ParameterTagStage";	//パラメータのタグ「ステージ」
const char* GameScene::ParameterTagLevel = "ParameterTagLevel";	//パラメータのタグ「レベル」

//コンストラクタ
GameScene::GameScene(IOnSceneChangedListener* impl, const Parameter& parameter) : AbstractScene(impl, parameter)
{
	_player = make_shared<Player>();
	_board = make_shared<Board>();
	_enemyManager = make_shared<EnemyManager>();
	_bulletManager = make_shared<BulletManager>();
}

//更新処理
void GameScene::update()
{
	_player->update();
	_board->update();
	_enemyManager->update();
	_bulletManager->update(*_enemyManager, *_player);

	if (_player->getLife()==-1) {
		Parameter parameter;
		const bool stackClear = true;
		_implSceneChanged->onSceneChanged(eScene::Title, parameter, stackClear);
		return;
	}
}

//描画処理
void GameScene::draw() const
{
	_player->draw();
	_board->draw();
	_enemyManager->draw();
	_bulletManager->draw();
}
Bulletmanager.h

コード:

pragma once

#include "Task.h"
#include <memory>
#include <list>
#include "Bullet.h"
#include "EnemyManager.h"
#include "Player.h"
#include "Shot.h"

class BulletManager : public Task
{

public:
	BulletManager();
	virtual ~BulletManager() = default;
	bool update() override;
	bool update(EnemyManager& enemyManager, Player player);
	void draw() const override;

private:
	void shot_B(EnemyManager& enemyManager, Player player, Shot& shot);	//ボスの弾幕を呼び出す

	float bossatan2(EnemyManager enemyManager, Player player);

	std::shared_ptr<Shot> _bossShot;	//ボスショット
};
Bulletmanager.cpp

コード:

#include "../include/BulletManager.h"
#include <DxLib.h>
#include "../include/Boss.h"
#include "../include/Define.h"
#include "../include/Pad.h"

using namespace std;

BulletManager::BulletManager()
{
	_bossShot	= make_shared<Shot>();
}

bool BulletManager::update() 
{
	return true;
}

bool BulletManager::update(EnemyManager& enemyManager, Player player)
{
	//ボスのショット処理
	if (enemyManager.getBoss()->getState() == 2) {	//ボスの状態が[2:弾幕実行中]の場合
		shot_B(enemyManager, player, *_bossShot);		//ボス弾幕の呼び出し(弾生成)
		_bossShot->update();
	}

	return true;
}

void BulletManager::draw() const
{
	_bossShot->draw();
}

void BulletManager::shot_B(EnemyManager& enemyManager, Player player, Shot& shot)
{
	#define TM000 120
	int i,t = shot.getCnt()%TM000;
	float angle;

	if (t<60 && t%10==0) {
		angle = bossatan2(enemyManager, player);
		for (i=0; i<30; i++) {
			shot._bullet.emplace_back(make_shared<Bullet>(	//弾生成
				enemyManager.getBoss()->getX(),	//座標X
				enemyManager.getBoss()->getY(),	//座標Y
				angle + (Define::PI*2) / 30*i,	//方向
				3,								//速度
				0,								//弾種
				0,								//弾色
				0								//状態
				)
			);
		}
	}
}

//自機と敵との成す角度を得る
float BulletManager::bossatan2(EnemyManager enemyManager, Player player)
{
	return atan2(player.getY() - enemyManager.getBoss()->getY(), player.getX() - enemyManager.getBoss()->getX());

}
Shot.h

コード:

#pragma once

#include "Task.h"
#include <memory>
#include <list>
#include "Bullet.h"

class Shot : public Task
{

public:
	Shot();
	virtual ~Shot() = default;
	bool update() override;
	void draw() const override;

	int getCnt();
	std::list<std::shared_ptr<Bullet>> getBullet();

	std::list<std::shared_ptr<Bullet>> _bullet;

private:
	//フラグ、種類、カウンタ、どの敵から発射されたかの番号、色
	int _flag, _knd, _cnt, _num, _color;
	//ベース角度、ベーススピード
	float _base_angle[1], _base_spd[1];

};
Shot.cpp

コード:

#include "../include/Shot.h"
#include <DxLib.h>

using namespace std;

//コンストラクタ
Shot::Shot()
{
	_cnt = 0;
}

bool Shot::update()
{
	for (auto itr = _bullet.begin(); itr != _bullet.end();) {
		if ((*itr)->update() == false) {
			itr = _bullet.erase(itr);
		}
		else {
			itr++;
		}
	}
	_cnt++;
	return true;
}

void Shot::draw() const
{
	for (auto Bullet : _bullet) {
		Bullet->draw();
	}
}

int Shot::getCnt()
{
	return _cnt;
}

std::list<std::shared_ptr<Bullet>> Shot::getBullet()
{
	return _bullet;
}
Bullet.h

コード:

#pragma once

#include "Task.h"

class Bullet : public Task
{

public :
	Bullet(float x, float y, float angle, float spd, int knd, int col, int state);
	virtual ~Bullet() = default;
	bool update() override;
	void draw() const override;

	float getX() const;
	float getY() const;
	float getAng() const;
	float getSpd() const;
	int getKnd() const;
	
private:
	bool isInside() const;

	float _x, _y;	//座標
	float _angle;	//角度
	float _speed;	//速度
	int _knd;		//弾種
	int _col;		//弾色
	int _state;		//状態
	int _cnt;		//カウンタ

	int _width;		//サイズ幅
	int _height;	//サイズ高
};
Bullet.cpp

コード:

#include "../include/Bullet.h"
#include "../include/ImageBullet.h"
#include <DxLib.h>
#include "../include/Define.h"

//コンストラクタ
Bullet::Bullet(float x, float y, float angle, float spd, int knd, int col, int state)
				: _x(x), _y(y), _angle(angle), _speed(spd), _knd(knd), _col(col), _state(state)
{
	const Size* _sizeList;
	_sizeList = ImageBullet::getIns()->getSize(_knd);

	_width	= _sizeList->getWidth();	//弾の幅を得る
	_height = _sizeList->getHeight();	//弾の高を得る
	_cnt	= 0 ;						//カウンタ初期化
}

//更新処理
bool Bullet::update()
{
	_x += cos(_angle)*_speed;
	_y += sin(_angle)*_speed;
	_cnt++;
	return isInside();
}

//描画処理
void Bullet::draw() const
{
	DrawRotaGraphF(_x, _y, 1.0, 0.0, ImageBullet::getIns()->get(_knd,_col), TRUE);
}

/*!
@brief 現在の位置が画面内か?
*/
bool Bullet::isInside() const
{
	if (_cnt < 60) {	//最初の1秒は判定しない
		return true;
	}
	if (_x < -_width / 2 || Define::OUT_W + _width / 2 < _x || _y < -_height / 2 || Define::OUT_H + _height / 2 < _y) {
		return false;
	}
	return true;
}

float Bullet::getX() const
{
	return _x;
}

float Bullet::getY() const
{
	return _y;
}

float Bullet::getAng() const
{
	return _angle;
}

float Bullet::getSpd() const
{
	return _speed;
}

int Bullet::getKnd() const
{
	return _knd;
}
これ以外のコードは、[龍神録プログラミングの館2]のものを使っています。

問題の場所は、[Shot]の[std::list<std::shared_ptr<Bullet>> Shot::getBullet()]という関数になります。
この関数を使って、[BulletManager]の[shot_B]に、[Shot]の[_bullet]を渡して、弾のオブジェクトを生成しようとした際に正常に動作しませんでした。
エラーは発生していません。[Bullet]のコンストラクタも起動はしていたのですが、直後に消滅している印象を受けました。
その為、[Bullet]の[update()]も[draw()]も実行されませんでした。

この問題は、[Shot]の[_bullet]をpublicにして、関数を経由せず直接渡すことによって解決しました。
しかし、何故関数を経由していた場合に正常動作しなかったのか分からず、気になっています。
無闇にpublic化するのは危険というのもあり、[_bullet]をprivateのまま正しい動作をさせる方法が知りたいです。
何が原因か、どうすれば良かったのかを教えてください。よろしくお願い致します。

BulletManager.cppのshot_B(修正前の問題コード)

コード:

void BulletManager::shot_B(EnemyManager& enemyManager, Player player, Shot& shot)
{
	#define TM000 120
	int i,t = shot.getCnt()%TM000;
	float angle;

	if (t<60 && t%10==0) {
		angle = bossatan2(enemyManager, player);
		for (i=0; i<30; i++) {
			shot.getBullet().emplace_back(make_shared<Bullet>(	//弾生成
				enemyManager.getBoss()->getX(),	//座標X
				enemyManager.getBoss()->getY(),	//座標Y
				angle + (Define::PI*2) / 30*i,	//方向
				3,								//速度
				0,								//弾種
				0,								//弾色
				0								//状態
				)
			);
		}
	}
}

アバター
みけCAT
記事: 6020
登録日時: 7年前
住所: 千葉県
連絡を取る:

Re: 弾幕STGにおけるオブジェクトの参照渡しと返り値について

#2

投稿記事 by みけCAT » 4ヶ月前

Shot::getBullet()の返り値は_bulletのコピーなので、そこに変更を加えても_bulletには反映されず、(この場合は別の変数などに保存していないので)すぐに破棄されてしまいます。
Shot::getBullet()を_bulletの参照を返すようにすれば、動作は改善するでしょう。
(これだとgetBullet()をpublicにすれば実質_bulletをpublicにすることになり、オブジェクト指向的?にはよくないかもしれませんが)
複雑な問題?マシンの性能を上げてOpenMPで殴ればいい!(死亡フラグ)

鈴狼

Re: 弾幕STGにおけるオブジェクトの参照渡しと返り値について

#3

投稿記事 by 鈴狼 » 4ヶ月前

早速の返信、ありがとうございます。
教えて頂いた通りに、参照を返すようにしたら無事に正常動作するようになりました。
オブジェクト指向的には良くないかもしれませんが、これ以外の方法も思いつかないので、何か閃くまではこのままでいこうと思います。
これにて解決とさせて頂きます。ありがとうございました。

コード:

std::list<std::shared_ptr<Bullet>>& getBullet();

返信

“C言語何でも質問掲示板” へ戻る