Наследование от Transformable и Drawable в SFML

Я пытаюсь наследовать от Transformable и Drawable в SFML, чтобы сделать мои объекты... ну, трансформируемыми и доступными для рисования. Я делаю простую игру прорыва, но, возможно, я делаю это неправильно. Вот мой код:

#include <SFML/Graphics.hpp>
#include <SFML/System.hpp>

class Player : public sf::Transformable, public sf::Drawable {
    public:
        Player(int x, int y);
        ~Player() {};

        sf::RectangleShape p_rect;

        void doMovement(const sf::RenderWindow& window);
        sf::FloatRect getGlobalBounds() const;
    private:
        virtual void draw(sf::RenderTarget& target, sf::RenderStates states) const {
            states.transform *= getTransform();
            target.draw(p_rect, states);
        }

};

class Ball : public sf::Transformable, public sf::Drawable {
    public:
        Ball(int r, int x, int y);
        ~Ball() {};

        sf::CircleShape b_circle;

        void doXMovement();
        void doYMovement();
        bool doXCollisions(const Player& player);
        bool doYCollisions(const Player& player);
        sf::FloatRect getGlobalBounds() const;
    private:
        virtual void draw(sf::RenderTarget& target, sf::RenderStates states) const {
            states.transform *= getTransform();
            target.draw(b_circle, states);
        }

        bool right;
        bool up;
};

Player::Player(int x, int y) {
    p_rect = sf::RectangleShape(sf::Vector2f(x, y));
}

void Player::doMovement(const sf::RenderWindow& window) {
    setPosition(sf::Mouse::getPosition(window).x, 500);
    if (getPosition().x < 0)
        setPosition(0, 500);
    else if (getPosition().x > 720)
        setPosition(720, 500);
}

sf::FloatRect Player::getGlobalBounds() const {
    return getTransform().transformRect(p_rect.getGlobalBounds());
}

Ball::Ball(int r, int x, int y) {
    b_circle = sf::CircleShape(r);
    b_circle.setPosition(x, y);
    right = true;
    up = false;
}

void Ball::doXMovement() {
    if (right)
        move(1, 0);
    else
        move(-1, 0);
}

void Ball::doYMovement() {
    if (up)
        move(0, -1);
    else
        move(0, 1);
}

bool Ball::doXCollisions(const Player& player) {
    bool coll;
    if (getGlobalBounds().intersects(player.getGlobalBounds())) {
        right = !right;
        coll = true;
    } else
        coll = false;

    if (getPosition().x >= 800 - b_circle.getRadius())
        right = false;
    else if (getPosition().x <= 0)
        right = true;
    return coll;
}

bool Ball::doYCollisions(const Player& player) {
    bool coll;
    if (getGlobalBounds().intersects(player.getGlobalBounds())) {
        up = !up;
        coll = true;
    } else
        coll = false;
    if (getPosition().x <= 0)
        up = false;
    return coll;
}

sf::FloatRect Ball::getGlobalBounds() const {
    return getTransform().transformRect(b_circle.getGlobalBounds());
}

int main() {
    sf::RenderWindow window(sf::VideoMode(800, 600), "Breakout");
    window.setMouseCursorVisible(false);
    Player player(80, 10);
    Ball ball(3, 100, 100);
    sf::Clock clock;
    while (window.isOpen()) {
        sf::Event event;
        while (window.pollEvent(event)) {
            if (event.type == sf::Event::Closed)
                window.close();
        }
        player.doMovement(window);
        if (clock.getElapsedTime().asMilliseconds() >= 3) {
            clock.restart();
            if (!ball.doYCollisions(player))
                ball.doXCollisions(player);
            ball.doYMovement();
            ball.doXMovement();
        }
        window.clear(sf::Color::Black);
        window.draw(player);
        window.draw(ball);
        window.display();
    }
    return 0;
}

Теперь перемещение и рисование работают (почти), как и ожидалось, однако коллизии немного шаткие. Первые мои проблемы со столкновениями:

  1. Нужно ли мне реализовать функцию getGlobalBounds так, как я это сделал? Или есть лучший способ сделать это с вещами, включенными в Transformable и Drawable?
  2. Должен ли я выполнять преобразования фигур напрямую или мне следует передать преобразования функции рисования, как я это делаю сейчас?

