我开始使用Allegro 5在C ++中使用Visual Studio 2017创建游戏。为了便于管理图像,我创建了一个ImageLoader类,可以加载和存储所有活动图像,并根据需要销毁它们。它使用将文件名与相应图像匹配的映射来实现。 目前我的main()代码如下所示:
int main(){
if (!al_init()) {
al_show_native_message_box(NULL, NULL, NULL, "Could not intialize allegro 5.", NULL, NULL);
return -1;
}
ALLEGRO_DISPLAY *display = al_create_display(DEFAULT_SCREEN_WIDTH, DEFAULT_SCREEN_HEIGHT);
al_set_window_title(display, "Game title");
// make usre the display was created correctly
if (!display) {
al_show_native_message_box(display, "Title", "settings", "Could not create Allegro window.", NULL, NULL);
}
// intialize fonts, primitives, keyboard,etc.
al_init_font_addon();
al_init_ttf_addon();
al_init_primitives_addon();
al_install_keyboard();
if(!al_init_image_addon()) {
al_show_native_message_box(display, "Error", "Error", "Failed to initialize al_init_image_addon!",
NULL, ALLEGRO_MESSAGEBOX_ERROR);
return -1;}
ImageLoader image_loader;
ImageLoader然后为播放器加载图像:
ALLEGRO_BITMAP *image = al_load_bitmap(filename);
if (image == NULL) {
std::cout << filename << std::endl;
std::cout << "loader failed to load image" << std::endl;
}
image_map[filename] = image;
当我测试这个部分时,它似乎工作,因为图像不是null。请注意,在ImageLoader.h中声明image_map是这样的:
std::map<const char *, ALLEGRO_BITMAP*> image_map;
当我尝试从ImageLoader获取图像时,问题出现在我的主游戏循环中:
for (GameImage image : imageList) {
ALLEGRO_BITMAP *draw_image = image_loader.get_current_image(image);
if (draw_image == NULL) {
//TODO: error handling
std::cout << "no image" << std::endl;
}
else
al_draw_bitmap(draw_image, image.get_x(), image.get_y(), NULL);
}
我的检查始终显示draw_image
为空。以下是get_current_image的代码:
ALLEGRO_BITMAP * ImageLoader::get_current_image(GameImage image)
{
return image_map[image.get_image_filename()];
}
我已经测试过以确保我在这里使用相同的文件名,就像我加载图像并通过检查if (image_map.find(image.get_image_filename()) == image_map.end())
将其存储在地图中一样,但即使返回false,ALLEGRO_BITMAP指针仍然为空。我已经尝试制作地图存储位图而不是指针,但是当我尝试向地图添加元素时,这会给我一个错误,因为我不允许像这样修改地图值。为什么这些指针在我设置它们的时间和检索它们的时间之间变为空?我也对我应该如何存储我的位图的其他建议持开放态度。
std::map<std::string, ALLEGRO_BITMAP*> image_map
,GameImage存储的文件名现在为std::string image_filename
。 ImageLoader.cpp中的load_image
现在看起来像这样:
ALLEGRO_BITMAP *image = al_load_bitmap(filename.c_str());
if (image == NULL) {
std::cout << filename << std::endl;
std::cout << "loader failed to load image" << std::endl;
}
image_map[filename] = image;
最后,get_current_image
仍然是相同的:
ALLEGRO_BITMAP * ImageLoader::get_current_image(GameImage image)
{
return image_map[image.get_image_filename()];
}
然而,同样的问题仍然存在。我还检查了图像映射的大小,并且在整个程序的持续时间内它保持为1,我插入的图像的文件名作为键,值作为非空指针指向位图并且看起来变为在某些时候为空。
编辑2:
将char数组更改为字符串并修复get_current_image()
以便在找不到搜索到的文件名时它不再添加到地图中,我发现在加载图像时我也犯了一个错误当我最初发布问题时,我忘了包括:
current_screen.load_images(image_loader);
事实证明,我写过load_images()
就是这样:
void MainGameScreen::load_images(ImageLoader loader)
...意味着函数对loader
的调用实际上并没有应用于我传入的ImageLoader。我将其更改为:
void MainGameScreen::load_images(ImageLoader& loader)
......现在一切正常。
答案 0 :(得分:1)
您的问题是使用const char *
作为密钥意味着map
将执行直接地址比较,即&#34;字符串的地址是否等于字符串的地址我抱着记忆&#34;。这几乎肯定不是你想要进行字符串比较的方式。
根据您的使用情况,实际上有两个解决此问题的方案。第一种是简单地将const char *
更改为std::string
,这是最简单的解决方案以及默认情况下应该完成的操作。
std::map<std::string, ALLEGRO_BITMAP*> image_map;
而且,从广义上讲,无论您使用字符串的任何地方,都应该使用std::string
或std::string const&
。没有理由使用其他任何东西。
......除非您关注绩效。如果您正在编写游戏,性能几乎肯定是您关心的,这将我们带入第二个解决方案。必须对地图进行大量查找才能调用大量的比较,虽然这通常不是一个很大的问题,但这是因为每次比较都是一个完整的基于字符串的相等检查。 / p>
解决方法是,在加载图片时,为每个图片发出一个唯一ID(如int64_t
或uint64_t
),并将这些值分配给GameImage
对象而不是文件名或路径名。然后,当您执行查找时,使用该ID执行查找。
由你决定。使用const char *
替换std::string
几乎肯定会修复代码中的逻辑错误并使其按预期方式工作。如果您发现必须进行所有这些字符串比较会大大减慢您的程序速度,那么剩下的就更像是一个优化问题。
编辑:
您的新问题是std::map
的{{1}}功能会在找不到任何预先存在的图像时自动插入默认值(在本例中为operator[]
)请求的名称。你想要的代码看起来更像是这样:
nullptr
这样,找不到图像的失败不会欺骗您使用的任何调试工具,以认为有效(空)值存储在该位置。
如果您想使用内置的异常工具,也可以将其简化为:
ALLEGRO_BITMAP * ImageLoader::get_current_image(GameImage image)
{
auto it = image_map.find(image.get_image_filename());
if(it == image_map.end()) return nullptr;
else return it->second;
//return image_map[image.get_image_filename()];
}