use std::f32::consts::FRAC_PI_2;
use sge::prelude::*;
const PLAYER_RADIUS: f32 = 30.0;
struct Player {
color: Color,
controller: PlayerController,
}
fn main() -> anyhow::Result<()> {
init("2D Platformer")?;
let mut world = World::new();
let mut player = Player {
color: Color::RED_500,
controller: world
.create_player_controller(Bounds::Rect(Vec2::splat(PLAYER_RADIUS * 2.0)))
.with_position(Vec2::new(0.0, -200.0)),
};
const PREV_LEN: usize = 100;
const PREV_REPEAT: usize = 10;
let mut prev_pos = RotatingArray::new([Vec2::ZERO; PREV_LEN]);
player
.controller
.set_binds()
.jump(KeyCode::Space)
.right(KeyCode::KeyF)
.left(KeyCode::KeyS);
player.controller.set_move_speed(5.0);
player.controller.set_double_jumps(1);
player.controller.set_jump_velocity(13.0);
world.set_gravity(50.0);
let platforms = vec![
platform(-500.0, 400.0, 1000.0),
platform(-400.0, 250.0, 200.0),
platform(200.0, 250.0, 200.0),
platform(-100.0, 100.0, 200.0),
];
for p in &platforms {
world
.create_fixed(Bounds::Rect(p.size))
.with_position(p.center());
}
let mut ps = ParticleSystem::new();
let jump_particles = ParticleBatch::builder()
.shape(Rect::new_square(
Vec2::ZERO,
10.0,
Color::WHITE.with_alpha(0.3),
))
.color_randomness(Color::BLACK.with_alpha(0.1))
.end_color(Color::WHITE.with_alpha(0.0))
.speed(5.0)
.direction(FRAC_PI_2)
.direction_randomness(FRAC_PI_2)
.speed_randomness(3.0)
.lifetime_randomness(0.5)
.rotation_speed_randomness(0.2)
.lifetime(1.0)
.build();
let land_particles = ParticleBatch::builder()
.shape(Rect::new_square(
Vec2::ZERO,
10.0,
Color::WHITE.with_alpha(0.1),
))
.color_randomness(Color::BLACK.with_alpha(0.1))
.end_color(Color::WHITE.with_alpha(0.0))
.speed(5.0)
.direction(-FRAC_PI_2)
.direction_randomness(FRAC_PI_2)
.acceleration(vec2(0.0, 0.2))
.speed_randomness(3.0)
.rotation_speed_randomness(0.2)
.quantity(5)
.quantity_randomness(2.0)
.lifetime_randomness(0.5)
.lifetime(0.5)
.build();
let mut debug_mode = false;
let mut camera_controller = PanningCameraController {
allow_panning: false,
..Default::default()
};
loop {
ps.update();
camera_controller.update();
if !debug_mode {
clear_screen(Color::NEUTRAL_900);
vignette_screen(Color::NEUTRAL_950, 0.5);
run_egui(|ui| {
draw_debug_info(ui);
});
}
draw_multiline_text(
"Press D to toggle debug mode\nSpace to jump\nA to move right\nF to move left",
Vec2::splat(10.0),
1.0,
);
if !debug_mode {
draw_2d_grid_world(Color::NEUTRAL_800);
}
if key_pressed(KeyCode::KeyD) {
debug_mode = !debug_mode;
}
if key_pressed(KeyCode::KeyR) {
player.controller.set_position(Vec2::new(0.0, -200.0));
}
if debug_mode {
world.draw_colliders_world();
}
if player.controller.position().y > 1000.0
&& player.controller.velocity().y.is_sign_negative()
{
player.controller.add_impulse(Vec2::Y * 30.0);
}
if player.controller.just_double_jumped() {
ps.spawn_batch(
&jump_particles,
player.controller.position() + vec2(0.0, PLAYER_RADIUS),
);
}
if player.controller.just_landed() {
ps.spawn_batch(
&land_particles,
player.controller.position() + vec2(0.0, PLAYER_RADIUS),
);
}
{
let ppos = player.controller.position();
let cpos = get_camera_2d().translation();
let (normal, len) = (ppos - cpos).normalize_and_length();
if len > 300.0 {
let pos = -normal * 300.0 + ppos;
get_camera_2d_mut().set_translation(pos);
}
}
for i in 0..PREV_REPEAT {
let ratio = (i + 1) as f32 / PREV_REPEAT as f32;
let pos = player
.controller
.position_last_frame()
.lerp(player.controller.position(), ratio);
prev_pos.push(pos);
}
if !debug_mode {
for (i, pos) in prev_pos.iter().enumerate() {
let age = i as f32 / PREV_LEN as f32;
draw_circle_world(
*pos,
PLAYER_RADIUS * age,
player.color.darken(0.2).desaturate(0.5),
);
}
draw_circle_world(player.controller.position(), PLAYER_RADIUS, player.color);
for p in &platforms {
draw_world(p);
}
ps.draw_world();
}
world.update();
if should_quit() {
break;
}
next_frame();
}
Ok(())
}
fn platform(x: f32, y: f32, w: f32) -> Rect {
Rect::new(Vec2::new(x, y), Vec2::new(w, 20.0), Color::NEUTRAL_800)
}