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
相关产品推荐
相关产品推荐

