敵クラスを継承を用いて実装していきたいと思います。
基底クラス
基底クラスには全ての敵に共通するような変数、あるいは関数を実装したいと思います。
まずは変数について考えていきましょう。
ドラクエのようなRPGに出てくる敵を思い浮かべてみてください。
その敵全てに共通するようなステータスは何だと思いますか?
思いつくだけでこれくらいはあると思います。
・HP
・MP
・攻撃力
・防御力
・倒したときに貰える経験値
・倒したときに貰えるお金
・死んだかどうかの判定
ではこれらのステータスを基底クラスに実装してみましょう。
#include "Player.h" //プレイヤークラスの実装は省きます。
//敵クラスの基底クラス
class IEnemy{
public:
IEnemy() {} //コンストラクタ
//デストラクタ
//C++の場合、基底クラスにはvirtualが必要です。
virtual ~IEnemy() {}
//派生クラスからアクセスできるように
//protectedにしておきます。
protected:
int HP; //HP
int MP; //MP
int Atk; //攻撃力
int Def; //防御力
int Exp; //経験値
int Gold; //お金
bool isDeath = false; //死んだかどうか、falseで初期化しておく
public:
Player* player //プレイヤー
};
次に関数です。敵全体に共通するとしては、攻撃する時の処理、ダメージを受けた時の処理、死んだ時の処理...色々ありますよね。
攻撃する時の処理ですが、敵によって色々変わってきますよね。打撃だったり、魔法を撃ったり。とりあえずここは純粋仮想関数として、派生クラスで具体的な処理を実装することにしましょう。ダメージを受けた時の処理も同様とします。
死んだ時の処理ですが、全敵共通でお金と経験値をプレイヤーが獲得する、死亡フラグをオンにする、というものにします。これらの処理は基底クラスで実装したいと思います。
他にも色々あるかと思いますが、この3つを実装していきます。
#include "Player.h" //プレイヤークラスの実装は省きます。
//敵クラスの基底クラス
class IEnemy{
public:
IEnemy() {} //コンストラクタ
virtual ~IEnemy() {} //デストラクタ、virtualを忘れないように
//攻撃をする処理。
//純粋仮想関数にして、派生クラスで実装する。
virtual void Attack() = 0;
//攻撃を受ける処理。
//純粋仮想関数にして、派生クラスで実装する。
virtual void Damage(int damage) = 0;
//死んだ時の処理
//こちらは全敵共通の処理にする。
void Death()
{
player->AddExp(Exp); //プレイヤーに経験値を加算する
player->AddGold(Gold); //プレイヤーにお金を加算する
isDeath = true; //死んだフラグをオンにする
}
//死んでるかどうか
bool GetisDeath()
{
return isDeath;
}
protected:
int HP; //HP
int MP; //MP
int Atk; //攻撃力
int Def; //防御力
int Exp; //経験値
int Gold; //お金
bool isDeath = false; //死んだかどうか、falseで初期化しておく
Player* player //プレイヤー
};
以上が基本クラスの実装です。
派生クラス
まずはスライムのような敵を実装していきたいと思います。
攻撃の処理は攻撃力をそのままダメージとする通常攻撃、ダメージの処理はダメージ-防御力の値をHPから減らすものとします。
#include "IEnemy.h"
class Slime : public IEnemy{
public:
Slime();
~Slime() {}
Attack() override;
Damage(int damage) override;
private:
};
Slime::Slime()
{
//各種ステータスを初期化します
HP = 10;
MP = 0;
Atk = 5;
Def = 2;
Exp = 2;
Gold = 2;
}
Slime::Attack()
{
//プレイヤーに攻撃する!
player->Damage(Atk);
}
Slime::Damage(int damage)
{
//HPを減らす
HP -= (damage - Def);
//もし、HPが0以下なら死んだ時の処理をする
if(HP <= 0) {
Death();
}
}
次はゴーレムのような敵を実装していきたいと思います。
攻撃する時の処理ですが、HPが半分以上なら通常攻撃だけ、半分より下ならちからため(次の攻撃の時に与えるダメージ2倍)と通常攻撃を交互にするものとします。
ダメージを受けた際の処理ですが、ダメージ-防御力をさらに2分の1したものをHPから減らすものとします。
#include "IEnemy.h"
class Golem : public IEnemy{
public:
Golem();
~Golem() {}
Attack() override;
Damage(int damage) override;
private:
int MaxHP; //最大HP
bool isThikara = false; //ちからためをしたかどうか
}
Golem::Golem()
{
//各種ステータスを初期化します
HP = 100;
MP = 0;
Atk = 30;
Def = 30;
Exp = 50;
Gold = 20;
MaxHP = HP;
}
Golem::Attack()
{
//HPが半分以上なら
if(HP * 2 >= MaxHP) {
//プレイヤーに攻撃
player->Damage(Atk);
}
//HPが半分を切っていたら
else {
//力を溜めていたら
if(isThikara) {
//プレイヤーに2倍の攻撃力で攻撃
player->Damage(Atk * 2);
//力溜めフラグをオフ
isThikara = false;
}
//溜めてなかったら
else {
//力溜めフラグをオン
isThikara = true;
}
}
}
Golem::Damage(int damage)
{
//受けるダメージ2分の1!
HP -= (damage - Def) / 2;
if(HP <= 0) {
Death();
}
}
実際にこれらのクラスを実際に使うとなると、こんな感じになる...と思います。
int main()
{
//プレイヤーを作る
Player* player = new Player;
//エネミーの配列
std::vector<IEnemy*> enemyList;
//スライムが現れた!
enemyList.push_back(new Slime);
enemyList.push_back(new Slime);
//ゴーレムが現れた!
enemyList.push_back(new Golem);
//戦闘!
while(true) {
//プレイヤーの攻撃!
player->Attack();
//エネミーの攻撃!
for(int i = 0; i < enemyList.size(); i++) {
//死んでなかったら
if(enemyList[i]->GetisDeath() == flase) {
//攻撃!
//SlimeやGolemクラスのAttack関数が実行される。
enemyList[i]->Attack();
}
}
.
.
.
}
}
以上です、拙い文章でしたが最後までご覧いただきありがとうございました。