[Решено] Вот мой код полностью завершил свою работу:

#include <ncurses.h>
#include <random>
#include <ctime>
using namespace std;

//VARIABLES
///////////
    int tick_count = 0;
    char key;
    int width = 10, height = 10;

    class Entity {
    public:
        class Common {
        public:
            char icon;
            int x, y;
            char collide;
            int power = 0, speed = 0, defense = 0, health = 0;
            int hp;

            virtual void move(int dir) {
                switch (dir) {
                    case 1:
                        y--;
                        break;
                    case 2:
                        y++;
                        break;
                    case 3:
                        x--;
                        break;
                    case 4:
                        x++;
                        break;
                }
            }
            virtual ~Common() {}
        };

        class Player: public Common {
            public:
                int target, room;
                Player() {
                    icon = '@';
                    x = 1, y = 1;
                    collide = 'n';
                    power = 1, speed = 1, defense = 1, health = 10;
                    hp = health;
                }
        };
        class Kobold: public Common {
            public:
                Kobold() {
                    icon = 'K';
                    collide = 'n';
                    power = 1, speed = 0, defense = 1, health = 2;
                    hp = health;
                }
        };
        class Spider: public Common {
            public:
                Spider() {
                    icon = 'S';
                    collide = 'n';
                    power = 4, speed = 2, defense = 0, health = 3;
                    hp = health;
                }
        };
    };

    class Room {
    public:
        int width, height, difficulty, enemies;
    };

    Room room[1];
    Entity::Player player;
    Entity::Common * enemy[2];
    int enemy_ID, enemy_count = 2;
///////////

void INIT() {
    room[0].width = 10, room[0].height = 10, room[0].enemies = 0;
    player.x = player.y = 1, player.collide = 'n', player.room = 0;
    enemy[0] = new Entity::Kobold();
    enemy[1] = new Entity::Spider();
    enemy[0]->x = enemy[0]->y = 5;
    enemy[1]->x = enemy[1]->y = 8;
}

void DRAW() {
    clear();
    printw("ATK: %d, DEF: %d, HP: %d \n", player.power, player.defense, player.hp);
    printw("#");
    for (int i = 0; i < room[player.room].width - 1; i++) {
        printw("--");
    }
    printw("-#\n");
    for (int j = 0; j < room[player.room].height; j++) {
        printw("|");
        for (int i = 0; i < room[player.room].width; i++) {
            if (i == room[player.room].width - 1) {
                if (i == player.x and j == player.y) {
                    printw("%c", player.icon);
                }
                else if (i == enemy[enemy_ID]->x and j == enemy[enemy_ID]->y) {
                    printw("%c", enemy[enemy_ID]->icon);
                }
                else {
                    printw(" ");
                }

            }
            else {
                if (i == player.x and j == player.y) {
                    printw("%c ", player.icon);
                }
                else if (i == enemy[enemy_ID]->x and j == enemy[enemy_ID]->y) {
                    printw("%c ", enemy[enemy_ID]->icon);
                }
                else {
                    printw("  ");
                }
            }
        }
        printw("|\n");
    }
    printw("#");
    for (int i = 0; i < width - 1; i++) {
        printw("--");
    }
    printw("-#\n");

    //Debug
    mvprintw(1, 30, "%d %s %c %c", tick_count, typeid(enemy[0]).name(), player.collide, enemy[0]->collide);
}

