C++ Sfml,如何为我的 Sprite 创建一个碰撞框

C++ Sfml, How can I create a collision box for my Sprite

我有问题要问sfml

我对 C++ 和 sfml 比较陌生。 我正在尝试创建 Space Invaders 类型的游戏。 我目前有一些碰撞问题, 在敌人的子弹和火箭之间, 我说的是第 145 行。这一行:

if (collide(rocket, enemy_bullets[i]))
{
    window.close();
}

你能创造出类似碰撞盒的东西吗?, 因为我不想和整个火箭精灵发生碰撞, 我只想与它的一部分发生碰撞,例如透明部分。

#include <SFML/Graphics.hpp>
#include <iostream>
#include <string>
#include <vector>
#include <chrono>

void print(std::string string)
{
    std::cout << string << std::endl;
}

sf::CircleShape create_bullet(sf::Vector2f possition, sf::Int16 offset)
{
    sf::CircleShape circel;
    circel.setRadius(10);
    circel.setPosition(possition.x + offset, possition.y);
    return circel;
}

bool collide(sf::Sprite a, sf::CircleShape b)
{
    return a.getGlobalBounds().intersects(b.getGlobalBounds());
}

int main()
{
    int speed;
    speed = 25;

    sf::RenderWindow window(sf::VideoMode(1200, 800), "Space Invaders", sf::Style::Titlebar | sf::Style::Close);
    sf::Texture rocket_texture;
    if (!rocket_texture.loadFromFile("data/rocket.png"))
    {
        print("Problem with loding file data/rocket.png");
        exit(-1);
    }

    sf::Texture enemy_texture;
    if (!enemy_texture.loadFromFile("data/enemy.png"))
    {
        print("Problem with loding file data/enemy.png");
        exit(-1);
    }

    sf::Sprite rocket;
    sf::Sprite enemy;

    std::chrono::milliseconds couldown = std::chrono::milliseconds(0);
    std::chrono::milliseconds time;

    std::chrono::milliseconds enemy_couldown = std::chrono::milliseconds(0);
    bool enemy_fire = false;
    float bulletspeed = 0.02;

    // sf::CircleShape test = create_bullet();

    int changex;

    rocket.setTexture(rocket_texture);
    rocket.setPosition(500, 650);
    rocket.scale(0.5, 0.5);

    std::vector<sf::Sprite> enemy_list;
    std::vector<sf::CircleShape> player_bullets;
    std::vector<sf::CircleShape> enemy_bullets;

    enemy.setTexture(enemy_texture);
    enemy.scale(0.2, 0.2);

    for (int i =0; i<8; i++)
    {
        enemy.setPosition(i * 150, 400);
        enemy_list.push_back(enemy);
    }


    while (window.isOpen())
    {
        sf::Event event;
        while (window.pollEvent(event))
        {
            changex = 0;
            switch (event.type)
            {
            // window closed
            case sf::Event::Closed:
                window.close();
                break;

            // key pressed
            case sf::Event::KeyPressed:
                if (event.key.code == sf::Keyboard::A)
                {
                    if (rocket.getPosition().x >= 0 )
                    {
                        changex = changex - speed;
                    }
                }
                else if (event.key.code == sf::Keyboard::D)
                {
                    if (rocket.getPosition().x <= 1100)
                    {
                        changex = changex + speed;
                    }
                }
                else if (event.key.code == sf::Keyboard::Space)
                {
                    time = std::chrono::duration_cast<std::chrono::milliseconds>(
                        std::chrono::system_clock::now().time_since_epoch());
                    if (couldown < time - std::chrono::milliseconds(100)){
                        couldown = time;
                        player_bullets.push_back(create_bullet(rocket.getPosition(), 47));
                    }                    
                }
                
                break;

            default:
                break;
            }
            rocket.move(changex, 0);
        }

        window.clear();
        window.draw(rocket);
        //swindow.draw(test);
        for (int i=0; i<player_bullets.size();i++)
        {
            player_bullets[i].move(0,-bulletspeed);
            window.draw(player_bullets[i]);
            if (player_bullets[i].getPosition().y < 0)
            {
                player_bullets.erase(player_bullets.begin()+i);
            }
        }

        for (int i = 0; i < enemy_bullets.size(); i++)
        {
            enemy_bullets[i].move(0, bulletspeed);
            window.draw(enemy_bullets[i]);
            if (enemy_bullets[i].getPosition().y > 800)
            {
                enemy_bullets.erase(enemy_bullets.begin() + i);
            }
            if (collide(rocket, enemy_bullets[i]))
            {
                window.close();
            }
        }

        time = std::chrono::duration_cast<std::chrono::milliseconds>(
            std::chrono::system_clock::now().time_since_epoch());
        if (enemy_couldown < time - std::chrono::milliseconds(2000))
        {
            enemy_couldown = time;
            enemy_fire = true;
        }

        // Draw all Enemys
        for (int i = 0; i < enemy_list.size(); i++)
        {
            for (int j = 0; j < player_bullets.size(); j++)
            {
                if (collide(enemy_list[i], player_bullets[j]))
                {
                    enemy_list.erase(enemy_list.begin() + i);
                }
            }

            if (enemy_fire)
            {
                enemy_couldown = time;
                // ADD: Move enemys
                enemy_bullets.push_back(create_bullet(enemy_list[i].getPosition(), 13));
            }
            window.draw(enemy_list[i]);
        }
        enemy_fire = false;
        window.display();
    }

    return 0;
}

