5
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

継承を用いてRPGに出てくるような敵クラスを実装してみる

5
Last updated at Posted at 2019-09-24

敵クラスを継承を用いて実装していきたいと思います。

基底クラス

基底クラスには全ての敵に共通するような変数、あるいは関数を実装したいと思います。

まずは変数について考えていきましょう。
ドラクエのようなRPGに出てくる敵を思い浮かべてみてください。
その敵全てに共通するようなステータスは何だと思いますか?
思いつくだけでこれくらいはあると思います。
 ・HP
 ・MP
 ・攻撃力
 ・防御力
 ・倒したときに貰える経験値
 ・倒したときに貰えるお金
 ・死んだかどうかの判定

ではこれらのステータスを基底クラスに実装してみましょう。

IEnemy.h
#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つを実装していきます。

IEnemy.h
#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から減らすものとします。

Slime.h
#include "IEnemy.h"
class Slime : public IEnemy{
public:
    Slime();
    ~Slime() {}
    Attack() override;
    Damage(int damage) override; 
private:
    
};
Slime.cpp
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から減らすものとします。

Golem.h
#include "IEnemy.h"
class Golem : public IEnemy{
public:
    Golem();
    ~Golem() {}
    Attack() override;
    Damage(int damage) override; 
private:
    int MaxHP;                //最大HP
    bool isThikara = false;   //ちからためをしたかどうか
}
Golem.cpp
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();
    }
}

実際にこれらのクラスを実際に使うとなると、こんな感じになる...と思います。

main.cpp
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();
            }
        }
        .
        .
        .
    }
}

以上です、拙い文章でしたが最後までご覧いただきありがとうございました。

5
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?