【问题标题】:Allegro 5: trouble storing bitmaps in a std mapAllegro 5:在标准图中存储位图时遇到问题
【发布时间】:2017-05-02 20:46:35
【问题描述】:

我开始使用 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;

当我测试这部分时,它似乎可以工作,因为图像不为空。注意 image_map 在 ImageLoader.h 中是这样声明的:

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 指针仍然为空。我尝试让地图存储位图而不是指针,但是当我尝试向地图添加元素时,这给了我一个错误,因为我不允许像这样修改地图值。为什么这些指针在我设置它们的时间和我检索它们的时间之间会变为空?对于如何存储位图,我也愿意接受其他建议。

编辑:我修改了我的项目,将文件名的实例作为 char 数组更改为 std::strings。图像映射现在在 ImageLoader.h 中声明为 std::map&lt;std::string, ALLEGRO_BITMAP*&gt; 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() 以便在找不到搜索的文件名时不再添加到地图后,我发现在一行中加载图像时我也犯了一个错误 I'我最初发布问题时忘记包括:

current_screen.load_images(image_loader);

事实证明,我是这样写的load_images()

void MainGameScreen::load_images(ImageLoader loader)

...这意味着函数对loader 的调用实际上并未应用于我传入的 ImageLoader。我将其更改为:

void MainGameScreen::load_images(ImageLoader& loader)

...现在一切正常。

【问题讨论】:

    标签: c++ visual-studio bitmap allegro


    【解决方案1】:

    您的问题是使用const char * 作为您的密钥意味着map 将执行直接地址比较,即“字符串的地址是否等于我在内存中保存的字符串的地址” .这几乎肯定不是您想要执行字符串比较的方式。

    这个问题实际上有两种解决方案,具体取决于您的用例。第一种是简单地将const char *更改为std::string,这是最简单的解决方案,默认情况下应该这样做。

    std::map<std::string, ALLEGRO_BITMAP*> image_map;
    

    而且,从广义上讲,在使用字符串的任何地方,您都应该使用std::stringstd::string const&amp;。没有理由使用其他任何东西。

    ...除非您担心性能。如果您正在编写游戏,性能几乎肯定是您关心的事情,这将我们带到了第二种解决方案。必须对地图进行大量查找将调用大量比较,虽然这通常不是一个大问题,但它在这里是因为每个比较都是一个成熟的基于字符串的相等性检查。

    解决方案是,当您加载图像时,为每个图像分配一个唯一 ID(如 int64_tuint64_t),并将这些值分配给 GameImage 对象而不是文件名或路径名。然后,当您进行查找时,使用该 ID 执行查找。

    这取决于你。用std::string 替换const char * 几乎可以肯定会修复代码中的逻辑错误并使其按您期望的方式工作。如果您发现必须进行所有这些字符串比较会显着降低您的程序速度,那么剩下的就是一个优化问题。

    编辑:

    您的新问题是std::mapoperator[] 函数在找不到任何具有所请求名称的预先存在的图像时会自动插入默认值(在本例中为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()];
    }
    

    这样,找不到图像不会欺骗您使用的任何调试工具,以为该位置存储了有效(空)值。

    如果您想改用内置的异常工具,也可以将其简化为:

    ALLEGRO_BITMAP * ImageLoader::get_current_image(GameImage image)
    {
        //Will throw an exception if nothing is found
        return image_map.at(image.get_image_filename());
    }
    

    【讨论】:

    • 我做了这个更改,但问题仍然存在。我认为文件名键与我最初输入的键相匹配,因为“image_map.find(image.get_image_filename()) == image_map.end()”返回 false,但我调用 get_current_image() 时的值仍然为空。跨度>
    • @user3726962 将get_current_image 的修订版添加到您的原始帖子中。
    • 我没有更改 get_current_image,因为它不包含键的类型,但 GameImage 中的 get_image_filename 现在返回字符串而不是字符数组。
    • 我做了你建议的改变,但我仍然遇到同样的问题。但是,我现在发现image_map的size()在加载图像并将其添加到load_image()中的地图后立即为1,但当我在get_current_image()中再次检查时变为0。我不确定地图可能会在哪里改变大小。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-10-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-06-13
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多