Add some juice to platformer example
This commit is contained in:
parent
1ef17e8918
commit
30ed7a781e
1 changed files with 131 additions and 10 deletions
|
|
@ -6,6 +6,8 @@
|
|||
#include <psemek/geom/camera.hpp>
|
||||
#include <psemek/geom/constants.hpp>
|
||||
#include <psemek/geom/intersection.hpp>
|
||||
#include <psemek/random/generator.hpp>
|
||||
#include <psemek/random/uniform.hpp>
|
||||
#include <psemek/util/clock.hpp>
|
||||
#include <psemek/util/to_string.hpp>
|
||||
|
||||
|
|
@ -22,12 +24,26 @@ struct player
|
|||
geom::point<float, 2> position;
|
||||
geom::vector<float, 2> velocity;
|
||||
|
||||
geom::point<float, 2> ghost_position;
|
||||
geom::vector<float, 2> ghost_velocity;
|
||||
|
||||
bool grounded = false;
|
||||
|
||||
geom::box<float, 2> bbox() const
|
||||
{
|
||||
return geom::expand(geom::box<float, 2>::singleton(position), size);
|
||||
}
|
||||
};
|
||||
|
||||
struct particle
|
||||
{
|
||||
geom::point<float, 2> position;
|
||||
geom::vector<float, 2> 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<bool>(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<particle> 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<particle> particles_;
|
||||
float move_particle_spawn_timer_ = 0.f;
|
||||
|
||||
random::generator rng_;
|
||||
|
||||
util::clock<> frame_clock_;
|
||||
gfx::painter painter_;
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue