vector
を用いたコレクション処理を身につける。前提
game_stepXX.cpp。目標: 基底 GameObject と派生
Enemy。名前とHPを表示するだけ。
// game_step01.cpp
#include <iostream>
#include <string>
using namespace std;
class GameObject {
public:
string name;
void show() const { cout << "名前: " << name << "\n"; }
};
class Enemy : public GameObject {
public:
int hp = 10;
void info() const { cout << "HP: " << hp << "\n"; }
};
int main() {
Enemy e;
e.name = "スライム";
e.hp = 12;
e.show();
e.info();
}目標: Enemy に
virtual attack() を用意。Slime と
Goblin が上書きする。
// game_step02.cpp ← game_step01.cpp をコピーして改造
#include <iostream>
#include <string>
using namespace std;
class GameObject {
public:
string name;
void show() const { cout << "名前: " << name << "\n"; }
};
class Enemy : public GameObject {
public:
int hp = 10;
virtual void attack() { cout << name << " が殴ってきた!\n"; }
virtual ~Enemy() = default; // 後で動的確保するので仮想デストラクタ
};
class Slime : public Enemy {
public:
void attack() override { cout << "スライムが体当たり!\n"; }
};
class Goblin : public Enemy {
public:
void attack() override { cout << "ゴブリンが棍棒!\n"; }
};
int main() {
Enemy* es[2] = { new Slime(), new Goblin() };
es[0]->name = "ぷるぷる"; es[1]->name = "ゴブさん";
for (int i = 0; i < 2; i++) {
es[i]->show();
es[i]->attack();
delete es[i];
}
}目標: Enemy
を抽象クラスにし、attack() を純粋仮想に。
// game_step03.cpp ← step02 をコピーして改造
#include <iostream>
#include <string>
using namespace std;
class GameObject {
public:
string name;
void show() const { cout << "名前: " << name << "\n"; }
};
class Enemy : public GameObject {
public:
int hp = 10;
virtual void attack() = 0; // 純粋仮想
virtual ~Enemy() = default;
};
class Slime : public Enemy {
public:
void attack() override { cout << "スライムが体当たり!\n"; }
};
class Goblin : public Enemy {
public:
void attack() override { cout << "ゴブリンが棍棒!\n"; }
};
int main() {
Enemy* e1 = new Slime(); e1->name = "ぷるぷる";
Enemy* e2 = new Goblin(); e2->name = "ゴブさん";
Enemy* es[2] = { e1, e2 };
for (int i = 0; i < 2; i++) { es[i]->show(); es[i]->attack(); }
for (int i = 0; i < 2; i++) delete es[i];
}目標: vector<Enemy*>
と範囲forで全体処理。
// game_step04.cpp ← step03 をコピー
#include <iostream>
#include <string>
#include <vector>
using namespace std;
class GameObject {
public:
string name;
void show() const { cout << "名前: " << name << "\n"; }
};
class Enemy : public GameObject {
public:
int hp = 10;
virtual void attack() = 0;
virtual ~Enemy() = default;
};
class Slime : public Enemy {
public:
void attack() override { cout << "スライムが体当たり!\n"; }
};
class Goblin : public Enemy {
public:
void attack() override { cout << "ゴブリンが棍棒!\n"; }
};
int main() {
vector<Enemy*> enemies;
auto s = new Slime(); s->name = "ぷるぷる"; enemies.push_back(s);
auto g = new Goblin(); g->name = "ゴブさん"; enemies.push_back(g);
for (Enemy* e : enemies) {
e->show();
e->attack();
}
for (Enemy* e : enemies) delete e;
}目標: ターンごとに敵が湧き、倒れたら
delete。~Enemy() のログで寿命を確認。
// game_step05.cpp ← step04 をコピー
#include <iostream>
#include <string>
#include <vector>
#include <limits>
using namespace std;
class GameObject {
public:
string name;
void show() const { cout << "名前: " << name << "\n"; }
};
class Enemy : public GameObject {
public:
int hp = 10;
virtual void attack() = 0;
virtual void takeDamage(int d) { hp -= d; }
bool isDead() const { return hp <= 0; }
virtual ~Enemy() { cout << name << " が消滅\n"; }
};
class Slime : public Enemy {
public:
Slime() { hp = 18; }
void attack() override { cout << "スライムの体当たり!\n"; }
};
class Goblin : public Enemy {
public:
Goblin() { hp = 25; }
void attack() override { cout << "ゴブリンの棍棒!\n"; }
};
int main() {
vector<Enemy*> enemies;
const int DAMAGE = 12;
int turnMax = 6;
for (int turn = 1; turn <= turnMax; ++turn) {
// 出現(動的生成)
if (turn % 2) {
auto e = new Slime(); e->name = "スライム#" + to_string(turn); enemies.push_back(e);
} else {
auto e = new Goblin(); e->name = "ゴブリン#" + to_string(turn); enemies.push_back(e);
}
cout << "\n--- Turn " << turn << " ---\n";
int idx = 1;
for (Enemy* e : enemies) {
cout << idx++ << ") "; e->show();
}
cout << "攻撃対象番号 (0でスキップ, -1で終了): ";
int choice;
if (!(cin >> choice)) { cin.clear(); cin.ignore(numeric_limits<streamsize>::max(), '\n'); choice = 0; }
if (choice == -1) break;
if (choice > 0 && choice <= (int)enemies.size()) {
Enemy* t = enemies[choice - 1];
t->takeDamage(DAMAGE);
cout << "→ " << t->name << " に " << DAMAGE << " ダメージ\n";
if (t->isDead()) {
cout << "撃破! delete 実行\n";
delete t;
enemies.erase(enemies.begin() + (choice - 1));
}
}
// 敵の行動
for (Enemy* e : enemies) e->attack();
}
// 残骸掃除
for (Enemy* e : enemies) delete e;
}目標: Dragon
を追加。被ダメ軽減など挙動差分を増やす。
// game_step06.cpp ← step05 をコピーし Dragon を追加
#include <iostream>
#include <string>
#include <vector>
#include <limits>
using namespace std;
class GameObject {
public:
string name;
void show() const { cout << "名前: " << name << "\n"; }
};
class Enemy : public GameObject {
public:
int hp = 10;
virtual void attack() = 0;
virtual void takeDamage(int d) { hp -= d; }
bool isDead() const { return hp <= 0; }
virtual ~Enemy() { cout << name << " が消滅\n"; }
};
class Slime : public Enemy {
public:
Slime() { hp = 18; }
void attack() override { cout << "スライムの体当たり!\n"; }
};
class Goblin : public Enemy {
public:
Goblin() { hp = 25; }
void attack() override { cout << "ゴブリンの棍棒!\n"; }
};
class Dragon : public Enemy {
public:
Dragon() { name = "ドラゴン"; hp = 40; }
void attack() override { cout << "ドラゴンの炎ブレス!\n"; }
void takeDamage(int d) override { hp -= (d - 3 > 0 ? d - 3 : 0); }
};
int main() {
vector<Enemy*> enemies;
const int DAMAGE = 12;
int turnMax = 9;
for (int turn = 1; turn <= turnMax; ++turn) {
// 出現ロジックを少し複雑に
if (turn % 3 == 0) {
auto e = new Dragon(); e->name += "#" + to_string(turn); enemies.push_back(e);
} else if (turn % 2) {
auto e = new Slime(); e->name = "スライム#" + to_string(turn); enemies.push_back(e);
} else {
auto e = new Goblin(); e->name = "ゴブリン#" + to_string(turn); enemies.push_back(e);
}
cout << "\n--- Turn " << turn << " ---\n";
int idx = 1;
for (Enemy* e : enemies) { cout << idx++ << ") "; e->show(); }
cout << "攻撃対象番号 (0でスキップ, -1で終了): ";
int choice; if (!(cin >> choice)) { cin.clear(); cin.ignore(numeric_limits<streamsize>::max(), '\n'); choice = 0; }
if (choice == -1) break;
if (choice > 0 && choice <= (int)enemies.size()) {
Enemy* t = enemies[choice - 1];
t->takeDamage(DAMAGE);
cout << "→ " << t->name << " に " << DAMAGE << " ダメージ\n";
if (t->isDead()) { cout << "撃破! delete 実行\n"; delete t; enemies.erase(enemies.begin() + (choice - 1)); }
}
for (Enemy* e : enemies) e->attack();
}
for (Enemy* e : enemies) delete e;
}目標: 範囲forの参照で全員にバフ/デバフをかけるなどの全体更新を追加。
// 例: ターン開始時に全員が小回復(HP+1)
// vector<Enemy*> enemies; がある前提
for (Enemy*& e : enemies) {
e->hp += 1; // 参照で書き換え。値コピーにしないこと。
}下から好きに 2つ以上 実装。設計とコードを提出。動作ログまたは短い動画を添付。
unique_ptr<Enemy> 版に書き換え (delete
忘れ対策)。Boss 追加。特性: 行動が2回、HP閾値でフェーズ移行。-DDEBUG
マクロやフラグで制御)。提出コメントに含めること
注意点
virtual ~Enemy() を忘れて多態削除で未定義動作。new[] と delete[] の取り違え
(配列版を使うなら)。評価観点
Enemy
を基底に、分岐なしで派生の攻撃が呼べる)。delete、終了時に残骸全解放)。vector<Enemy*>
と範囲forで一覧処理)。