#include #include #include // std::sin unsigned const screen_width { 640 }; unsigned const screen_height { 640 }; 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; }; State* state = nullptr; sf::RenderWindow window { sf::VideoMode { screen_width, screen_height }, "Basic SFML Example" }; class Game_State : public State { public: Game_State() { circle.setOrigin(radius, radius); circle.setFillColor(sf::Color::Green); sf::Vector2u const window_size { window.getSize() }; circle.setPosition(window_size.x / 2, window_size.y / 2); if (!texture.loadFromFile("fighter.png")) { 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 }; }; // // För att skapa en huvudmeny behöver vi endast skapa en ny subklass // till State som representerar huvudmenyn. // // I detta fall sköter vi logik för att texten ska pulsera i update(), // samtidigt som vi sköter bytet till Game_State när användare trycker // Enter i handle(). // class Menu_State : public State { public: Menu_State() { // För att skriva ut en text i SFML så måste vi ladda in ett // typsnitt. Detta hanteras på liknande sätt som texturer. // OBS: Font måste existera sålänge vårt text objekt ska // existera. if (!font.loadFromFile("font.ttf")) { throw std::runtime_error { "Kan inte öppna: font.ttf" }; } // Initiera text objektet som representerar vår text som ska // skrivas ut. text.setFont(font); text.setString("Press to start!"); sf::Vector2u const window_size { window.getSize() }; // För att placera texten i mitten av skärmen måste vi veta // bredden och höjden av texten. För att få ut detta kan vi // använda vår käre vän .getGlobalbounds()! sf::FloatRect bounds { text.getGlobalBounds() }; text.setOrigin(bounds.width / 2, bounds.height / 2); text.setPosition(window_size.x / 2, window_size.y / 2); } void handle(sf::Event event) override { if (event.type == sf::Event::KeyPressed) { if (event.key.code == sf::Keyboard::Key::Enter) { // Byt tillstånd till Game_State. // // OBS: Detta leder till minnesläckor eftersom att vi // inte tar bort det gamla objektet. Det finns ett // problem med att skriva: 'delete state;' här // eftersom att vi då tar bort objektet vi för // tillfället befinner oss i, vilket lätt kan leda // till problem. // // Fundera själva på hur detta kan lösas! (Tips: Om // man får bort state som en global variabel så är // detta lättare att lösa). // state = new Game_State; } } } void update(sf::Time delta) override { // Öka tiden som har gått. elapsed_time += delta.asSeconds(); // En full cykel av pulsering för texten ska ta 2 sekunder. float const period { 2.0f }; // Omskalningsfaktorn är i intervallet [0.9, 1.1], vi använder // sin för att få det periodiska beteendet. OBS: std::sin // använder radianer medan SFML använder grader för vinklar. double const scale { 1.0 + 0.1*std::sin( (2 * M_PI) * elapsed_time / period) }; text.setScale(scale, scale); } void render(sf::RenderWindow& window) override { window.draw(text); } private: sf::Font font; sf::Text text; float elapsed_time { 0.0f }; }; int main() { state = new Menu_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; }