/** * paradiso - Paradigmen der Softwareentwicklung * * (c) Copyright 2023 Hartmut Seichter * */ #include #include #include #include #include #include #include #include #include #include #include #include #include "lib/image_loader.hpp" const int frame_rate = 60; struct Background { paradiso::Sprite backgroundLeft; paradiso::Sprite backgroundRight; paradiso::Sprite grassLeft; paradiso::Sprite grassRight; paradiso::Sprite* scrolling[2] = {&backgroundLeft, &backgroundRight}; Background() { auto backgroundImage = image_loader::load(std::string("background-day.png")); backgroundLeft = paradiso::Sprite{ .bitmap = backgroundImage, .pivot = paradiso::Vector2::make(0.0f, 0.0f), .scale = paradiso::Vector2::make(1.01f, 1.0f)}; backgroundRight = paradiso::Sprite{ .bitmap = backgroundImage, .pivot = paradiso::Vector2::make(2.0f, 0.0f), .scale = paradiso::Vector2::make(1.01f, 1.0f)}; auto grassImage = image_loader::load(std::string("base.png")); grassLeft = paradiso::Sprite{ .bitmap = grassImage, .pivot = paradiso::Vector2::make(0.0f, -1.0f), .scale = paradiso::Vector2::make(1.0f, 0.33333f)}; grassRight = paradiso::Sprite{ .bitmap = grassImage, .pivot = paradiso::Vector2::make(2.0f, -1.0f), .scale = paradiso::Vector2::make(1.0f, 0.33333f)}; } void draw(const paradiso::Shader& shader) { for (auto sprite : scrolling) { if (sprite->pivot.x() <= -2.0f) { sprite->pivot.x() += 4.0f; } sprite->pivot.x() -= 0.001f; shader.set_uniform("pivot", sprite->pivot); shader.set_uniform("scale", sprite->scale); shader.set_uniform("rotation", sprite->rotation); renderer.draw(*sprite, shader); } } paradiso::Renderer renderer{}; }; struct Grass { paradiso::Sprite grassLeft; paradiso::Sprite grassRight; paradiso::Sprite* scrolling[2] = {&grassLeft, &grassRight}; Grass() { auto grassImage = image_loader::load(std::string("base.png")); grassLeft = paradiso::Sprite{ .bitmap = grassImage, .pivot = paradiso::Vector2::make(0.0f, -0.9f), .scale = paradiso::Vector2::make(1.0f, 0.33333f)}; grassRight = paradiso::Sprite{ .bitmap = grassImage, .pivot = paradiso::Vector2::make(2.0f, -0.9f), .scale = paradiso::Vector2::make(1.0f, 0.33333f)}; } void draw(const paradiso::Shader& shader) { for (auto sprite : scrolling) { if (sprite->pivot.x() <= -2.0f) { sprite->pivot.x() += 4.0f; } sprite->pivot.x() -= 0.035f; shader.set_uniform("pivot", sprite->pivot); shader.set_uniform("scale", sprite->scale); shader.set_uniform("rotation", sprite->rotation); renderer.draw(*sprite, shader); } } paradiso::Renderer renderer{}; }; struct FlappyBird { paradiso::Renderer renderer1{}; paradiso::Renderer renderer2{}; paradiso::Renderer renderer3{}; std::array birds; unsigned int flap = 0; unsigned int flapSpeed = 15; // How many ticks per flap unsigned int flapCounter = 0; // How many ticks since last flap float velocity = 0.0f; const float max_velocity = 0.05f; const float gravity = -0.004f; const float move_up_velocity = 0.0055f; bool move_up = false; bool paused = true; const float max_pos = 0.95f; const float min_pos = -0.5f; float pos = 0.0f; float rotation = 0.0f; FlappyBird() { float scaleh = 0.07f; float scalew = scaleh * 1.416666666666667f; birds = { paradiso::Sprite{ .bitmap = image_loader::load(std::string("yellowbird-downflap.png")), .pivot = paradiso::Vector2::make(0.0f, 0.0f), .scale = paradiso::Vector2::make(scalew, scaleh)}, paradiso::Sprite{ .bitmap = image_loader::load(std::string("yellowbird-midflap.png")), .pivot = paradiso::Vector2::make(0.0f, 0.0f), .scale = paradiso::Vector2::make(scalew, scaleh)}, paradiso::Sprite{ .bitmap = image_loader::load(std::string("yellowbird-upflap.png")), .pivot = paradiso::Vector2::make(0.0f, 0.0f), .scale = paradiso::Vector2::make(scalew, scaleh)}}; } void draw(const paradiso::Shader& shader) { // Update flap state if (flapCounter < flapSpeed) { flapCounter++; } else { flapCounter = 0; flap = (flap + 1) % birds.size(); } auto bird = birds[flap]; bird.pivot.y() = pos; bird.rotation = rotation; shader.set_uniform("pivot", bird.pivot); shader.set_uniform("scale", bird.scale); shader.set_uniform("rotation", bird.rotation); switch (flap) { case 0: renderer1.draw(bird, shader); break; case 1: renderer2.draw(bird, shader); break; case 2: renderer3.draw(bird, shader); break; } } void update() { // Stop game if (paused) return; // Apply gravity velocity += gravity; if (move_up) velocity += move_up_velocity - gravity; // Cap velocity if (velocity > max_velocity) velocity = max_velocity; if (velocity < -max_velocity) velocity = -max_velocity; // Cap position pos += velocity; if (pos < min_pos) { pos = min_pos; velocity = 0.0f; } if (pos > max_pos) { pos = max_pos; velocity = 0.0f; } // Update rotation rotation = velocity * 15.0f; } // keyboard handler void on_keyboard(const paradiso::Window::KeyboardInputStack& input) { if (input.size()) { paused = false; bool pressed_up = input.top().key == ' ' || input.top().key == 'W'; if (input.top().action == 1 && pressed_up) { move_up = true; } else if (input.top().action == 0 && pressed_up) { move_up = false; } } } }; auto main() -> int { // Ausgabefenster ... sieht aus als wäre es auf dem Stack auto window = paradiso::Window(); auto size = paradiso::Size{.width = 500, .height = 700}; /* window.set_resizecallback([](auto& w) -> void { w.set_size(paradiso::Size{.width = 405, .height = 720}); }); */ window .set_size(size) // ... Grösse .set_position(paradiso::Point{.x = 100, .y = 100}) // ... Position .set_title("PardiSO.FlappyBird") // ... Titel .set_visible(true); // ... und jetzt anzeigen! // der Fenster Kontext auto ctx = paradiso::Context{}; // ein Shader (Schattierungsprogramm) auto shader = paradiso::Shader{}; // wir nutzen einen vorgefertigten shader shader.load_preset(paradiso::Shader::Preset::Sprite); // ein viewport stellt die Sicht der Kamera dar, d.h. bei quadratischen // Pixeln sollte hier auch eine dementsprechende Grösse eingestellt // werden ctx.set_viewport(paradiso::Rectangle{ .position = paradiso::Point{.x = 0, .y = 0}, .size = size}); // nothing beats a classic look ctx.set_clearcolor(paradiso::RGBA::from_rgb(0x00, 0x00, 0x00)); // Load auto background = Background{}; auto grass = Grass{}; auto flappyBird = FlappyBird{}; // timer // das update führt den hier mitgegebnen Ausdruck innerhalb der internen // Updates des Fensters auf. Es wird hier auch explizit ein bool gefordert // damit das update auch zu jederzeit unterbrochen werden kann while (window.update([&](paradiso::Window& w) -> bool { ctx.set_viewport(paradiso::Rectangle{ .position = paradiso::Point{.x = 0, .y = 0}, .size = size}); ctx.clear(); auto t1 = std::chrono::high_resolution_clock::now(); // Keyboard and state change flappyBird.on_keyboard(w.keyboard_input()); flappyBird.update(); // Draw background.draw(shader); grass.draw(shader); flappyBird.draw(shader); // wait for frame rate auto t2 = std::chrono::high_resolution_clock::now(); auto duration = std::chrono::duration_cast(t2 - t1); auto wait = std::chrono::microseconds(1000000 / frame_rate) - duration; std::this_thread::sleep_for(wait); // Quit return !(w.keyboard_input().size() && w.keyboard_input().top().key == 'Q'); })) { }; return 0; }