我一直在编写一个冒险游戏,并且在整个二维阵列地牢中运动之前一切正常。地图是他们自己的结构,而角色是一个类。 Character类的成员变量之一是Map curMap
,它表示玩家当前所在的地图。但是,在Main.cpp中进行测试时,工作但地图不能正确更新。这是代码,之后我将解释我已经完成的工作。
Maps.h
#include <string>
#include <vector>
using namespace std;
struct Map {
vector<string> layout;
vector<int> exits; //indices in allMaps array
string name;
int level;
Map();
Map(vector<string> l, vector<int> e, string n, int lvl);
};
extern Map noMap;
extern Map highashPlainsA, highashPlainsB, highashPlainsC; //Highash Plains
extern Map alnwick, alnwickForge, alnwickMarket, alnwickInn; //Alnwick
extern vector<Map> allMaps;
extern vector<string> blankLayout;
extern vector<string> plainsLayoutA, plainsLayoutB, plainsLayoutC; //Highash Plains
extern vector<string> alnwickLayout; //Major Towns
extern vector<string> forgeLayout, marketLayout, innLayout; //Town Buildings
Maps.cpp
#include "Maps.h"
Map::Map() :
layout(blankLayout),
exits({0, 0, 0, 0}),
name("_NO_MAP_"),
level(0){}
Map::Map(vector<string> l, vector<int> e, string n, int lvl) :
layout(l),
exits(e),
name(n),
level(lvl){}
Map noMap = Map();
Map highashPlainsA = Map(plainsLayoutA, {4, 3, 2}, "Highash Plains A", 1);
vector<Map> allMaps = {noMap, highashPlainsA};
vector<string> blankLayout =
{"##############################",
"# #",
"# #",
"# #",
"# #",
"# #",
"# #",
"# #",
"# #",
"##############################"};
vector<string> plainsLayoutA =
{"##########################33##",
"# G ##### ####### E #### 2",
"# # # D # # G # 2",
"# # # ## ####################",
"# # ##E# # G G#",
"#E# G# G G#",
"# # ##########E############",
"# #### #DE G ED#",
"# ###D #DE G ED#",
"################111###########"};
Character.h (我在这里删掉了很多不重要的代码)
#include "Maps.h"
#include <iostream>
class Character {
Map curMap;
public:
Character();
~Character();
void SetMap(Map map);
Map GetMap();
};
extern Character player;
Character.cpp (左边说的不重要的代码也在这里)
#include "Character.h"
Character::Character() : curMap(highashPlainsA){}
Character::~Character(){}
void Character::SetMap(Map map){
cout << "OLD MAP NAME: " << curMap.name << "\n";
curMap = map;
curMap.layout = map.layout;
curMap.exits = map.exits;
curMap.name = map.name;
curMap.level = map.level;
cout << "NEW MAP NAME: " << curMap.name << "\n";
}
Map Character::GetMap(){
return curMap;
}
Character player = Character();
Main.cpp的
#include "Character.h"
int main(){
player.SetMap(highashPlainsA);
cout << "\nSIZE OF CURRENT LAYOUT: " << player.GetMap().layout.size() << "\n";
cout << "SIZE OF PLAINS A: " << plainsLayoutA.size() << "\n";
cout << "Name of map: " << player.GetMap().name << "\n";
// cout << "Layout of map: \n" << player.GetMap().layout[0] << "\n" << player.GetMap().layout[1] << "\n" << player.GetMap().layout[2] << "\n"<< player.GetMap().layout[3] << "\n"; // Crashes because for whatever reason the layout size is 0
return 0;
}
经过测试的内容:
在Character::SetMap()
我甚至尝试更改每个单独的变量,但是不应该将Map设置为整体更改所有这些值?另外,在Map::Map(vector<string> l, vector<int> e, string n, int lvl)
中,我在没有初始化列表的情况下尝试了它,但它仍然无效。我并没有真正期待它,但我猜它值得一试。
问题:
控制台输出:
OLD MAP NAME: Highash Plains A
NEW MAP NAME: Highash Plains A
SIZE OF CURRENT LAYOUT: 0
SIZE OF PLAINS A: 10
Name of map: Highash Plains A
字符类 初始化curMap
成员,并调用默认构造函数来执行此操作。这是预期的功能,因为在设置过程中播放器所处的地图无关紧要。但是,在尝试将地图设置为另一个预先创建的Map实例时(使用SetMap()
,一切都从那里开始下降。根据控制台记录,curMap
的名称设置为正确的名称(旧地图) NAME)并且在设置为新映射时保持这一点。但是,布局返回一个空向量,而目标向量的直接大小的日志给出了正确的值。发生了什么,我该如何修复它? / p>
答案 0 :(得分:0)
已解决:您的问题是HighplainsA的构造函数是在plainsLayout的构造函数之前调用的。那是因为所有构造函数都在全局范围内,因此它们运行的顺序归结为纯粹的运气。这是为什么在全局范围内使用静态变量通常只是一个糟糕的想法的一个例子。
当我将调试语句放入Map的构造函数时,布局数据为空。这是因为布局数据自己的构造函数尚未执行,它是一个空向量。但是,一旦你点击main,所有构造函数都已经运行,所以看起来布局是正确的。创建地图时,这是不正确的。
请注意,如果您尝试做正确的事情并将字符串的原始const char * []表更改为向量变量,则可能会遇到这个隐藏错误。原始char *表没有构造函数,它是在编译时确定的一些内存地址,而向量必须在它们有效之前调用构造函数。解决该问题的一种方法是将引用存储到Map中的布局向量而不是复制它。这样,当您在main中实际访问它时,所有数据都将被构造,而全局值只是连接点。
但是,根本不要依赖全局变量,因为只要你给出了一个具有构造函数的类类型,就必须在程序开始时调用它。如果您使用另一个具有构造函数的对象(例如用于布局的向量),并且在构造函数中将其用于同样位于全局范围内的另一个对象,则这尤其糟糕。这是任何人猜测哪一个会先跑。
基本上,您希望为依赖于其他数据的所有数据创建初始化函数。然后,您就可以手动控制数据初始化的顺序。
~~~
其他一些建议是在访问地图时尽量减少您创建的地图的副本数量:
Map &Character::GetMap(){
return curMap;
}
^这会返回对实际地图的引用。你每次调用它时都会制作另一个副本,然后在完成使用时删除副本。
至于存储地图。目前,Character还有一张地图的副本。这与不在Character中的地图对象不同。我不知道这是不是设计但你可能不打算这样做。地图需要被许多类使用,例如敌人,以及角色。因此,每个角色都不应该有地图的完整副本。
引用可以存储在类中,但仅限于创建对象时,因此如果使用引用来存储地图,则在创建角色后无法更改它。但是指针与引用类似,但可以在运行时进行更改。例如你可以这样做:
class Character
{
Map *currMap; // * here specifies that it is a pointer
void setMap(Map &newMap) // & here specifies that it is a reference
{
currMap = &newMap; // & here, turns a reference into a pointer
}
Map &Character::GetMap() // & here, is returning a reference
{
return *curMap; // * here, turns a pointer into a reference
}
}
指针* currmap保存地图的地址。传入引用&amp; newMap,然后将内存地址(&amp; newMap)保存在currMap中。不要过于混淆*和&amp;,它们在这里稍微不同的情境中使用,所以你只需要记住哪一个场合。要从指针访问子值,您可以采用以下方法之一:
currMap->name; // get a subvalue from a pointer
(*currMap).name; // * on the left here turns a pointer into a reference
答案 1 :(得分:0)
我认为您还应该将地图传递给您的updateMap函数:
void Character::UpdateMap(Map map){
curMap = map;//and all other initiations depending on your object
sHandler.ClearScreen(); //In a separate class that does work, is just a call to system("cls")
sHandler.DisplayMap(curMap); //For loop from 0 to (but not including) the size of the layout vector, uses cout to print each row. Works given the correct layout.
}