void LOGIC() {
    //GAME LOGIC
    ////////////
        tick_count += 1;
        srand(time(NULL));
    ////////////

    //BATTLE LOGIC
    //////////////
        if (player.collide == 'e' or enemy[enemy_ID]->collide == 'p') {
            for (int i; i < enemy_count; i++) {
                if (enemy[enemy_ID]->collide == 'p')
                    player.target = i;
            }

            if (player.speed > enemy[player.target]->speed) {

            }
            else if (player.speed < enemy[player.target]->speed) {

            }
            else if (player.speed == enemy[player.target]->speed) {
                if (player.collide = 'e') {
                    if (rand() % 2) {
                        enemy[player.target]->hp -= player.power - enemy[player.target]->defense;
                        mvprintw(14, 0, "You hit %s with %d damage", enemy[player.target], player.power - enemy[player.target]->defense);
                        clrtoeol();
                    }
                    else {

                    }
                }
                else if (player.collide = 'n') {
                    player.hp -= enemy[player.target]->power - player.defense;
                    mvprintw(14, 0, "%s hit you with %d damage", enemy[player.target], enemy[player.target]->power - player.defense);
                    getch();
                    clrtoeol();
                }
            }
        }
    //////////////

    //PLAYER LOGIC
    //////////////
        if (player.x - 1 == enemy[enemy_ID]->x and player.y == enemy[enemy_ID]->y and key == 'a'
        or player.x + 1 == enemy[enemy_ID]->x and player.y == enemy[enemy_ID]->y and key == 'd'
        or player.y - 1 == enemy[enemy_ID]->y and player.x == enemy[enemy_ID]->x and key == 'w'
        or player.y + 1 == enemy[enemy_ID]->y and player.x == enemy[enemy_ID]->x and key == 's'
        )
            player.collide = 'e';
        else
            player.collide = 'n';

        if (player.x < 0)
            player.x = 0;
        else if (player.x == width)
            player.x = width - 1;
        else if (player.y < 0)
            player.y = 0;
        else if (player.y == height)
            player.y = height - 1;
    //////////////

    //ENEMY LOGIC
    /////////////
        for (enemy_ID = 0; enemy_ID < enemy_count - 1; enemy_ID++)
            enemy_ID++;

        if (enemy[enemy_ID]->x == player.x and enemy[enemy_ID]->y > player.y and enemy[enemy_ID]->y - 1 != player.y)
            enemy[enemy_ID]->move(1);
        else if (enemy[enemy_ID]->x == player.x and enemy[enemy_ID]->y < player.y and enemy[enemy_ID]->y + 1 != player.y)
            enemy[enemy_ID]->move(2);
        else if (enemy[enemy_ID]->y == player.y and enemy[enemy_ID]->x > player.x and enemy[enemy_ID]->x - 1 != player.x)
            enemy[enemy_ID]->move(3);
        else if (enemy[enemy_ID]->y == player.y and enemy[enemy_ID]->x < player.x and enemy[enemy_ID]->x + 1 != player.x)
            enemy[enemy_ID]->move(4);

        if (enemy[enemy_ID]->x - 1 == player.x and player.y == enemy[enemy_ID]->y
        or enemy[enemy_ID]->x + 1 == player.x and player.y == enemy[enemy_ID]->y
        or enemy[enemy_ID]->y - 1 == player.y and player.x == enemy[enemy_ID]->x
        or enemy[enemy_ID]->y + 1 == player.y and player.x == enemy[enemy_ID]->x
        )
            enemy[enemy_ID]->collide = 'p';
        else
            enemy[enemy_ID]->collide = 'n';
    /////////////
}

void INPUT() {
    key = getch();
    switch (key) {
        case 'w':
            player.move(1);
            break;
        case 's':
            player.move(2);
            break;
        case 'a':
            player.move(3);
            break;
        case 'd':
            player.move(4);
            break;
    }
}

int main() {
    initscr();
    cbreak();
    noecho();
    INIT();
    DRAW();
    while (key != '\n') {
        //Main Loop
        ///////////////////////
            INPUT();
            LOGIC();
            DRAW();
        ///////////////////////
        refresh();
    }
    endwin();
    return 0;
}

