Some architectural changes

This commit is contained in:
brxxh 2025-05-31 00:02:16 +02:00
parent b412c992ee
commit ccd0697ab3

View file

@ -17,9 +17,15 @@
#include <paradiso/window.hpp> #include <paradiso/window.hpp>
#include <chrono> #include <chrono>
#include <print>
#include <thread> #include <thread>
#include <tuple>
#include <unordered_map>
#include <iostream> namespace TappyPlane {
using SpriteName = std::tuple<std::string_view, std::string_view>;
using SpriteMap = std::unordered_map<std::string_view, paradiso::Bitmap>;
static const unsigned int FRAME_RATE = 60; static const unsigned int FRAME_RATE = 60;
@ -56,23 +62,24 @@ struct Background {
std::array<paradiso::Sprite, 2> sprites; std::array<paradiso::Sprite, 2> sprites;
paradiso::Renderer renderer{}; paradiso::Renderer renderer{};
paradiso::Bitmap image = void init(SpriteMap& sprite_map) {
paradiso::BitmapIO::get().load("PNG/background.png");
void init() {
// For resetting.
scrolling_speed = INIT_SCROLLING_SPEED;
sprites = {paradiso::Sprite{ sprites = {paradiso::Sprite{
.bitmap = image, .bitmap = sprite_map["background"],
.pivot = {paradiso::Vector2<float>::make(0.0f, 0.0f)}, .pivot = {paradiso::Vector2<float>::make(0.0f, 0.0f)},
.scale = {paradiso::Vector2<float>::make(1.0f, 1.0f)}}, .scale = {paradiso::Vector2<float>::make(1.0f, 1.0f)}},
paradiso::Sprite{ paradiso::Sprite{
.bitmap = image, .bitmap = sprite_map["background"],
.pivot = {paradiso::Vector2<float>::make(2.0f, 0.0f)}, .pivot = {paradiso::Vector2<float>::make(2.0f, 0.0f)},
.scale = {paradiso::Vector2<float>::make(1.0f, 1.0f)}}}; .scale = {paradiso::Vector2<float>::make(1.0f, 1.0f)}}};
} }
void reset() {
scrolling_speed = INIT_SCROLLING_SPEED;
sprites[0].pivot.x() = 0.0f;
sprites[1].pivot.x() = 2.0f;
}
void update() { void update() {
for (auto& sprite : sprites) { for (auto& sprite : sprites) {
sprite.pivot.x() -= scrolling_speed; sprite.pivot.x() -= scrolling_speed;
@ -100,17 +107,9 @@ struct Ground {
std::array<paradiso::Sprite, 2> sprites; std::array<paradiso::Sprite, 2> sprites;
paradiso::Renderer renderer{}; paradiso::Renderer renderer{};
// Maybe put this into 'init()' because we might use the other textures
// like snow, ice, ... and then we can parameterize this.
paradiso::Bitmap image =
paradiso::BitmapIO::get().load("PNG/groundGrass.png");
AABB aabb{}; AABB aabb{};
void init() { void init(SpriteMap& sprite_map) {
// For resetting.
scrolling_speed = INIT_SCROLLING_SPEED;
// The image's height is 71 px. // The image's height is 71 px.
float scale_y = 71.0f / WINDOW_HEIGHT; float scale_y = 71.0f / WINDOW_HEIGHT;
@ -118,11 +117,11 @@ struct Ground {
sprites = { sprites = {
paradiso::Sprite{ paradiso::Sprite{
.bitmap = image, .bitmap = sprite_map["ground"],
.pivot = {paradiso::Vector2<float>::make(0.0f, pivot_y)}, .pivot = {paradiso::Vector2<float>::make(0.0f, pivot_y)},
.scale = {paradiso::Vector2<float>::make(1.0f, scale_y)}}, .scale = {paradiso::Vector2<float>::make(1.0f, scale_y)}},
paradiso::Sprite{ paradiso::Sprite{
.bitmap = image, .bitmap = sprite_map["ground"],
.pivot = {paradiso::Vector2<float>::make(2.0f, pivot_y)}, .pivot = {paradiso::Vector2<float>::make(2.0f, pivot_y)},
.scale = {paradiso::Vector2<float>::make(1.0f, scale_y)}}}; .scale = {paradiso::Vector2<float>::make(1.0f, scale_y)}}};
@ -132,6 +131,13 @@ struct Ground {
.size = {paradiso::Vector2<float>::make(2.0f, scale_y)}}; .size = {paradiso::Vector2<float>::make(2.0f, scale_y)}};
} }
void reset() {
scrolling_speed = INIT_SCROLLING_SPEED;
sprites[0].pivot.x() = 0.0f;
sprites[1].pivot.x() = 2.0f;
}
void update() { void update() {
for (auto& sprite : sprites) { for (auto& sprite : sprites) {
sprite.pivot.x() -= scrolling_speed; sprite.pivot.x() -= scrolling_speed;
@ -153,7 +159,14 @@ struct Ground {
struct Rocks { struct Rocks {
static constexpr float INIT_SCROLLING_SPEED = 0.006f; static constexpr float INIT_SCROLLING_SPEED = 0.006f;
static constexpr float GAP_SIZE = 1.6f; static constexpr float GAP_SIZE = 1.5f;
// The image's size is 108x239 px.
static constexpr float INIT_SCALE_X = 108.0f / WINDOW_WIDTH;
static constexpr float INIT_SCALE_Y = 239.0f / WINDOW_HEIGHT;
static constexpr paradiso::Vector2<float> INIT_SCALE{
paradiso::Vector2<float>::make(INIT_SCALE_X, INIT_SCALE_Y)};
float scrolling_speed = INIT_SCROLLING_SPEED; float scrolling_speed = INIT_SCROLLING_SPEED;
@ -163,45 +176,20 @@ struct Rocks {
paradiso::Renderer top_renderer{}; paradiso::Renderer top_renderer{};
paradiso::Renderer bottom_renderer{}; paradiso::Renderer bottom_renderer{};
// Maybe put this into 'init()' because we might use the other textures
// like snow, ice, ... and then we can parameterize this.
paradiso::Bitmap top_image =
paradiso::BitmapIO::get().load("PNG/rockGrassDown.png");
paradiso::Bitmap bottom_image =
paradiso::BitmapIO::get().load("PNG/rockGrass.png");
AABB top_aabb; AABB top_aabb;
AABB bottom_aabb; AABB bottom_aabb;
void init(float x, bool reset_scrolling_speed = false) { float init_pivot_x = 0.0f;
if (reset_scrolling_speed) {
scrolling_speed = INIT_SCROLLING_SPEED;
}
// The image's size is 108x239 px. auto get_random_top_position_y() -> float {
float scale_x = 108.0f / WINDOW_WIDTH; return 1.0f - INIT_SCALE_Y + (std::rand() % 50) / 100.0f + 0.01f;
float scale_y = 239.0f / WINDOW_HEIGHT; }
auto scale = paradiso::Vector2<float>{
paradiso::Vector2<float>::make(scale_x, scale_y)};
float top_position_y =
1.0f - scale_y + (std::rand() % 60) / 100.0f + 0.04f;
float bottom_position_y = top_position_y - GAP_SIZE;
top_sprite = paradiso::Sprite{
.bitmap = top_image,
.pivot = {paradiso::Vector2<float>::make(x, top_position_y)},
.scale = scale};
bottom_sprite = paradiso::Sprite{
.bitmap = bottom_image,
.pivot = {paradiso::Vector2<float>::make(x, bottom_position_y)},
.scale = scale};
void init_aabbs() {
// We'll make the width a quarter the width of the texture. // We'll make the width a quarter the width of the texture.
// (If you want 1:1 size, you need to double the scale of the sprite.) // (If you want 1:1 size, you need to double the scale of the sprite.)
auto size = paradiso::Vector2<float>{ auto size = paradiso::Vector2<float>{
paradiso::Vector2<float>::make(scale_x / 2, scale_y * 2)}; paradiso::Vector2<float>::make(INIT_SCALE_X / 2, INIT_SCALE_Y * 2)};
auto top_position = auto top_position =
paradiso::Vector2<float>{paradiso::Vector2<float>::make( paradiso::Vector2<float>{paradiso::Vector2<float>::make(
@ -217,6 +205,52 @@ struct Rocks {
bottom_aabb = AABB{bottom_position, size}; bottom_aabb = AABB{bottom_position, size};
} }
void init(SpriteMap& sprite_map, float pivot_x) {
init_pivot_x = pivot_x;
float top_position_y = get_random_top_position_y();
float bottom_position_y = top_position_y - GAP_SIZE;
top_sprite = paradiso::Sprite{.bitmap = sprite_map["rocks.top"],
.pivot = {paradiso::Vector2<float>::make(
init_pivot_x, top_position_y)},
.scale = INIT_SCALE};
bottom_sprite =
paradiso::Sprite{.bitmap = sprite_map["rocks.bottom"],
.pivot = {paradiso::Vector2<float>::make(
init_pivot_x, bottom_position_y)},
.scale = INIT_SCALE};
init_aabbs();
}
void reset() {
scrolling_speed = INIT_SCROLLING_SPEED;
float top_position_y = get_random_top_position_y();
float bottom_position_y = top_position_y - GAP_SIZE;
top_sprite.pivot = paradiso::Vector2<float>{
paradiso::Vector2<float>::make(init_pivot_x, top_position_y)};
bottom_sprite.pivot = paradiso::Vector2<float>{
paradiso::Vector2<float>::make(init_pivot_x, bottom_position_y)};
init_aabbs();
}
void jump_back() {
float top_position_y = get_random_top_position_y();
float bottom_position_y = top_position_y - GAP_SIZE;
top_sprite.pivot = paradiso::Vector2<float>{
paradiso::Vector2<float>::make(1.2f, top_position_y)};
bottom_sprite.pivot = paradiso::Vector2<float>{
paradiso::Vector2<float>::make(1.2f, bottom_position_y)};
init_aabbs();
}
void update() { void update() {
top_sprite.pivot.x() -= scrolling_speed; top_sprite.pivot.x() -= scrolling_speed;
bottom_sprite.pivot.x() -= scrolling_speed; bottom_sprite.pivot.x() -= scrolling_speed;
@ -231,14 +265,14 @@ struct Rocks {
// \ // \
// *------ the x we need. // *------ the x we need.
// @Efficiency: We could just scroll it with the sprite. // @Efficiency: We could just scroll the AABBs with the sprites.
top_aabb.position.x() = top_sprite.pivot.x() - top_aabb.size.x() / 2; top_aabb.position.x() = top_sprite.pivot.x() - top_aabb.size.x() / 2;
bottom_aabb.position.x() = bottom_aabb.position.x() =
bottom_sprite.pivot.x() - bottom_aabb.size.x() / 2; bottom_sprite.pivot.x() - bottom_aabb.size.x() / 2;
if (top_sprite.pivot.x() <= -1.2f) { if (top_sprite.pivot.x() <= -1.2f) {
// Reset. // Reset.
init(1.2f); jump_back();
return; return;
} }
} }
@ -267,6 +301,16 @@ struct Plane {
static const unsigned int ANIM_INTERVAL = 10; static const unsigned int ANIM_INTERVAL = 10;
// The image's size is 88x73 px.
static constexpr float INIT_SCALE_X = 88.0f / WINDOW_WIDTH;
static constexpr float INIT_SCALE_Y = 73.0f / WINDOW_HEIGHT;
static constexpr paradiso::Vector2<float> INIT_SCALE{
paradiso::Vector2<float>::make(INIT_SCALE_X, INIT_SCALE_Y)};
static constexpr paradiso::Vector2<float> INIT_PIVOT{
paradiso::Vector2<float>::make(0.0f, INIT_POSITION_Y)};
// Animation sprites // Animation sprites
std::array<paradiso::Sprite, 3> sprites; std::array<paradiso::Sprite, 3> sprites;
std::array<paradiso::Renderer, 3> renderers; std::array<paradiso::Renderer, 3> renderers;
@ -285,46 +329,45 @@ struct Plane {
AABB aabb; AABB aabb;
void init() { void init_aabb() {
// The image's size is 88x73 px. // We'll make the size half the size of the texture.
float scale_x = 88.0f / WINDOW_WIDTH; // (If you want 1:1 size, you need to double the scale of the sprite.)
float scale_y = 73.0f / WINDOW_HEIGHT; // We could use INIT_SCALE here, but we'll keep this for clarity.
auto size = paradiso::Vector2<float>{
paradiso::Vector2<float>::make(INIT_SCALE_X, INIT_SCALE_Y)};
auto pivot = paradiso::Vector2<float>{ // Center it.
paradiso::Vector2<float>::make(0.0f, position_y)}; auto position = paradiso::Vector2<float>{paradiso::Vector2<float>::make(
auto scale = paradiso::Vector2<float>{ INIT_PIVOT.x() - size.x() / 2, INIT_PIVOT.y() - size.y() / 2)};
paradiso::Vector2<float>::make(scale_x, scale_y)};
sprites = { aabb = AABB{position, size};
paradiso::Sprite{.bitmap = paradiso::BitmapIO::get().load( }
"PNG/Planes/planeRed1.png"),
.pivot = pivot, void init(SpriteMap& sprite_map) {
.scale = scale}, sprites = {paradiso::Sprite{.bitmap = sprite_map["plane.1"],
paradiso::Sprite{.bitmap = paradiso::BitmapIO::get().load( .pivot = INIT_PIVOT,
"PNG/Planes/planeRed2.png"), .scale = INIT_SCALE},
.pivot = pivot, paradiso::Sprite{.bitmap = sprite_map["plane.2"],
.scale = scale}, .pivot = INIT_PIVOT,
paradiso::Sprite{.bitmap = paradiso::BitmapIO::get().load( .scale = INIT_SCALE},
"PNG/Planes/planeRed3.png"), paradiso::Sprite{.bitmap = sprite_map["plane.3"],
.pivot = pivot, .pivot = INIT_PIVOT,
.scale = scale}, .scale = INIT_SCALE}};
};
init_aabb();
}
void reset() {
for (auto& sprite : sprites) {
sprite.pivot = INIT_PIVOT;
sprite.scale = INIT_SCALE;
}
// For resetting.
position_y = INIT_POSITION_Y; position_y = INIT_POSITION_Y;
velocity_y = INIT_VELOCITY_Y; velocity_y = INIT_VELOCITY_Y;
rotation = INIT_ROTATION; rotation = INIT_ROTATION;
// We'll make the size half the size of the texture. init_aabb();
// (If you want 1:1 size, you need to double the scale of the sprite.)
auto size = paradiso::Vector2<float>{
paradiso::Vector2<float>::make(scale_x, scale_y)};
// Center it.
auto position = paradiso::Vector2<float>{paradiso::Vector2<float>::make(
pivot.x() - size.x() / 2, pivot.y() - size.y() / 2)};
aabb = AABB{position, size};
} }
void update(const paradiso::Window::KeyboardInputStack& input) { void update(const paradiso::Window::KeyboardInputStack& input) {
@ -363,7 +406,7 @@ struct Plane {
// \ // \
// *--- the y we need. // *--- the y we need.
// @Efficiency: We could just add the velocity. // @Efficiency: We could just add the velocity to the AABB's y position.
aabb.position.y() = position_y - aabb.size.y() / 2; aabb.position.y() = position_y - aabb.size.y() / 2;
rotation = velocity_y * 500.0f; rotation = velocity_y * 500.0f;
@ -381,7 +424,7 @@ struct Plane {
} }
}; };
struct StartText { struct StartUI {
static const unsigned int TAP_ANIM_INTERVAL = 40; static const unsigned int TAP_ANIM_INTERVAL = 40;
// textGetReady.png, tapLeft.png, tapRight.png // textGetReady.png, tapLeft.png, tapRight.png
@ -395,43 +438,37 @@ struct StartText {
unsigned int tap_anim_counter = 0; unsigned int tap_anim_counter = 0;
unsigned int current_tap_sprite = 0; unsigned int current_tap_sprite = 0;
void init() { void init(SpriteMap& sprite_map) {
base_sprites = { base_sprites = {
// textGetReady.png
paradiso::Sprite{ paradiso::Sprite{
.bitmap = .bitmap = sprite_map["start_ui.text"],
paradiso::BitmapIO::get().load("PNG/UI/textGetReady.png"),
.pivot = {paradiso::Vector2<float>::make(0.0f, 0.5f)}, .pivot = {paradiso::Vector2<float>::make(0.0f, 0.5f)},
// The image's size is 400x73 px. // The image's size is 400x73 px.
.scale = {paradiso::Vector2<float>::make( .scale = {paradiso::Vector2<float>::make(
400.0f / WINDOW_WIDTH, 73.0f / WINDOW_HEIGHT)}}, 400.0f / WINDOW_WIDTH, 73.0f / WINDOW_HEIGHT)}},
// tapLeft.png
paradiso::Sprite{ paradiso::Sprite{
.bitmap = paradiso::BitmapIO::get().load("PNG/UI/tapLeft.png"), .bitmap = sprite_map["tap_sign.left"],
.pivot = {paradiso::Vector2<float>::make(0.5f, 0.0f)}, .pivot = {paradiso::Vector2<float>::make(-0.5f, 0.0f)},
// The image's size is 85x42 px. // The image's size is 85x42 px.
.scale = {paradiso::Vector2<float>::make( .scale = {paradiso::Vector2<float>::make(
85.0f / WINDOW_WIDTH, 42.0f / WINDOW_HEIGHT)}}, 85.0f / WINDOW_WIDTH, 42.0f / WINDOW_HEIGHT)}},
// tapRight.png
paradiso::Sprite{ paradiso::Sprite{
.bitmap = paradiso::BitmapIO::get().load("PNG/UI/tapRight.png"), .bitmap = sprite_map["tap_sign.right"],
.pivot = {paradiso::Vector2<float>::make(-0.5f, 0.0f)}, .pivot = {paradiso::Vector2<float>::make(0.5f, 0.0f)},
// The image's size is 85x42 px. // The image's size is 85x42 px.
.scale = {paradiso::Vector2<float>::make( .scale = {paradiso::Vector2<float>::make(
85.0f / WINDOW_WIDTH, 42.0f / WINDOW_HEIGHT)}}, 85.0f / WINDOW_WIDTH, 42.0f / WINDOW_HEIGHT)}},
}; };
tap_sprites = { tap_sprites = {
// tap.png
paradiso::Sprite{ paradiso::Sprite{
.bitmap = paradiso::BitmapIO::get().load("PNG/UI/tap.png"), .bitmap = sprite_map["tap.normal"],
.pivot = {paradiso::Vector2<float>::make(0.0f, -0.5f)}, .pivot = {paradiso::Vector2<float>::make(0.0f, -0.5f)},
// The image's size is 59x59 px. // The image's size is 59x59 px.
.scale = {paradiso::Vector2<float>::make( .scale = {paradiso::Vector2<float>::make(
59.0f / WINDOW_WIDTH, 59.0f / WINDOW_HEIGHT)}}, 59.0f / WINDOW_WIDTH, 59.0f / WINDOW_HEIGHT)}},
// tapTick.png
paradiso::Sprite{ paradiso::Sprite{
.bitmap = paradiso::BitmapIO::get().load("PNG/UI/tapTick.png"), .bitmap = sprite_map["tap.tick"],
.pivot = {paradiso::Vector2<float>::make(0.0f, -0.5f)}, .pivot = {paradiso::Vector2<float>::make(0.0f, -0.5f)},
// The image's size is 59x59 px. // The image's size is 59x59 px.
.scale = {paradiso::Vector2<float>::make( .scale = {paradiso::Vector2<float>::make(
@ -469,19 +506,16 @@ struct StartText {
} }
}; };
struct GameOverText { struct GameOverUI {
paradiso::Sprite sprite; paradiso::Sprite sprite;
paradiso::Renderer renderer{}; paradiso::Renderer renderer{};
paradiso::Bitmap image = void init(SpriteMap& sprite_map) {
paradiso::BitmapIO::get().load("PNG/UI/textGameOver.png");
void init() {
// The image's size is 412x78 px. // The image's size is 412x78 px.
float scale_x = 412.0f / WINDOW_WIDTH; float scale_x = 412.0f / WINDOW_WIDTH;
float scale_y = 78.0f / WINDOW_HEIGHT; float scale_y = 78.0f / WINDOW_HEIGHT;
sprite = {.bitmap = image, sprite = {.bitmap = sprite_map["game_over_ui.text"],
.pivot = {paradiso::Vector2<float>::make(0.0f, 0.0f)}, .pivot = {paradiso::Vector2<float>::make(0.0f, 0.0f)},
.scale = {paradiso::Vector2<float>::make(scale_x, scale_y)}}; .scale = {paradiso::Vector2<float>::make(scale_x, scale_y)}};
} }
@ -500,155 +534,200 @@ struct GameOverText {
enum class GameState { Start, Playing, GameOver }; enum class GameState { Start, Playing, GameOver };
auto main() -> int { struct App {
std::srand(std::time(nullptr)); SpriteMap sprites;
auto canvas_size = paradiso::Size{.width = WINDOW_WIDTH * WINDOW_SCALE, static auto create() -> App {
.height = WINDOW_HEIGHT * WINDOW_SCALE}; auto app = App{};
// Unser Fenster, auf das wir rendern. paradiso::BitmapIO::get().set_path(
auto window = paradiso::Window(); paradiso::get_executable_path().parent_path().string() + "/assets");
window
.set_size(canvas_size) // ... Größe
.set_position(paradiso::Point{.x = 200, .y = 200}) // ... Position
.set_title("ParadiSO.TappyPlane") // ... Titel
.set_visible(true); // ... und jetzt anzeigen!
// Der Fenster Context. auto assets = std::array{
auto context = paradiso::Context{}; SpriteName{"background", "PNG/background.png"},
SpriteName{"ground", "PNG/groundGrass.png"},
SpriteName{"rocks.top", "PNG/rockGrassDown.png"},
SpriteName{"rocks.bottom", "PNG/rockGrass.png"},
SpriteName{"plane.1", "PNG/Planes/planeRed1.png"},
SpriteName{"plane.2", "PNG/Planes/planeRed2.png"},
SpriteName{"plane.3", "PNG/Planes/planeRed3.png"},
SpriteName{"tap_sign.left", "PNG/UI/tapRight.png"},
SpriteName{"tap_sign.right", "PNG/UI/tapLeft.png"},
SpriteName{"tap.normal", "PNG/UI/tap.png"},
SpriteName{"tap.tick", "PNG/UI/tapTick.png"},
SpriteName{"start_ui.text", "PNG/UI/textGetReady.png"},
SpriteName{"game_over_ui.text", "PNG/UI/textGameOver.png"},
};
// Ein Shader (Schattierungsprogramm). for (const auto& [name, filename] : assets) {
auto shader = paradiso::Shader{}; std::print("{} : {} -> ", name, filename);
// Wir nutzen einen vorgefertigten Shader, der speziell für Sprites ist. auto bitmap = paradiso::BitmapIO::get().load(filename);
shader.load_preset(paradiso::Shader::Preset::Sprite); app.sprites[name] = bitmap;
// Ein Viewport stellt die Sicht der Kamera dar, d.h. bei quadratischen
// Pixeln sollte hier auch eine dementsprechende Größe eingestellt
// werden.
context.set_viewport(paradiso::Rectangle{
.position = paradiso::Point{.x = 0, .y = 0}, .size = canvas_size});
// Conflower blue
context.set_clearcolor(paradiso::RGBA::from_rgb(0x64, 0x95, 0xED));
// Der Asset Loader bekommt den Pfad.
paradiso::BitmapIO::get().set_path("assets");
// Wir initialisieren unsere Sachen.
auto game_state = GameState::Start;
auto background = Background{};
auto ground = Ground{};
auto rocks1 = Rocks{};
auto rocks2 = Rocks{};
auto plane = Plane{};
auto start_text = StartText{};
auto game_over_text = GameOverText{};
background.init();
ground.init();
rocks1.init(1.2f, true); // We don't need to set this to true,
rocks2.init(2.4f, true); // but why not?
plane.init();
start_text.init();
game_over_text.init();
auto top_aabb =
AABB{.position = {paradiso::Vector2<float>::make(-1.0f, 1.0f)},
.size = {paradiso::Vector2<float>::make(2.0f, 0.5f)}};
while (window.update([&](paradiso::Window& window) -> bool {
auto t1 = std::chrono::high_resolution_clock::now();
if (window.keyboard_input().size() &&
window.keyboard_input().top().key == 'Q') {
// Update-Loop beenden.
return false;
} }
if (game_state == GameState::Start) { return app;
if (window.keyboard_input().size() && }
window.keyboard_input().top().key == ACTION_KEY &&
window.keyboard_input().top().action == 1) {
game_state = GameState::Playing;
// Make the plane jump for the first time. void run() {
// @ToDo: The plane jumps too high because there was no gravity auto canvas_size =
// before. paradiso::Size{.width = WINDOW_WIDTH * WINDOW_SCALE,
plane.update(window.keyboard_input()); .height = WINDOW_HEIGHT * WINDOW_SCALE};
}
background.update(); // Unser Fenster, auf das wir rendern.
ground.update(); auto window = paradiso::Window();
start_text.update(); window
} else if (game_state == GameState::Playing) { .set_size(canvas_size) // ... Größe
background.update(); .set_position(paradiso::Point{.x = 200, .y = 200}) // ... Position
ground.update(); .set_title("ParadiSO.TappyPlane") // ... Titel
.set_visible(true); // ... und jetzt anzeigen!
rocks1.update(); // Der Fenster Context.
rocks2.update(); auto context = paradiso::Context{};
plane.update(window.keyboard_input()); // Ein Shader (Schattierungsprogramm).
auto shader = paradiso::Shader{};
background.scrolling_speed *= SCROLLING_SPEED_INC_FACTOR; // Wir nutzen einen vorgefertigten Shader, der speziell für Sprites ist.
ground.scrolling_speed *= SCROLLING_SPEED_INC_FACTOR; shader.load_preset(paradiso::Shader::Preset::Sprite);
rocks1.scrolling_speed *= SCROLLING_SPEED_INC_FACTOR;
rocks2.scrolling_speed *= SCROLLING_SPEED_INC_FACTOR;
if (plane.aabb.is_colliding_with(rocks1.top_aabb) ||
plane.aabb.is_colliding_with(rocks1.bottom_aabb) ||
plane.aabb.is_colliding_with(rocks2.top_aabb) ||
plane.aabb.is_colliding_with(rocks2.bottom_aabb) ||
plane.aabb.is_colliding_with(top_aabb) ||
plane.aabb.is_colliding_with(ground.aabb)) {
game_state = GameState::GameOver;
}
} else if (game_state == GameState::GameOver) {
if (window.keyboard_input().size() &&
window.keyboard_input().top().key == ACTION_KEY) {
// Reset everything.
background.init();
ground.init();
rocks1.init(1.2f, true);
rocks2.init(2.4f, true);
plane.init();
game_state = GameState::Start;
}
}
// Ein Viewport stellt die Sicht der Kamera dar, d.h. bei quadratischen
// Pixeln sollte hier auch eine dementsprechende Größe eingestellt
// werden.
context.set_viewport(paradiso::Rectangle{ context.set_viewport(paradiso::Rectangle{
.position = paradiso::Point{.x = 0, .y = 0}, .size = canvas_size}); .position = paradiso::Point{.x = 0, .y = 0}, .size = canvas_size});
context.clear();
background.draw(shader); // Conflower blue
context.set_clearcolor(paradiso::RGBA::from_rgb(0x64, 0x95, 0xED));
plane.draw(shader); // Der Asset Loader bekommt den Pfad.
paradiso::BitmapIO::get().set_path("assets");
rocks1.draw(shader); // Wir initialisieren unsere Sachen.
rocks2.draw(shader); auto game_state = GameState::Start;
ground.draw(shader); auto background = Background{};
auto ground = Ground{};
auto rocks1 = Rocks{};
auto rocks2 = Rocks{};
auto plane = Plane{};
auto start_ui = StartUI{};
auto game_over_ui = GameOverUI{};
// Kinda dumb that we check the game state here the second time but I background.init(sprites);
// like to seperate 'update' from 'draw'. ground.init(sprites);
if (game_state == GameState::Start) { rocks1.init(sprites, 1.2f); // We don't need to set this to true,
start_text.draw(shader); rocks2.init(sprites, 2.4f); // but why not?
} else if (game_state == GameState::GameOver) { plane.init(sprites);
game_over_text.draw(shader); start_ui.init(sprites);
} game_over_ui.init(sprites);
// Einen kurzen Moment warten, um auf 60 FPS zu kommen. auto top_aabb =
auto t2 = std::chrono::high_resolution_clock::now(); AABB{.position = {paradiso::Vector2<float>::make(-1.0f, 1.0f)},
auto duration = .size = {paradiso::Vector2<float>::make(2.0f, 0.5f)}};
std::chrono::duration_cast<std::chrono::microseconds>(t2 - t1);
auto wait = std::chrono::microseconds(1000000 / FRAME_RATE) - duration;
std::this_thread::sleep_for(wait);
return true; while (window.update([&](paradiso::Window& window) -> bool {
})) { auto t1 = std::chrono::high_resolution_clock::now();
};
if (window.keyboard_input().size() &&
window.keyboard_input().top().key == 'Q') {
// Update-Loop beenden.
return false;
}
if (game_state == GameState::Start) {
if (window.keyboard_input().size() &&
window.keyboard_input().top().key == ACTION_KEY &&
window.keyboard_input().top().action == 1) {
game_state = GameState::Playing;
// Make the plane jump for the first time.
// @ToDo: The plane jumps too high because there was no
// gravity before.
plane.update(window.keyboard_input());
}
background.update();
ground.update();
start_ui.update();
} else if (game_state == GameState::Playing) {
background.update();
ground.update();
rocks1.update();
rocks2.update();
plane.update(window.keyboard_input());
// @ToDo: Should we really do this here?
background.scrolling_speed *= SCROLLING_SPEED_INC_FACTOR;
ground.scrolling_speed *= SCROLLING_SPEED_INC_FACTOR;
rocks1.scrolling_speed *= SCROLLING_SPEED_INC_FACTOR;
rocks2.scrolling_speed *= SCROLLING_SPEED_INC_FACTOR;
if (plane.aabb.is_colliding_with(rocks1.top_aabb) ||
plane.aabb.is_colliding_with(rocks1.bottom_aabb) ||
plane.aabb.is_colliding_with(rocks2.top_aabb) ||
plane.aabb.is_colliding_with(rocks2.bottom_aabb) ||
plane.aabb.is_colliding_with(top_aabb) ||
plane.aabb.is_colliding_with(ground.aabb)) {
game_state = GameState::GameOver;
}
} else if (game_state == GameState::GameOver) {
if (window.keyboard_input().size() &&
window.keyboard_input().top().key == ACTION_KEY) {
// Reset everything.
background.reset();
ground.reset();
rocks1.reset();
rocks2.reset();
plane.reset();
game_state = GameState::Start;
}
}
context.set_viewport(
paradiso::Rectangle{.position = paradiso::Point{.x = 0, .y = 0},
.size = canvas_size});
context.clear();
background.draw(shader);
plane.draw(shader);
rocks1.draw(shader);
rocks2.draw(shader);
ground.draw(shader);
// Kinda dumb that we check the game state here the second time but
// I like to seperate 'update' from 'draw'.
if (game_state == GameState::Start) {
start_ui.draw(shader);
} else if (game_state == GameState::GameOver) {
game_over_ui.draw(shader);
}
// Einen kurzen Moment warten, um auf 60 FPS zu kommen.
auto t2 = std::chrono::high_resolution_clock::now();
auto duration =
std::chrono::duration_cast<std::chrono::microseconds>(t2 - t1);
auto wait =
std::chrono::microseconds(1000000 / FRAME_RATE) - duration;
std::this_thread::sleep_for(wait);
return true;
})) {
};
}
};
} // namespace TappyPlane
auto main() -> int {
std::srand(std::time(nullptr));
auto app = TappyPlane::App::create();
app.run();
return 0; return 0;
} }