You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

C++如何声明可存储Entity不同子类实例的数组实现Roguelike敌人管理

已解决完整代码

以下是写完的全部可运行代码:

#include <ncurses.h>
#include <random>
#include <ctime>
#include <typeinfo>
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;
}
原需求说明

开发复古ASCII风格Roguelike游戏时,需要实现一个enemy数组,数组的每个元素可以是不同敌人类型的实例,比如enemy[0]是Entity::Kobold对象,enemy[1]是Entity::Spider对象,方便统一遍历处理所有敌人的属性(坐标、血量、攻防等)。
初始代码结构如下:

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);
    };
};
实现逻辑

核心是利用C++的多态特性,通过基类指针数组存储不同子类的实例:

  • 首先抽象公共基类:将所有实体(玩家、敌人)共有的属性和方法抽取为Entity::Common基类,公共属性包括坐标、图标、碰撞状态、攻防血数值,公共方法包括move移动函数,必须声明虚析构函数避免内存泄漏。
  • 所有实体类继承基类:Player、Kobold、Spider都继承Entity::Common,在各自的构造函数中初始化专属的属性值即可。
  • 声明敌人数组:声明基类指针数组Entity::Common * enemy[敌人数量];即可,如果需要动态增减敌人,更推荐用vector<Entity::Common*>动态容器,适配Roguelike随机生成敌人的需求。
  • 初始化敌人实例:直接给数组元素分配子类实例即可:
enemy[0] = new Entity::Kobold();
enemy[1] = new Entity::Spider();
  • 统一遍历处理:直接通过基类指针访问公共属性,不需要区分具体敌人类型:
// 检查敌人与玩家坐标重合
for (int i = 0; i < enemy_count; i++) {
    if (enemy[i]->x == player.x && enemy[i]->y == player.y) {
        printw("player and an enemy are in the same spot");
    }
}

// 处理敌人死亡逻辑
for (int i = 0; i < enemy_count; i++) {
    if (enemy[i]->hp <= 0) {
        delete enemy[i];
        enemy[i] = nullptr;
        // 此处可添加掉落道具、增加玩家经验等逻辑
    }
}

如果使用C++11及以上版本,更推荐用智能指针unique_ptr/shared_ptr代替裸指针,不需要手动管理内存,安全性更高。


内容的提问来源于stack exchange,提问作者DinoNuggies

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.26 14:24:03