[ВОПРОС] (Извините за потенциально сбивающий с толку заголовок) Я делаю rogue-подобный старый стиль на основе ascii. Вот код:

    class Entity {
    public:
        class Player {
        public:
            int x, y, target;
            char collide;
            int power = 1, speed = 1, defense = 1, health = 10;
            int hp = health;
            
            void move(int dir);
        };
        class Kobold {
        public:
            int x, y;
            char collide;
            const int power = 2, speed = 1, defense = 1, health = 3;
            int hp = health;

            void move(int dir);
        };

Моя цель состоит в том, чтобы иметь массив с именем enemy, и каждое значение массива (или как бы оно там ни называлось) имело другой класс в качестве своего типа. Например: enemy[0] может быть объектом Kobold, который является членом Entity, и иметь enemy[5] быть объектом Spider, который является членом Entity и т. Д. Таким образом, легче иметь врагов, которых легче объявлять и ссылаться в циклах for. Я прошу, как таким образом объявить массив enemy. Было бы неплохо получить и ответить на то, что я спрашиваю, но я не уверен, что это возможно простым способом, поэтому, если у кого-то есть более простой или легкий способ достичь моей цели, я также хотел бы его увидеть. Заранее спасибо.

Изменить: пример кода (игнорируйте, что это не реально):

//Declaring an array with 8 values with no type
void enemy[8];

//Assigning the Kobold class to the first enemy as it's type
enemy[0] = Entity::Kobold;

//Assigning the Spider class to the 3rd enemy as it's type
enemy[2] = Entity::Spider;

//Player attacks 3rd enemy (spider) and inflicts damage
enemy[2].hp -= player.power;

//Checks if any enemy is dead, and if so then delete it
for (int i = 0; i < 8; i++) {
    if (enemy[i].hp <= 0) {
        //Removing enemy from it's class, therefore deleting it
        void enemy[i];
    }
}

//First enemy (Kobold) attacks player, inflicting damage
player.hp -= enemy[0].power;

//Player isn't dead, and attacks first enemy (Kobold) and kills it
enemy[0].hp -= player.power;

//Checks if any enemy is dead, and if so then delete it (again)
for (int i = 0; i < 8; i++) {
    if (enemy[i].hp <= 0) {
        //Removing enemy from it's class, therefore deleting it
        void enemy[i];
    }
}

printw("You Win!");

Я знаю, что это неэффективно и, вероятно, содержит некоторые ошибки, но оно просто помогает визуализировать то, что я пытаюсь сделать.

Редактировать:

Это похоже на то, что я спрашиваю: Возможно ли иметь массив различных объектов, происходящих из одного и того же базового класса?

Я пытаюсь создать массив как объект Entity, но я хочу, чтобы любое значение массива было объектом любого подкласса из Entity. Так что-то вроде этого:

Entity enemy[5];
enemy[0] = new Kobold;
enemy[4] = new Spider;

И тогда я могу сослаться на более простое:

for (int i = 0; i < 5; i++) {
    if (array[i].x == player.x and array[i].y == player.y) {
        printw("player and an enemy are in the same spot");
    }
}

Таким образом, суть в том, чтобы легко получить доступ к переменным от каждого врага (виртуально) сразу.

Другой пример (проверка, есть ли у врага 0 хп):

for (int i = 0; i < amount_of_enemies; i++) {
    if (enemy[i].hp <= 0) {
        //some code to delete the enemy and give player XP etc.
    }
}
1
DinoNuggies 10 Ноя 2021 в 05:26
4
Возможно, вы захотите прочитать о плойморфизме и виртуальных функциях в своем любимом учебнике по C ++.
 – 
Igor Tandetnik
10 Ноя 2021 в 05:32
«Так легче иметь врагов [...]» - прочтите это еще раз. Ваше описание проблемы предполагает, что "враг" - это концепция в вашем коде. Где находится класс enemy, реализующий эту концепцию?
 – 
JaMiT
10 Ноя 2021 в 06:09
1
Вы уверены, что Player и Kobold являются членами класса Entity? Разве они не должны быть подклассами Entity?
 – 
Serge Ballesta
10 Ноя 2021 в 13:12
И не подклассы, это внутренние классы . Но да, вероятно, не то, что хотел сделать опрашивающий.
 – 
Aziuth
10 Ноя 2021 в 14:20
Да вот что я имел в виду, мое плохое.
 – 
DinoNuggies
11 Ноя 2021 в 01:04

1 ответ

Лучший ответ

Часть 1

Здесь можно использовать два популярных способа. Один из них - использовать из C ++ 17. std :: any. Другой способ - использовать методы и интерфейсы virtual.

Подход std::any позволяет хранить любые несвязанные классы в одном массиве std::vector<std::any>. std :: any позволяет хранить разные типы в одном контейнере, поддерживая только один тип.

Позже при доступе к объектам из этого массива вы должны проверить и привести их типы к определенным типам и обработать каждый тип внутри предложения if. Каждый if будет иметь свой код обработки для этого типа.

С любым у вас могут быть совершенно разные методы и поля для каждого обрабатываемого класса, например методы .player_shout() (для Player) и .kobold_defense() (для Kobold) в приведенном ниже коде.

У массива также могут быть некоторые типы, которые вы не можете обработать или не знаете, тогда вы можете пропустить их или обработать другим способом, см. Код ниже:

Попробуйте онлайн!

#include <iostream>
#include <vector>
#include <any>

class Entity {
public:        
    class Player {
    public:
        int x, y, target;
        char collide;
        int power = 1, speed = 1, defense = 1, health = 10;
        int hp = health;
        
        void move(int dir) { std::cout << "Moved Player to dir " << dir << std::endl; }
        void player_shout() { std::cout << "Player shouted!" << std::endl; }
    };
    class Kobold {
    public:
        int x, y;
        char collide;
        const int power = 2, speed = 1, defense = 1, health = 3;
        int hp = health;

        void move(int dir) { std::cout << "Moved Kobold to dir " << dir << std::endl; }
        void kobold_defense() { std::cout << "Kobold defensed!" << std::endl; }
    };
};

void ProcessEntities(std::vector<std::any> & entities) {
    for (std::size_t i = 0; i < entities.size(); ++i) {
        auto & e = entities[i];
        if (e.type() == typeid(Entity::Player)) {
            Entity::Player & player = *std::any_cast<Entity::Player>(&e);
            player.move(2);
            player.player_shout();
        } else if (e.type() == typeid(Entity::Kobold)) {
            Entity::Kobold & kobold = *std::any_cast<Entity::Kobold>(&e);
            kobold.move(1);
            kobold.kobold_defense();
        } else
            std::cout << "Unknown entity " << i << " of type "
                << e.type().name() << std::endl;
    }
}

struct BadType {};

int main() {
    std::vector<std::any> entities = {
        Entity::Player(), Entity::Kobold(), Entity::Player(),
        Entity::Player(), BadType(), Entity::Kobold(), BadType()};
    ProcessEntities(entities);
}

Выход:

oved Player to dir 2
Player shouted!
Moved Kobold to dir 1
Kobold defensed!
Moved Player to dir 2
Player shouted!
Moved Player to dir 2
Player shouted!
Unknown entity 4 of type 7BadType
Moved Kobold to dir 1
Kobold defensed!
Unknown entity 6 of type 7BadType

Часть 2

Вы также можете использовать виртуальные методы для представления общего класса с некоторыми атрибутами (полями) и методы, общие для всех сущностей. Это называется подходом виртуального интерфейса.

Все сущности должны наследовать от этого общего интерфейса и реализовывать все унаследованные методы. Общий класс сам по себе не реализует методы интерфейса, хотя может при необходимости.

У вас может быть произвольная глубокая иерархия виртуальных классов, все возможно. В следующем примере показана реализация простого класса Common и его наследование всеми сущностями.

Вы также можете смешивать наследование виртуальных классов с подходом к приведению типов (это было сделано для std::any в Части 1 моего ответа). В следующем примере вызывается общий виртуальный метод move(), а также показан общий атрибут speed. Остальная логика выполняется посредством проверки типов с помощью метода type() и преобразования типов с помощью dynamic_cast, после преобразования вы можете использовать определенные (ненаследуемые) атрибуты каждой сущности.

Следовательно, вы можете произвольно смешивать виртуальное наследование с динамическим приведением типов.

Попробуйте онлайн!

#include <iostream>
#include <vector>
#include <memory>

class Entity {
public:
    class Common {
    public:
        int x, y;
        char collide;
        int power = 1, speed = 1, defense = 1, health = 10;
        int hp = health;

        virtual void move(int dir) = 0;
        virtual std::type_info const & type() const = 0;
        virtual ~Common() {}
    };

    class Player : public Common {
    public:
        int player_attr = 123;

        std::type_info const & type() const { return typeid(*this); }
        void move(int dir) { std::cout << "Moved Player to dir "
            << dir << " with health " << health << std::endl; }
        void player_shout() { std::cout << "Player shouted with attr "
            << player_attr << "!" << std::endl; }
    };
    class Kobold : public Common {
    public:
        int kobold_attr = 456;

        std::type_info const & type() const { return typeid(*this); }
        void move(int dir) { std::cout << "Moved Kobold to dir "
            << dir << " with health " << health << std::endl; }
        void kobold_defense() { std::cout << "Kobold defensed with attr "
            << kobold_attr << " !" << std::endl; }
    };
};

void ProcessEntities(std::vector<std::shared_ptr<Entity::Common>> & entities) {
    for (std::size_t i = 0; i < entities.size(); ++i) {
        auto & e = entities[i];
        std::cout << "Entity " << i << ": Speed " << e->speed << ". ";
        e->move(i % 3); // Common method
        if (e->type() == typeid(Entity::Player)) {
            Entity::Player & player = dynamic_cast<Entity::Player &>(*e);
            player.player_shout();
            std::cout << "Player attr " << player.player_attr << std::endl;
        } else if (e->type() == typeid(Entity::Kobold)) {
            Entity::Kobold & kobold = dynamic_cast<Entity::Kobold &>(*e);
            kobold.kobold_defense();
            std::cout << "Kobold attr " << kobold.kobold_attr << std::endl;
        } else
            std::cout << "Unknown entity " << i << std::endl;
    }
}

int main() {
    std::vector<std::shared_ptr<Entity::Common>> entities = {
        std::make_shared<Entity::Player>(), std::make_shared<Entity::Kobold>(),
        std::make_shared<Entity::Player>(), std::make_shared<Entity::Player>(),
        std::make_shared<Entity::Kobold>()};
    ProcessEntities(entities);
}

Выход:

Entity 0: Speed 1. Moved Player to dir 0 with health 10
Player shouted with attr 123!
Player attr 123
Entity 1: Speed 1. Moved Kobold to dir 1 with health 10
Kobold defensed with attr 456 !
Kobold attr 456
Entity 2: Speed 1. Moved Player to dir 2 with health 10
Player shouted with attr 123!
Player attr 123
Entity 3: Speed 1. Moved Player to dir 0 with health 10
Player shouted with attr 123!
Player attr 123
Entity 4: Speed 1. Moved Kobold to dir 1 with health 10
Kobold defensed with attr 456 !
Kobold attr 456

Если вам нужна более простая версия виртуального наследования, чтобы быть более понятной для вас, я создал этот более простой код:

Попробуйте онлайн!

class Entity {
public:
    class Common {
    public:
        int x, y;
        char collide;
        int power = 0, speed = 0, defense = 0, health = 0;
        int hp = health;
        
        virtual void move(int dir) = 0;
        virtual ~Common() {}
    };
    class Player : public Common {
    public:
        Player() { power = 1; speed = 1; defense = 1; health = 10; hp = health; }
        void move(int dir) {}
    };
    class Kobold : public Common {
    public:
        Kobold() { power = 2; speed = 1; defense = 1; health = 3; hp = health; }
        void move(int dir) {}
    };
    class Spider : public Common {
    public:
        Spider() { power = 3; speed = 2; defense = 2; health = 5; hp = health; }
        void move(int dir) {}
    };
};

int main() {
    Entity::Common * enemies[3] = {
        new Entity::Kobold(), new Entity::Spider()};
    enemies[0]->move(3);
    enemies[0]->hp -= enemies[1]->power;
}

Часть 3

Если два первых решения (из частей 1 и 2 моего ответа) вам не подходят, я могу предложить третье решение на основе std :: variant и std :: visit.

Я взял из вашего вопроса примеры того, как вы обрабатываете врагов и игрока. И немного изменил его, чтобы использовать мою третью идею использования std :: variant. См. Код ниже.

В классы врагов внесены небольшие изменения, такие как добавление конструктора копирования (см. CONSTR(Kobold);) и реализация типа сущности Empty для хранения пустых значений.

Вы можете видеть в следующем коде, что я создал два макроса EN и EN2. В основном, если вы храните своих врагов как std :: variant, вам нужно использовать один из этих двух макросов, чтобы получить доступ и / или изменить врага или взаимодействовать с игроком. Потому что вы не можете напрямую использовать тип std :: variant. Эти макросы используют std::visit для доступа к базовому определенному типу.

Чтобы начать использовать решение std :: variant, как показано ниже, вам просто нужно один раз определить специальный тип Enemy, просто перечислив все возможные классы врагов using Enemy = std::variant<Entity::Kobold, Entity::Spider, Entity::Dragon, Entity::Empty>;, см. Код.

Я создал класс Empty для хранения пустых значений в массиве. Проверить, пуст ли элемент, можно с помощью специального макроса EMPTY(enemy).

Попробуйте онлайн!

#include <variant>
#include <vector>
#include <iostream>

class Entity {
public:
    #define COPY(self, o) { self.x = o.x; self.y = o.y; self.collide = o.collide; self.power = o.power; self.speed = o.speed; self.defense = o.defense; self.health = o.health; self.hp = o.hp; }
    #define CONSTR(T) \
        T() {} \
        T(T const & o) { COPY((*this), o); } \
        T & operator =(T const & o) { COPY((*this), o); return *this; }

    class Player {
    public:
        int x, y, target;
        char collide;
        int power = 1, speed = 1, defense = 1, health = 10;
        int hp = health;
        
        void show_hp() const { std::cout << "Player health " << hp << std::endl; }
        void move(int dir) { std::cout << "Moved Player to dir " << dir << std::endl; }
    };
    class Kobold {
    public:
        int x, y;
        char collide;
        int power = 2, speed = 1, defense = 1, health = 1;
        int hp = health;

        CONSTR(Kobold);

        void show_hp() const { std::cout << "Kobold health " << hp << (hp <= 0 ? ", Dead!!!" : "") << std::endl; }
        void move(int dir) { std::cout << "Moved Kobold to dir " << dir << std::endl; }
    };
    class Spider {
    public:
        int x, y;
        char collide;
        int power = 3, speed = 1, defense = 1, health = 5;
        int hp = health;

        CONSTR(Spider);

        void show_hp() const { std::cout << "Spider health " << hp << (hp <= 0 ? ", Dead!!!" : "") << std::endl; }
        void move(int dir) { std::cout << "Moved Spider to dir " << dir << std::endl; }
    };
    class Dragon {
    public:
        int x, y;
        char collide;
        int power = 7, speed = 1, defense = 1, health = 20;
        int hp = health;

        CONSTR(Dragon);

        void show_hp() const { std::cout << "Dragon health " << hp << (hp <= 0 ? ", Dead!!!" : "") << std::endl; }
        void move(int dir) { std::cout << "Moved Dragon to dir " << dir << std::endl; }
    };
    class Empty {
    public:
        int x, y;
        char collide;
        int power = 0, speed = 0, defense = 0, health = 1;
        int hp = health;

        CONSTR(Empty);

        void show_hp() const { std::cout << "Empty!!!" << std::endl; }
        void move(int dir) { std::cout << "Empty!!!" << std::endl; }
    };
    #define EMPTY(x) (typeid(x) == typeid(Entity::Empty))
};

using Enemy = std::variant<
    Entity::Kobold, Entity::Spider, Entity::Dragon, Entity::Empty>;

#define EN(_pos, code) \
    std::visit([&](auto & enemy) code, enemies[_pos]);

#define EN2(_pos0, _pos1, code) \
    std::visit([&](auto & enemyA) { \
        std::visit([&](auto & enemyB) code, enemies[_pos1]); \
    }, enemies[_pos0]);

void Process(std::vector<Enemy> & enemies) {
    Entity::Player player;

    EN(2, {
        // Player attacks 3rd enemy (spider) and inflicts damage
        enemy.show_hp();
        enemy.hp -= player.power;
        enemy.show_hp();
    });

    //Checks if any enemy is dead, and if so then delete it
    for (int i = 0; i < enemies.size(); ++i) {
        EN(i, {
            if (!EMPTY(enemy) && enemy.hp <= 0) {
                //Removing enemy from it's class, therefore deleting it
                enemies[i] = Entity::Empty();
                std::cout << "Enemy " << i << " is dead and removed." << std::endl;
            }
        });
    }

    EN(0, {
        player.show_hp();
        //First enemy (Kobold) attacks player, inflicting damage
        player.hp -= enemy.power;
        player.show_hp();

        enemy.show_hp();
        //Player isn't dead, and attacks first enemy (Kobold) and kills it
        enemy.hp -= player.power;
        enemy.show_hp();
    });

    //Checks if any enemy is dead, and if so then delete it
    for (int i = 0; i < enemies.size(); ++i) {
        EN(i, {
            if (!EMPTY(enemy) && enemy.hp <= 0) {
                //Removing enemy from it's class, therefore deleting it
                enemies[i] = Entity::Empty();
                std::cout << "Enemy " << i << " is dead and removed." << std::endl;
            }
        });
    }

    EN2(3, 4, {
        enemyA.show_hp();
        enemyA.hp -= enemyB.power;
        enemyA.show_hp();

        enemyB.show_hp();
        enemyB.hp -= enemyA.power;
        enemyB.show_hp();
    });

    //Checks if any enemy is dead, and if so then delete it
    for (int i = 0; i < enemies.size(); ++i) {
        EN(i, {
            if (!EMPTY(enemy) && enemy.hp <= 0) {
                //Removing enemy from it's class, therefore deleting it
                enemies[i] = Entity::Empty();
                std::cout << "Enemy " << i << " is dead and removed." << std::endl;
            }
        });
    }

    std::cout << "You win!" << std::endl;
}

int main() {
    std::vector<Enemy> enemies = {
        Entity::Kobold(), Entity::Dragon(), Entity::Spider(),
        Entity::Spider(), Entity::Dragon(), Entity::Kobold()};
    Process(enemies);
}

Выход:

Spider health 5
Spider health 4
Player health 10
Player health 8
Kobold health 1
Kobold health 0, Dead!!!
Enemy 0 is dead and removed.
Spider health 5
Spider health -2, Dead!!!
Dragon health 20
Dragon health 17
Enemy 3 is dead and removed.
You win!
1
Arty 15 Ноя 2021 в 07:12
Спасибо за подробный ответ, но, похоже, это не правильный ответ на мой вопрос. Мне нужно несколько объектов, действующих как разные внутриигровые враги с атрибутами, заданными через класс, но мне также нужно изменить класс для врагов. Мой план состоял в том, чтобы иметь несколько вражеских объектов и просто повторно использовать их снова и снова, поэтому, если у меня, например, 8 вражеских объектов, я хочу иметь возможность изменить 3 из них на Кобольдов и 2 на Пауков и т. Д., И затем позже, когда игрок входит в другую комнату, я могу использовать те же объекты и изменить их тип на что-то другое.
 – 
DinoNuggies
11 Ноя 2021 в 01:00
Однако, если подумать, возможно, лучше будет просто создать новый объект для каждого встреченного врага.
 – 
DinoNuggies
11 Ноя 2021 в 01:01
Обновленный вопрос
 – 
DinoNuggies
11 Ноя 2021 в 01:32
Не обращайте внимания на мой первый комментарий, я неправильно прочитал код с первого раза, это фактически ответ на мой вопрос. Спасибо, очень информативно и легко читается, прошу прощения, что не правильно прочитал.
 – 
DinoNuggies
11 Ноя 2021 в 01:40
Да, всегда лучше создать новый объект, если он другого типа. Если он другого типа, но имеет много общих атрибутов с предыдущим объектом, вы просто копируете общие атрибуты в новый объект, например, с помощью конструктора. Если он того же типа, вы можете повторно использовать его и изменить необходимые поля для выполнения миграции. Поскольку у вас есть консольная игра в стиле RPG, скорость не имеет значения, в этом случае самый простой способ понять - просто воссоздать новые объекты и скопировать необходимые поля.
 – 
Arty
11 Ноя 2021 в 06:21