From 30ed7a781e73ce1c84251c7a96e35b39e1b68018 Mon Sep 17 00:00:00 2001 From: lisyarus Date: Sat, 13 May 2023 10:36:39 +0300 Subject: [PATCH] Add some juice to platformer example --- examples/platformer.cpp | 141 +++++++++++++++++++++++++++++++++++++--- 1 file changed, 131 insertions(+), 10 deletions(-) diff --git a/examples/platformer.cpp b/examples/platformer.cpp index a50166de..85e16683 100644 --- a/examples/platformer.cpp +++ b/examples/platformer.cpp @@ -6,6 +6,8 @@ #include #include #include +#include +#include #include #include @@ -22,12 +24,26 @@ struct player geom::point position; geom::vector velocity; + geom::point ghost_position; + geom::vector ghost_velocity; + + bool grounded = false; + geom::box bbox() const { return geom::expand(geom::box::singleton(position), size); } }; +struct particle +{ + geom::point position; + geom::vector velocity; + float size; + float lifetime; + gfx::color_rgba color; +}; + struct platformer_app : app::app { platformer_app() @@ -44,6 +60,8 @@ struct platformer_app : app::app player_.size = {0.25f, 0.5f}; player_.position = {0.f, 5.f}; player_.velocity = {0.f, 0.f}; + player_.ghost_position = player_.position; + player_.ghost_velocity = {0.f, 0.f}; } void update() override @@ -51,14 +69,17 @@ struct platformer_app : app::app float const dt = frame_clock_.restart().count(); float const gravity = -50.f; - float const acceleration = 50.f; - float const friction = 10.f; + float const acceleration = 200.f; + float const friction = 25.f; float const jump_speed = 15.f; - if (is_key_down(SDLK_a)) - player_.velocity[0] -= acceleration * dt; - if (is_key_down(SDLK_d)) - player_.velocity[0] += acceleration * dt; + geom::vector const ghost_spring_force{200.f, 500.f}; + geom::vector const ghost_spring_damping{10.f, 20.f}; + + float const particle_grow_speed = 0.1f; + float const move_particle_spawn_period = 1.f / 32.f; + int const jump_particle_count = 16; + int const land_particle_count = 32; player_.velocity[0] *= std::exp(- friction * dt); @@ -66,7 +87,18 @@ struct platformer_app : app::app player_.position += player_.velocity * dt; - bool grounded = false; + { + auto v = (player_.position - player_.ghost_position); + player_.ghost_velocity += geom::pointwise_mult(v, ghost_spring_force) * dt; + player_.ghost_velocity[0] *= std::exp(- ghost_spring_damping[0] * dt); + player_.ghost_velocity[1] *= std::exp(- ghost_spring_damping[1] * dt); + } + player_.ghost_position += player_.ghost_velocity * dt; + + bool was_grounded = player_.grounded; + auto velocity_before_collision = player_.velocity; + + player_.grounded = false; for (auto const & platform : map_.platforms) { @@ -88,7 +120,7 @@ struct platformer_app : app::app if (intersection[1].center() < player_.position[1]) { player_.position[1] += intersection[1].length(); - grounded = true; + player_.grounded = true; } else player_.position[1] -= intersection[1].length(); @@ -97,8 +129,77 @@ struct platformer_app : app::app } } - if (grounded && is_key_down(SDLK_w)) + if (is_key_down(SDLK_a)) + player_.velocity[0] -= acceleration * dt; + if (is_key_down(SDLK_d)) + player_.velocity[0] += acceleration * dt; + + if (player_.grounded && (is_key_down(SDLK_a) ^ is_key_down(SDLK_d))) + { + move_particle_spawn_timer_ += dt; + if (move_particle_spawn_timer_ > move_particle_spawn_period) + { + move_particle_spawn_timer_ -= move_particle_spawn_period; + float s = is_key_down(SDLK_a) ? 1.f : -1.f; + + auto position = player_.position + geom::vector{s * player_.size[0], -player_.size[1]}; + auto velocity = geom::direction(random::uniform(rng_, geom::rad(60.f), geom::rad(120.f))); + auto size = random::uniform(rng_, 0.05f, 0.15f); + auto lifetime = random::uniform(rng_, 0.25f, 0.5f); + int c = random::uniform(rng_, 63, 191); + gfx::color_rgba color{c, 0, c, 255}; + + particles_.push_back({position, velocity, size, lifetime, color}); + } + } + + if (player_.grounded && is_key_down(SDLK_w)) + { player_.velocity[1] += jump_speed; + + for (int i = 0; i < jump_particle_count; ++i) + { + float s = random::uniform(rng_, -1.f, 1.f); + + auto position = player_.position + geom::vector{s * player_.size[0], -player_.size[1]}; + auto velocity = (random::uniform(rng_) ? 1.f : -1.f) * geom::direction(random::uniform(rng_, geom::rad(-30.f), geom::rad(30.f))) * 3.f; + auto size = random::uniform(rng_, 0.05f, 0.15f); + auto lifetime = random::uniform(rng_, 0.25f, 0.5f); + int c = random::uniform(rng_, 63, 191); + gfx::color_rgba color{c, 0, c, 255}; + + particles_.push_back({position, velocity, size, lifetime, color}); + } + } + + if (!was_grounded && player_.grounded) + { + for (int i = 0; i < land_particle_count; ++i) + { + float s = random::uniform(rng_, -1.f, 1.f); + + auto position = player_.position + geom::vector{s * player_.size[0], -player_.size[1]}; + auto velocity = geom::direction(random::uniform(rng_, geom::rad(0.f), geom::rad(180.f))) * (-velocity_before_collision[1]) * 0.2f; + auto size = random::uniform(rng_, 0.05f, 0.15f); + auto lifetime = random::uniform(rng_, 0.25f, 0.5f); + int c = random::uniform(rng_, 63, 191); + gfx::color_rgba color{c, 0, c, 255}; + + particles_.push_back({position, velocity, size, lifetime, color}); + } + } + + std::vector alive_particles; + for (auto & p : particles_) + { + p.lifetime -= dt; + if (p.lifetime <= 0.f) continue; + + p.position += p.velocity * dt; + p.size += particle_grow_speed * dt; + alive_particles.push_back(p); + } + particles_ = std::move(alive_particles); } void present() override @@ -109,7 +210,23 @@ struct platformer_app : app::app for (auto const & box : map_.platforms) painter_.rect(box, {0, 0, 0, 255}); - painter_.rect(player_.bbox(), {255, 0, 0, 255}); + { + geom::point bottom = player_.position - geom::vector{0.f, player_.size[1]}; + geom::point top = player_.ghost_position + geom::vector{0.f, player_.size[1]}; + + auto d = geom::vector{player_.size[0], 0.f}; + + gfx::color_rgba color{255, 0, 0, 255}; + painter_.triangle(bottom - d, bottom + d, top - d, color); + painter_.triangle(bottom + d, top - d, top + d, color); + } + + for (auto const & p : particles_) + { + auto colorf = gfx::to_colorf(p.color); + colorf[3] *= 1.f - std::exp(- 2.f * p.lifetime); + painter_.circle(p.position, p.size, gfx::to_coloru8(colorf)); + } float const aspect_ratio = width() * 1.f / height(); float const view_size = 5.f; @@ -121,6 +238,10 @@ struct platformer_app : app::app private: map map_; player player_; + std::vector particles_; + float move_particle_spawn_timer_ = 0.f; + + random::generator rng_; util::clock<> frame_clock_; gfx::painter painter_;