use bevy::prelude::*;
use bevy_physimple::prelude::*;
#[derive(Default, Component)]
pub struct Player {
double_jump: bool,
on_wall: Option<Vec2>,
on_floor: bool
}
pub struct Gravity(Vec2);
fn main() {
let mut app = App::new();
app .insert_resource(WindowDescriptor {
title: "A cool name for an example".to_string(),
..Default::default()
})
.add_plugins(DefaultPlugins)
.add_plugin(Physics2dPlugin)
.add_system(bevy::window::close_on_esc)
;
app .add_startup_system(setup_sys)
;
app .add_system(controller_on_stuff_sys)
.add_system(character_system_sys)
.add_system(change_sensor_color_sys)
.add_system(gravity_sys)
.add_system(ray_head_sys)
;
app.run();
}
fn setup_sys(
mut coms: Commands,
a_server: Res<AssetServer>,
) {
let wall = Color::BLACK;
coms.insert_resource(Gravity(Vec2::new(0.0,-540.0)));
coms.spawn_bundle(Camera2dBundle::default());
let style = TextStyle {
font: a_server.load("fonts/FiraSans-Bold.ttf"),
font_size: 32.0,
color: Color::ANTIQUE_WHITE,
};
let text = "A/D - Movement\nSpace/W - Jump/Double jump\nS - Stomp(when mid air)";
coms
.spawn_bundle(Text2dBundle {
text: Text::from_section(text, style),
transform: Transform::from_xyz(-270.0, 360.0, 0.0),
..Default::default()
})
;
coms
.spawn_bundle(SpriteBundle {
sprite: Sprite {
custom_size: Some(Vec2::splat(28.0)),
color: Color::ALICE_BLUE,
..Default::default()
},
..Default::default()
})
.insert_bundle(KinematicBundle {
shape: CollisionShape::Square(Square::size(Vec2::splat(28.0))),
..Default::default()
})
.insert(Player::default())
.insert(
RayCast::new(Vec2::new(100.0,0.0))
.with_offset(Vec2::new(14.0,0.0)) .with_static(true) )
.with_children(|p| {
p.spawn_bundle(SpriteBundle {
sprite: Sprite {
custom_size: Some(Vec2::splat(10.0)),
color: Color::MIDNIGHT_BLUE,
..Default::default()
},
..Default::default()
});
})
;
coms
.spawn_bundle(SpriteBundle {
sprite: Sprite {
custom_size: Some(Vec2::new(600.0, 30.0)),
color: wall,
..Default::default()
},
transform: Transform::from_xyz(150.0, -200.0, 0.0),
..Default::default()
})
.insert_bundle(StaticBundle {
shape: CollisionShape::Square(Square::size(Vec2::new(600.0, 30.0))),
..Default::default()
})
;
coms
.spawn_bundle(SpriteBundle {
sprite: Sprite {
custom_size: Some(Vec2::new(40.0, 300.0)),
color: wall,
..Default::default()
},
transform: {
let mut t = Transform::from_xyz(450.0, 0.0, 0.0);
t.rotation = Quat::from_rotation_z(-0.1 * 3.14);
t
},
..Default::default()
})
.insert_bundle(StaticBundle {
shape: CollisionShape::Square(Square::size(Vec2::new(40.0, 300.0))),
..Default::default()
})
;
coms
.spawn_bundle(SpriteBundle {
sprite: Sprite {
custom_size: Some(Vec2::new(30.0, 90.0)),
color: wall,
..Default::default()
},
transform: Transform::from_xyz(-150.0, -160.0, 0.0),
..Default::default()
})
.insert_bundle(StaticBundle {
shape: CollisionShape::Square(Square::size(Vec2::new(30.0,90.0))),
..Default::default()
})
;
coms
.spawn_bundle(SpriteBundle {
sprite: Sprite {
custom_size: Some(Vec2::new(200.0,30.0)),
color: wall,
..Default::default()
},
transform: Transform::from_xyz(-150.0, 0.0,0.0),
..Default::default()
})
.insert_bundle(StaticBundle {
shape: CollisionShape::Square(Square::size(Vec2::new(200.0, 30.0))),
..Default::default()
})
;
const SENSOR_SIZE: f32 = 50.0;
coms
.spawn_bundle(SpriteBundle {
sprite: Sprite {
custom_size: Some(Vec2::splat(SENSOR_SIZE)),
color: Color::GOLD,
..Default::default()
},
transform: Transform::from_xyz(30.0, -150.0, 0.0),
..Default::default()
})
.insert_bundle(SensorBundle {
shape: CollisionShape::Square(Square::size(Vec2::splat(SENSOR_SIZE))),
..Default::default()
});
const CUBE_SIZE: f32 = 35.0;
coms
.spawn_bundle(SpriteBundle {
sprite: Sprite {
custom_size: Some(Vec2::splat(CUBE_SIZE)),
color: Color::CRIMSON,
..Default::default()
},
transform: Transform::from_xyz(100.0,0.0,0.0),
..Default::default()
})
.insert_bundle(KinematicBundle {
shape: CollisionShape::Square(Square::size(Vec2::splat(CUBE_SIZE))),
..Default::default()
})
;
}
fn gravity_sys(
time: Res<Time>,
grav: Res<Gravity>,
mut q: Query<&mut Vel>,
) {
let g = grav.0;
let t = time.delta_seconds();
for mut v in q.iter_mut() {
v.0 += t * g;
}
}
fn controller_on_stuff_sys(
mut query: Query<(Entity, &mut Player)>,
mut colls: EventReader<CollisionEvent>,
) {
let (e, mut c) = query.single_mut();
c.on_floor = false;
c.on_wall = None;
for coll in colls.iter().filter(|&c| c.is_b_static) {
if coll.entity_a == e {
let n = coll.normal.dot(Vec2::Y);
if n > 0.7 {
c.on_floor = true;
}
else if n.abs() <= 0.7 {
c.on_wall = Some(coll.normal);
}
}
}
}
fn character_system_sys(
input: Res<Input<KeyCode>>,
time: Res<Time>,
gravity: Res<Gravity>,
mut query: Query<(&mut Player, &mut Vel)>,
) {
let gravity = gravity.0;
for (mut controller, mut vel) in query.iter_mut() {
if let Some(normal) = controller.on_wall {
vel.0 -= normal * 0.1;
if vel.0.y < -1.0 {
vel.0.y = -1.0;
}
}
let jump = |body: &Player, vel: &mut Vel| {
vel.0 = vel.0.slide(gravity.normalize()) - gravity * 0.6;
let wall = body.on_wall.unwrap_or(Vec2::ZERO) * 250.0;
vel.0 += wall;
};
let should_jump = input.just_pressed(KeyCode::Space) || input.just_pressed(KeyCode::W);
if controller.on_floor || controller.on_wall.is_some() {
controller.double_jump = true;
if should_jump {
jump(&controller, &mut vel);
}
}
else if controller.double_jump && should_jump {
controller.double_jump = false;
jump(&controller, &mut vel);
}
if input.just_pressed(KeyCode::S) && !controller.on_floor {
vel.0 = Vec2::new(0.0, -5000.0);
}
let acc = Vec2::new(1000.0, 0.0) * time.delta_seconds();
if input.pressed(KeyCode::A) {
vel.0 -= acc;
}
else if input.pressed(KeyCode::D) {
vel.0 += acc;
}
else {
vel.0.x *= 1.0 - (10.0 * time.delta_seconds());
}
const TERMINAL_X: f32 = 500.0;
if vel.0.x.abs() > TERMINAL_X {
vel.0.x = TERMINAL_X.copysign(vel.0.x); }
}
}
fn change_sensor_color_sys(
mut q: Query<(&Sensor, &mut Sprite)>,
) {
for (s, mut h) in q.iter_mut() {
h.color = if s.bodies.len() == 0 {
Color::GOLD
}
else {
Color::rgba(0.0, 0.5, 1.0, 0.5)
}
}
}
fn ray_head_sys(
mut ts: Query<&mut Transform, Without<RayCast>>,
q: Query<(&RayCast, &Children, &Transform)>,
) {
for (r,c, rt) in q.iter() {
if let Some(c) = c.first() {
if let Ok(mut t) = ts.get_mut(*c) {
let pos = Vec2::new(rt.translation.x, rt.translation.y);
t.translation = r.collision.map(|a| a.collision_point - pos).unwrap_or(r.cast + r.offset).extend(0.0);
}
}
}
}