c++ - 将对象声明为数组,然后将同一数组中的某个值的类型声明为子类
问题描述
[已解决]这是我的全部代码都完成了工作:
#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;
}
[问题](抱歉,标题可能令人困惑)我正在制作一个老式的基于 ascii 的 Rogue-like。这是一些代码:
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
,haveenemy[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 hp):
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部分
这里可以使用两种流行的方法,一种是使用 C++17 的std::any。另一种方法是使用virtual
方法和接口。
std::any
方法允许在同一个数组中存储任何不相关的类std::vector<std::any>
。std::any 允许将不同类型存储在仅支持单一类型的单个容器中。
稍后当从该数组访问实体时,您必须检查它们的类型并将其转换为特定类型并处理if
子句内的每个类型。每个if
都会有特定于这种类型的处理代码。
对于任何处理的类,您都可以拥有完全不同的方法和字段,例如下面代码中的方法.player_shout()
(for Player
) 和.kobold_defense()
(for 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!
推荐阅读
- nginx - 如何从 Nginx 的基本身份验证中排除路由?
- typescript - Axios + Typescript:如何传递“ResponseType”类型的参数?
- android - VPNService 权限对话框是否可以通过?
- android - 如何将数据库从内部存储复制到外部存储(假设是下载文件夹)?
- javascript - 检索作为字符串返回的数组的值
- terraform-provider-gcp - GCP-terraform 删除角色但不重新创建(1.20)
- react-native - 如何隐藏不允许使用“navigationOptions”实现的所有类的标题的“createBottomTabNavigator”的标题
- azure-devops - Need to add some more fields to Reason after State is changed to Resolved
- python - 芹菜工人尽管告诉他们不要这样做,但立即接受了 5 份工作
- c++ - 带有 PBO 的 GPU 的视频流在 OpenGL 中加载 ImageQT