在 C++ 中创建 pacman 碰撞检测的困难

Difficulties creating pacman collision detection in C++

我正在尝试制作 pac 游戏。我正在使用 C++ 和 sfml。现在一切看起来都很好,但问题是我不知道如何进行地图碰撞检测。我正在从 .bmp 文件加载地图。这是一张图片:

有没有办法设置墙壁坐标以便我可以在碰撞时检测到它们?

这是我的部分代码:

sf::RenderWindow window(sf::VideoMode(451, 500), "Packman", sf::Style::Close);
    sf::Image icon;
    icon.loadFromFile("pac_icon.png");

    window.setIcon(icon.getSize().x, icon.getSize().y, icon.getPixelsPtr());

    sf::Texture tMap;
    tMap.loadFromFile("pac_map.bmp");
    sf::Sprite sprMap;
    sprMap.setTexture(tMap);

    Pac oPac(window);

    while (window.isOpen())
    {
        sf::Event event;

        while (window.pollEvent(event))
        {
            if (event.type == sf::Event::EventType::Closed) 
            {
                window.close();
            }
        }   

        if (sf::Keyboard::isKeyPressed(sf::Keyboard::Right) 
            && !(sf::Keyboard::isKeyPressed(sf::Keyboard::Up) || sf::Keyboard::isKeyPressed(sf::Keyboard::Down)))
        {
            oPac.MoveRight();
        }

        if (sf::Keyboard::isKeyPressed(sf::Keyboard::Left) 
            && !(sf::Keyboard::isKeyPressed(sf::Keyboard::Up) || sf::Keyboard::isKeyPressed(sf::Keyboard::Down)))
        {
            oPac.MoveLeft();
        }

        if (sf::Keyboard::isKeyPressed(sf::Keyboard::Up))
        {
            oPac.MoveUp();
        }

        if (sf::Keyboard::isKeyPressed(sf::Keyboard::Down))
        {
            oPac.MoveDown();
        }

        if (sf::Keyboard::isKeyPressed(sf::Keyboard::Escape))
        {
            window.close();
        }


        window.clear();
        window.draw(sprMap);
        oPac.Update();
        window.display();
    }

您可以做的最简单的事情就是将您的世界划分为多个单元格

游戏的每个元素都可以放入一个图块中(例如,布尔值的二维数组,true 是 there is an element,false 是 there is no element)。

您的角色将能够通过您的控件改变其方向,并且其位置 x 和 y 将根据其方向随时间修改。

您必须制定一个 算法来检查在给定方向 的情况下是否可以从另一个瓦片到达一个瓦片。因此,您将能够朝一个方向移动您的角色,并在他无法到达瓷砖时停止他(这基本上是碰撞检测 + 解决碰撞)。

如果您想了解 2D 碰撞,这里有一篇关于 2D tilemap 碰撞的好文章https://jonathanwhiting.com/tutorial/collision/.

存储在二维数组中的图块地图示例:

bool world[5][5] =
{
{ 1, 1, 1, 1, 1 },
{ 1, 0, 0, 0, 1 },
{ 1, 0, 1, 0, 1 },
{ 1, 0, 0, 0, 1 },
{ 1, 1, 1, 1, 1 }
};

std::pair<int> playerPosition { 1, 1 };

当然,您可以从文件中加载这些信息。你可以想象一个非常基本的数据格式:

11111
10001
10101
10001
11111

一个 getline() 调用 + 对每一行的逐个字符解析将完成解析此文件的工作

专业提示:你也可以在一行中写下你的关卡信息,假设每5个字符你处理另一行(你的关卡高度)(取决于你的水平宽度)。它会减少解析时间,也会减少关卡文件的大小(例如:1111110001101011000111111

专业提示 2:您可以将关卡数据编码为整数或十六进制,因此,当您解析文件时,您需要转换将这些值转换为布尔数组(每个布尔值将由 integer/hexadecimal 的一位确定)。 127[decimal] 可以表示为 ‭01111111‬[binary],因此你可以用更少的字符写更多的信息,但是你必须用一些 ',' 分隔你的整数(例如:31,17,21,17,31 以保持与前面示例相同的地图)

您需要一种方法来在您的程序中用坐标表示墙壁,您将其与吃豆人进行比较。

我想您可以根据您提供的图像生成墙壁,但这可能是最困难的尝试。

我建议两种解决方案: - 要么保留你的图像,同时存储代表所有墙壁的一组矩形的坐标和大小,并在加载关卡时加载这两个文件 - 您可以不加载图像,而是将关卡的表示形式存储在文件中,加载该文件,然后从中构建关卡(逻辑和图形)。

另一种选择可能是像素完美碰撞,但我对此了解不多。

我对 sfml 不熟悉,但我可以想到三种通用的碰撞检测方法:

  1. 对 level/background 中 pacman 字符边界框内的所有像素求和。如果总和为 0,则所有像素都是黑色并且没有碰撞。但是,如果总和大于 0,则那里有蓝色像素,并且你有 运行 到墙上。当然,这并不是最快的方法。

  2. 像这样绘制第二张地图: 然后对照这张地图测试 pacman 的中心像素:如果像素是白色,那么你就可以了。如果不是,则发生碰撞。另外,如果针对 pacman 的一个角进行测试更容易,那么在创建地图时只需沿着正确的对角线移动白色区域即可。

  3. 在地图的所有墙壁周围绘制边界框:,然后将这些框的xy坐标存储在某个数据结构中,并在它们之间进行框-框碰撞检测和吃豆子盒子。这是一种更重数学的方法,但是边界框数据应该比存储完整的辅助位图占用更少的内存(即使这些位图可以是 1 位黑白)。