現在、STGを作成しており、敵の弾をstd::listで管理しているのですが、敵のクラスを削除するとき(恐らく)listの処理の中で例外が投げられて
動作がストップしてしまう問題に困っております。この例外の原因解明、および問題の解決方法はあるでしょうか。
Windows 10 Build 1607
C++ DxLibrary
VIsual Studio 2017 community
以下に該当部分のソースを提示します。
設計としては、Managerクラスがplayer enemy などゲーム内の要素のクラスを持っており、
削除や登録を行っています。例外はManager.cpp 37行目で投げられます。例外が投げられた場所はlist内の
1546行目です。
また、Manager::All()は毎フレーム呼び出されます。
回答よろしくお願いします。
Manager.hpp
#include "Player.hpp"
#include "Enemy.hpp"
#include "Back.hpp"
#define TMP 16
class Manager
{
private:
const int ENEMY_MAX = 16;
Board *board;
Player *player;
Enemy *enemy[TMP];
//敵データ読み込み用
typedef struct
{
int ID;
float x;
float y;
int in_time;
int stop_time;
int shot_time;
int out_time;
int move_kind;
int shot_kind;
}EnemyData;
EnemyData enemydata[TMP];
public:
Manager();
~Manager();
void All();
void LoadEnemyData();
void EnterEnemy(int argIndex);
};
//少なくともゲームのメインシーンにおいて各々を管理するためのクラスです。
//後々シーン管理なり始めたらなにかしらのクラスの継承先になる可能性があります。
#include "Manager.hpp"
#include "System.hpp"
#include <fstream>
#include <string>
#include <iostream>
#include <sstream>
Manager::Manager()
{
player = new Player();
board = new Board();
for (int i = 0; i < ENEMY_MAX; i++)
{
enemy[i] = new Enemy();
}
for (int i = 0; i < ENEMY_MAX; i++)
{
enemydata[i].ID = -1; //IDが-1なら未登録であるということにする
}
LoadEnemyData();
}
void Manager::All()
{
board->All();
player->All();
for (int i = 0; i < ENEMY_MAX; i++)
{
//敵が存在しなくなったら消す
if (enemy[i]->IsDestroyed())
{
enemy[i]->~Enemy();
delete enemy[i]; //ここで例外が投げられる
}
//敵が登録されていて、かつ登場すべき時間であれば敵を登録する
if (enemydata[i].ID != -1 && enemydata[i].in_time == System::Instance()->GetCount())
{
EnterEnemy(i);
}
//有効な敵だけ処理する
if (enemy[i]->IsStarting())
{
enemy[i]->All();
}
}
}
//とりあえず関数だけ分けてみたり
void Manager::EnterEnemy(int argIndex)
{
enemy[argIndex]->Init(enemydata[argIndex].x, enemydata[argIndex].y, enemydata[argIndex].in_time,
enemydata[argIndex].stop_time, enemydata[argIndex].shot_time, enemydata[argIndex].out_time,
enemydata[argIndex].move_kind, enemydata[argIndex].shot_kind);
}
//クソ設計過ぎて泣ける
void Manager::LoadEnemyData()
{
std::ifstream EnemyCsv("./assets/csv/Enemy00.csv"); //ファイル開く
std::string buf; //バッファ
int row = 0; //行
int column = 0; //列
std::getline(EnemyCsv, buf); //一行読み飛ばして
buf.erase(); //バッファ削除
while (std::getline(EnemyCsv,buf)) //ファイルの終端まで読み込む
{
column = 0;
std::stringstream ss(buf); //ストリームにして扱いやすくする
std::string tmp[9]; //一時保存用
for (column; column < 9; column++) //9個の要素があるのでその分ループ
{
int value; //代入用
std::getline(ss, tmp[column], ','); //分割して
value = std::atoi(tmp[column].c_str()); //変換して代入
switch (column) //要素で分けてみたり
{
//ひたすら数字を数値に変換しつつデータをブチ込む
case 0: enemydata[row].ID = value; break;
case 1: enemydata[row].x = value; break;
case 2: enemydata[row].y = value; break;
case 3: enemydata[row].in_time = value; break;
case 4: enemydata[row].stop_time = value; break;
case 5: enemydata[row].shot_time = value; break;
case 6: enemydata[row].out_time = value; break;
case 7: enemydata[row].move_kind = value; break;
case 8: enemydata[row].shot_kind = value; break;
}
}
row++;
}
}
Manager::~Manager()
{
delete player;
delete board;
}
#pragma once
#include <list>
class Enemy
{
//敵
float x,y; //座標
float speed; //速さ
float vx, vy; //速度
float ang; //角度
int gr[24]; //グラフィックハンドル
int imgX,imgY; //画像のサイズ
//行動パターン制御用
int in_time,stop_time,shot_time,out_time;
int cnt; //カウンタ
int s_cnt; //敵がいま打っている弾の数の保持用
int shot_kind,move_kind; //移動パターン、弾幕パターンの保持用
bool endflag; //敵が消滅したかのフラグ
bool startflag; //敵が生成されたかのフラグ
//弾
typedef struct
{
float x, y; //座標
float vx, vy; //速度
float ang; //角度
float speed; //早さ
float colrad; //あたり判定の半径
int kind; //弾の種類
int cnt; //カウンタ
bool flag; //存在するかのフラグ
}Shot;
std::list<Shot> shot;
int s_gr[16];
//弾用パラメータ(使用するかどうかは任意)
int s1;
int s2;
int s3;
int s4;
float sf1;
float sf2;
float sf3;
float sf4;
bool s_end; //弾幕が終了したかのフラグ
void Update();
void Move();
void Enter_shot();
void Move_shot();
void Draw();
public:
bool All();
bool IsDestroyed();
bool IsStarting();
bool Init(float arg_x, float arg_y, int arg_in_time, int arg_stop_time, int arg_shot_time, int arg_out_time, int arg_move_kind, int arg_shot_kind);
Enemy();
~Enemy();
};
//敵のクラス。
//移動やショットなどの制御、描画等
#include "Enemy.hpp"
#include "GV.h"
#include "System.hpp"
#include <cmath>
Enemy::Enemy()
{
startflag = false;
}
bool Enemy::IsDestroyed()
{
return endflag;
}
bool Enemy::IsStarting()
{
return startflag;
}
bool Enemy::Init(float arg_x, float arg_y, int arg_in_time, int arg_stop_time, int arg_shot_time, int arg_out_time, int arg_move_kind, int arg_shot_kind)
{
LoadDivGraph("assets/img/char/Enemy.png", 24, 6, 4, 64, 64, gr, true); //キャラ画像の読み込み
GetGraphSize(gr[0], &imgX, &imgY); //キャラのサイズを取得
//初期位置設定
x = arg_x;
y = arg_y;
vx = 0.0f;
vy = 0.0f;
ang = 0.0f;
//移動タイミング設定
in_time = arg_in_time;
stop_time = arg_stop_time;
shot_time = arg_shot_time;
out_time = arg_out_time;
//カウンタ、フラグ、敵の種類を初期化
move_kind = arg_move_kind;
shot_kind = arg_shot_kind;
cnt = 0;
s_cnt = 0;
endflag = false;
startflag = true;
LoadDivGraph("./assets/img/bullet/bullet01.png", 16, 4, 4, 64, 64, s_gr, true);
//弾のパラメータ初期化
s1 = 0;
s2 = 0;
s3 = 0;
s4 = 0;
sf1 = 0;
sf2 = 0;
sf3 = 0;
sf4 = 0;
s_end = false;
return 0;
}
//更新
void Enemy::Update()
{
cnt++;
s_cnt = 0;
Move();
Enter_shot();
//敵が持つ弾が無く、かつ画面外に出たらこの敵を消すフラグを立てる
if (120 < this->cnt && s_cnt == 0)
{
if (this->x + this->imgX < FIELD_X || FIELD_X + FIELDSIZE_X < this->x + this->imgX ||
this->y + this->imgY < FIELD_Y || FIELD_Y + FIELDSIZE_Y < this->y + this->imgY)
this->endflag = true;
}
}
//敵キャラ移動
void Enemy::Move()
{
int gcnt = System::Instance()->GetCount();
int gtime = System::Instance()->GetTime();
//switch文により行動パターン分け
switch (move_kind)
{
case 0: //降りてきてそのまま帰る
{
this->speed = 5;
if (gcnt < stop_time)
this->ang = TRANS_RAD(90);
else if (stop_time < gcnt && gcnt < out_time)
this->speed = 0;
else if (gcnt > out_time)
this->ang = TRANS_RAD(270);
break;
}
case 1: //降りてきて右回転して帰る
{
this->speed = 5;
if (gcnt < stop_time)
this->ang = TRANS_RAD(90);
else if (stop_time < gcnt && gcnt < out_time)
this->ang = TRANS_RAD(-gcnt + stop_time + 90);
else if (out_time < gcnt)
this->ang = TRANS_RAD(-out_time+stop_time+90);
break;
}
case 2:
{
this->speed = 3;
if (this->cnt < 90)
{
this->ang = TRANS_RAD(90);
}
else if (90 < this->cnt && this->cnt < 720)
{
this->ang = TRANS_RAD(90 - (this->cnt - 90));
}
else if (720 <= this->cnt)
{
this->ang = TRANS_RAD(270);
}
break;
}
case 3:
{
break;
}
default:
{
printfDx("Error:move_kind %d is not exist", move_kind);
break;
}
}
//実際に移動させる
this->vx = cos(this->ang)*this->speed;
this->vy = sin(this->ang)*this->speed;
this->x += this->vx;
this->y += this->vy;
}
//敵ショットの登録
void Enemy::Enter_shot()
{
if (System::Instance()->GetCount() > shot_time && System::Instance()->GetCount() < out_time) //発射開始以降に登録を開始
{
switch (shot_kind) //弾幕の種類分け
{
case 0: //全方位弾
{
const int way = 36;
std::list<Shot> tmp;
Shot t;
if (this->cnt % 6 == 0)
{
for (int i = 0; i < way; i++)
{
t.ang = TRANS_RAD(i * 10 + this->cnt);
t.x = this->x;
t.y = this->y;
t.cnt = 0;
t.kind = 0;
t.flag = true;
t.speed = 8;
t.colrad = 15;
shot.push_back(t);
}
}
break;
}
case 1:
{
break;
}
default:
break;
}
}
//実際に移動させる
Move_shot();
}
//敵ショットの移動
void Enemy::Move_shot()
{
for (auto itr = shot.begin(); itr != shot.end();)
{
if (!(itr->x < FIELD_X || FIELD_X + FIELDSIZE_X < itr->x ||
itr->y < FIELD_Y || FIELD_Y + FIELDSIZE_Y < itr->y))
{
s_cnt++;
itr->vx = cos(itr->ang)*itr->speed;
itr->vy = sin(itr->ang)*itr->speed;
itr->x += itr->vx;
itr->y += itr->vy;
itr->cnt++;
itr++;
}
else {
itr = shot.erase(itr);
s_cnt--;
}
}
}
//敵キャラの描画
void Enemy::Draw()
{
//敵キャラおよび敵ショットはゲーム画面内でのみ描画させる
SetDrawArea(FIELD_X, FIELD_Y, FIELD_X + FIELDSIZE_X, FIELD_Y + FIELDSIZE_Y);
for (auto itr = shot.begin(); itr != shot.end();)
{
DrawRotaGraph(itr->x, itr->y, 0.8f, itr->ang + 3.14159/2, s_gr[itr->kind], true, false);
itr++;
}
int img=0;
if (!endflag)
{
img = cnt%30/10;
DrawRotaGraphF(x,y,2.0,0.0,gr[img],true,false);
}
SetDrawAreaFull();
printfDx("%d Shots", s_cnt);
}
//すべて呼ぶ関数
bool Enemy::All()
{
Update();
Draw();
return true;
}
Enemy::~Enemy()
{
}