#include #include unsigned const screen_width { 640 }; unsigned const screen_height { 640 }; // // I denna fil presenterar vi hur man kan implementera sfml_v7.cc som // en tillståndsmaskin m.h.a. objektorientering. // // (bas)klassen State har 3 virtuella funktioner, handle(), update() // och render() som alla anropas (virtuellt) i main() funktionen. // class State { public: virtual ~State() = default; virtual void handle(sf::Event event) = 0; virtual void update(sf::Time delta) = 0; virtual void render(sf::RenderWindow& window) = 0; }; // // Vi lägge state variabeln och fönstret som globala variabeler för // att göra dem lättillgängliga i våra tillstånd. OBS!! Detta är // *inte* bra design. Använd inte globala variabler i era egna // projekt. // State* state = nullptr; sf::RenderWindow window { sf::VideoMode { screen_width, screen_height }, "Basic SFML Example" }; // // Låt oss föra över det vi har gjort hittils till ett eget tillstånd // genom att skapa en klass för tillståndet (Game_State) som ärver // från State och överlagrar alla de virtuella funktionerna. // // Jämför detta med sfml_v7.cc. // // En fördel med att göra på detta sätt är att vi kan lagra alla // variabler som behövs som datamedlemmar i klassen vilket gör att // handle(), update() och render() har tillgång till dem.window // // All initiering vi tidigare gjorde precis innan main-loopen gör vi // istället i konstruktorn. // // Notera: Det är viktigt (för att koden ska vara lättläst) att man // faktiskt implementera de olika stegen i respektive funktion. Undvik // att lägga för mycket logik i handle() etc. // class Game_State : public State { public: // Jämför implementationen av sfml_v7.cc med dessa funktioner: Game_State() { sf::Vector2u window_size { window.getSize() }; circle.setOrigin(radius, radius); circle.setFillColor(sf::Color::Green); circle.setPosition(window_size.x / 2, window_size.y / 2); if (!texture.loadFromFile("fighter.png")) { // Vi kan inte längre avbryta programmet med return så vi // kastar ett undantag istället. throw std::runtime_error { "Kan inte öppna: fighter.png" }; } sprite.setTexture(texture); sf::Vector2u texture_size { texture.getSize() }; sprite.setOrigin(texture_size.x / 2, texture_size.y / 2); sprite.setPosition(window_size.x / 2, 3 * window_size.y / 4); } void handle(sf::Event event) override { if (event.type == sf::Event::MouseButtonPressed) { sf::Event::MouseButtonEvent mouse { event.mouseButton }; if (mouse.button == sf::Mouse::Button::Left) { circle.setPosition(mouse.x, mouse.y); } } } void update(sf::Time delta) override { sf::Vector2f old_position { sprite.getPosition() }; float current_speed { delta.asSeconds() * speed }; if (sf::Keyboard::isKeyPressed(sf::Keyboard::Key::W)) { move_sprite({0, -current_speed}, 0); } else if (sf::Keyboard::isKeyPressed(sf::Keyboard::Key::A)) { move_sprite({-current_speed, 0}, 270); } else if (sf::Keyboard::isKeyPressed(sf::Keyboard::Key::S)) { move_sprite({0, current_speed}, 180); } else if (sf::Keyboard::isKeyPressed(sf::Keyboard::Key::D)) { move_sprite({current_speed, 0}, 90); } sf::FloatRect circle_bounds { circle.getGlobalBounds() }; sf::FloatRect sprite_bounds { sprite.getGlobalBounds() }; if (circle_bounds.intersects(sprite_bounds)) { sprite.setPosition(old_position); } } void render(sf::RenderWindow& window) override { window.draw(sprite); window.draw(circle); } private: void move_sprite(sf::Vector2f direction, float angle) { sprite.setRotation(angle); sprite.move(direction); } float const radius { 16.0f }; sf::CircleShape circle { radius }; sf::Texture texture; sf::Sprite sprite; float const speed { 128.0f }; }; int main() { state = new Game_State(); sf::Clock clock; while (window.isOpen()) { sf::Event event; while (window.pollEvent(event)) { if (event.type == sf::Event::Closed) { window.close(); } state->handle(event); } state->update(clock.restart()); window.clear(); state->render(window); window.display(); } delete state; }