С рисунком также происходит что-то странное, что, вероятно, является быстрым решением. Прямо сейчас метод getPosition возвращает неверные значения для моего объекта мяча. Область, которую он возвращает, кажется, смещена вниз и немного вправо. Любая причина, которая может быть?

Спасибо за любую помощь, которую вы можете оказать!

РЕДАКТИРОВАТЬ: также приветствуются любые общие советы по С++, я все еще новичок.


person mrobinson7627    schedule 28.07.2013    source источник


Ответы (1)


На вашем месте я бы определил новый класс с именем TransformableAndDrawable следующим образом:

class TransformableAndDrawable : public sf::Transformable, public sf::Drawable {
    // Your code here
}

В этом классе вы должны определить все члены, которые обычно необходимы вашим трансформируемым и рисуемым классам. Кроме того, в этом классе вы должны определить все методы, которые обычно могут быть реализованы в ваших трансформируемых и рисуемых классах. Затем ваши классы должны быть унаследованы от TransformableAndDrawable, например:

class Player : TransformableAndDrawable {
    // Your code here
}

Теперь ответ на первый вопрос: я бы реализовал данный метод в классе TransformableAndDrawable, если бы это был общий метод, поэтому все классы, унаследованные от TransformableAndDrawable, будут иметь этот метод.

Вместо того, чтобы давать разные имена, такие как p_rect и p_circle, назовите этих участников одинаковыми именами, например p_shape, чтобы у вас не было проблем с именами. Кроме того, я считаю, что вы можете объявить свой p_shape классом-предком или интерфейсом (я не знаю, какие классы определены в библиотеке, с которой вы работаете) и только при необходимости указать характер формы (будь то круг или прямоугольник или что-то еще).

Что касается второго вопроса: мне нравится, как вы все реализовали, но вы допустили две ошибки:

  • он не масштабируемый: нам нужно общее решение, класс, который можно использовать для любой формы, с которой вы работаете сейчас и в будущем, не так ли?
  • это недостаточно общее: когда я хочу знать глобальные границы формы, меня не интересует природа формы, я бы предпочел, чтобы ваш код обрабатывал природу формы без моего ведома.

Короче говоря, вы должны сделать следующее:

  1. Создайте класс-оболочку, который будет унаследован от Transformable и Drawable.

  2. В своем классе-оболочке будьте независимы от природы формы, будьте как можно более общими, надеюсь, есть какой-то класс или интерфейс, который является предком как для RectangleShape, так и для CircleShape.

  3. Наследуйте все свои рисуемые и трансформируемые классы из вашего класса-оболочки, поэтому у вас будет общая функциональность среди ваших классов.

  4. Если что-то в вашем классе-оболочке не подходит для класса, который был унаследован от него, перезапишите метод в этом классе.

РЕДАКТИРОВАТЬ:

Я более подробно изучил библиотеку, которую вы используете, и обнаружил, что существует класс Shape, который является предком как CircleShape, так и RectangleShape. Таким образом, вместо этих классов используйте Shape, и ваш код будет более общим и пригодным для повторного использования.

person Lajos Arpad    schedule 28.07.2013
comment
Хорошо, круто, я думал о создании суперкласса для этих классов, так как я действительно ненавижу писать один и тот же код дважды. Надеюсь, делая это, я смогу исправить свою странную проблему с рисунком. - person mrobinson7627; 28.07.2013
comment
Стоит ли использовать указатель Shape при использовании функции getRadius? Это сработает, но я чувствую, что это может быть плохой практикой. Лично я так не думаю, потому что это класс, который должен иметь круглую форму. РЕДАКТИРОВАТЬ: Вот так: if (getPosition().x >= 800 - dynamic_cast<sf::CircleShape*>(shape)->getRadius()) - person mrobinson7627; 29.07.2013
comment
Вы можете преобразовать его, но я считаю, что вы должны реализовать функциональность для этого и в суперклассе, и там вы можете поддерживать любой класс, используя шаблоны. - person Lajos Arpad; 29.07.2013
comment
Как мне это сделать? Должна ли быть какая-то функция? - person mrobinson7627; 31.07.2013