如果你知道怎么做, 我想听听

提前致谢

您可以创建一个从 sf::Sprite 派生的 class,它有一个 sf::FloatRect 作为碰撞框,您需要创建一个函数来设置碰撞框。

class Sprite : public sf::Sprite {
    sf::FloatRect hitbox;
}

您可以通过以下方式将 Hitbox 移动到精灵位置:

getTransform().transformRect(hitbox);

我过去曾将此用于 SFML 的命中框。


编辑,这是一个完整的示例程序:

#include <SFML/Graphics.hpp>

/// custom sprite class with hitbox
class HitboxSprite : public sf::Sprite {
public:
    /// sets the hitbox
    void setHitbox(const sf::FloatRect& hitbox) {
        m_hitbox = hitbox;
    }
    /// gets the hitbox (use this instead of getGlobalBounds())
    sf::FloatRect getGlobalHitbox() const {
        return getTransform().transformRect(m_hitbox);
    }
private:
    sf::FloatRect m_hitbox;
};

int main() {
    sf::RenderWindow window(sf::VideoMode(256, 128), "Example");

    // create two sprites, player and enemy
    HitboxSprite player;
    player.setPosition({ 64.f, 64.f });
    HitboxSprite enemy;
    enemy.setPosition({ 128.f, 64.f });
    enemy.setColor(sf::Color::Red);
    // create sprite texture and apply to sprites
    sf::Texture square_texture;
    square_texture.loadFromFile("32x32square.png");
    player.setTexture(square_texture);
    enemy.setTexture(square_texture);
    // set custom hitboxes
    // (this one starts (8, 8) pixels from the top left and has a size of (16, 16)
    // (this means the hitbox will be 1/2 of the square in the middle)
    player.setHitbox({ 8.f, 8.f, 16.f, 16.f });
    enemy.setHitbox({ 8.f, 8.f, 16.f, 16.f });

    sf::Clock clock;
    while (window.isOpen()) {
        // process events
        sf::Event event;
        while (window.pollEvent(event)) {
            if (event.type == sf::Event::Closed)
                window.close();
        }

        const float dt = clock.restart().asSeconds();
        constexpr float player_speed = 128.f;
        // move player with arrow keys
        player.move({
            player_speed * dt * (sf::Keyboard::isKeyPressed(sf::Keyboard::Right) - sf::Keyboard::isKeyPressed(sf::Keyboard::Left)),
            player_speed * dt * (sf::Keyboard::isKeyPressed(sf::Keyboard::Down) - sf::Keyboard::isKeyPressed(sf::Keyboard::Up))
        });

        // check for collision
        const bool colliding = player.getGlobalHitbox().intersects(enemy.getGlobalHitbox());

        // set background color based on collision
        window.clear(colliding ? sf::Color::Green : sf::Color::Blue);
        // draw sprites
        window.draw(enemy);
        window.draw(player);
        // display
        window.display();
    }

    return 0;
}

如果您需要解释任何部分,请告诉我。 这是我制作的半透明 png,中间部分是 hitbox: