Commit 45b6a639 authored by robert's avatar robert
Browse files

Merge branch 'rahix/player-control-collide' into 'master'

Controllable and colliding player entity

See merge request !12
parents 6282dd5a 4716c1a3
GIMP Palette
Name: RC3 Colors
# Primaries & Shades
178 57 255 #b239ff
103 2 159 #67029f
68 0 105 #440069
36 0 56 #240038
104 0 231 #6800e7
65 0 139 #41008b
42 0 94 #2a005e
20 0 47 #14002f
5 185 236 #05b9ec
0 118 169 #0076a9
2 93 132 #025d84
0 42 58 #002a3a
# Background
16 14 35 #100e23
......@@ -5,9 +5,9 @@ use ncollide2d::shape;
use std::sync::mpsc;
pub struct Collider {
handle: Option<ncollide2d::pipeline::CollisionObjectSlabHandle>,
pub handle: Option<ncollide2d::pipeline::CollisionObjectSlabHandle>,
drop_notifier: Option<mpsc::SyncSender<ncollide2d::pipeline::CollisionObjectSlabHandle>>,
shape: shape::ShapeHandle<f32>,
pub shape: shape::ShapeHandle<f32>,
groups: pipeline::CollisionGroups,
}
......@@ -36,7 +36,7 @@ impl Collider {
}
pub fn new_rect_collider(width: f32, height: f32) -> Collider {
let shape = shape::Cuboid::new(nalgebra::Vector2::new(width, height));
let shape = shape::Cuboid::new(nalgebra::Vector2::new(width / 2.0, height / 2.0));
let groups = pipeline::CollisionGroups::new();
Collider::new(shape::ShapeHandle::new(shape), groups)
......@@ -55,7 +55,7 @@ impl Drop for Collider {
}
pub struct CollisionWorld {
world: ncollide2d::world::CollisionWorld<f32, legion::Entity>,
pub world: ncollide2d::world::CollisionWorld<f32, legion::Entity>,
drop_notifier: mpsc::SyncSender<ncollide2d::pipeline::CollisionObjectSlabHandle>,
drop_receiver: mpsc::Receiver<ncollide2d::pipeline::CollisionObjectSlabHandle>,
}
......
mod edge;
mod movable;
mod node;
mod orbitbody;
mod player;
mod position;
mod thesun;
pub use edge::Edge;
pub use movable::{Gravity, Movable};
pub use movable::Movable;
pub use node::Node;
pub use orbitbody::{Gravity, OrbitBody};
pub use player::Player;
pub use position::Position;
pub use thesun::TheSun;
pub use edge::draw_edges_system;
pub use movable::{update_gravity_system, update_movement_system};
pub use node::{draw_nodes_system, update_nodes_system};
pub use orbitbody::{update_gravity_system, update_movement_system};
pub use thesun::draw_thesun_system;
use crate::components;
pub struct Movable {
velocity: nalgebra::Vector2<f32>,
acceleration: nalgebra::Vector2<f32>,
pub velocity: nalgebra::Vector2<f32>,
}
impl Movable {
pub fn new(vel_x: f32, vel_y: f32) -> Movable {
pub fn new() -> Movable {
Movable {
velocity: nalgebra::Vector2::new(vel_x, vel_y),
acceleration: nalgebra::Vector2::new(0.0, 0.0),
velocity: nalgebra::Vector2::new(0.0, 0.0),
}
}
}
#[legion::system(for_each)]
pub fn update_movement(pos: &mut components::Position, mov: &mut Movable) {
let delta = 0.3;
mov.velocity += mov.acceleration * delta;
pos.0 += mov.velocity * delta;
}
pub struct Gravity;
#[legion::system(for_each)]
pub fn update_gravity(_: &Gravity, mov: &mut Movable, pos: &components::Position) {
let sun = nalgebra::Point2::new(600.0, 540.0);
let distance_vector = sun - pos.0;
let gravity = 60000.0 / distance_vector.magnitude_squared();
mov.acceleration = distance_vector.normalize() * gravity;
}
use crate::components;
pub struct OrbitBody {
velocity: nalgebra::Vector2<f32>,
acceleration: nalgebra::Vector2<f32>,
}
impl OrbitBody {
pub fn new(vel_x: f32, vel_y: f32) -> OrbitBody {
OrbitBody {
velocity: nalgebra::Vector2::new(vel_x, vel_y),
acceleration: nalgebra::Vector2::new(0.0, 0.0),
}
}
}
#[legion::system(for_each)]
pub fn update_movement(pos: &mut components::Position, mov: &mut OrbitBody) {
let delta = 0.3;
mov.velocity += mov.acceleration * delta;
pos.0 += mov.velocity * delta;
}
pub struct Gravity;
#[legion::system(for_each)]
pub fn update_gravity(_: &Gravity, mov: &mut OrbitBody, pos: &components::Position) {
let sun = nalgebra::Point2::new(600.0, 540.0);
let distance_vector = sun - pos.0;
let gravity = 60000.0 / distance_vector.magnitude_squared();
mov.acceleration = distance_vector.normalize() * gravity;
}
pub struct Player;
mod player;
pub use player::create_player;
use crate::colliders;
use crate::components;
pub fn create_player(world: &mut legion::World) -> legion::Entity {
world.push((
components::Player,
components::Position::new(600.0, 600.0),
components::Movable::new(),
colliders::Collider::new_circle_collider(50.0),
))
}
......@@ -3,6 +3,7 @@ use wasm_bindgen::prelude::*;
pub mod colliders;
pub mod colors;
pub mod components;
pub mod entities;
pub mod gamestate;
pub mod resources;
pub mod states;
......
......@@ -15,8 +15,8 @@ impl Clock {
Clock { perf, start }
}
pub fn game_time(&self) -> f64 {
(self.perf.now() - self.start) / 1000.0
pub fn frame_delta(&self) -> f32 {
0.1
}
pub fn wall_time(&self) -> f64 {
......
mod camera;
mod clock;
mod player;
mod rendering;
pub use camera::Camera;
pub use clock::Clock;
pub use player::Player;
pub use rendering::Color;
pub use rendering::ImageHandle;
pub use rendering::Rendering;
/// Stores the entity ID of the player
pub struct Player(pub legion::Entity);
......@@ -22,31 +22,31 @@ impl HeavenState {
let n0 = node_world.push((
components::Node::new(),
components::Position::new(800.0, 340.0),
components::Movable::new(5.0, 10.0),
components::OrbitBody::new(5.0, 10.0),
components::Gravity,
));
let n1 = node_world.push((
components::Node::new(),
components::Position::new(120.0, 300.0),
components::Movable::new(5.0, -5.0),
components::OrbitBody::new(5.0, -5.0),
components::Gravity,
));
let n2 = node_world.push((
components::Node::new(),
components::Position::new(700.0, 740.0),
components::Movable::new(8.0, -4.0),
components::OrbitBody::new(8.0, -4.0),
components::Gravity,
));
let n3 = node_world.push((
components::Node::new(),
components::Position::new(340.0, 290.0),
components::Movable::new(10.0, 0.0),
components::OrbitBody::new(10.0, 0.0),
components::Gravity,
));
let n4 = node_world.push((
components::Node::new(),
components::Position::new(300.0, 400.0),
components::Movable::new(0.0, 10.0),
components::OrbitBody::new(0.0, 10.0),
components::Gravity,
));
......
use crate::colliders;
use crate::colors;
use crate::components;
use crate::entities;
use crate::gamestate;
use crate::resources;
use crate::svg_loader;
......@@ -20,23 +22,33 @@ impl InGameState {
let background = rendering.register_image(level.background_image.clone());
let mut resources = legion::Resources::default();
let mut world = legion::World::default();
resources.insert(resources::Clock::new());
resources.insert(rendering);
resources.insert(resources::Camera::new(1920.0, 1080.0));
resources.insert(colliders::CollisionWorld::new());
let mut world = legion::World::default();
let player = entities::create_player(&mut world);
resources.insert(resources::Player(player));
level.apply_colliders(&mut world);
let schedule = legion::Schedule::builder()
.add_system(colliders::synchronize_collisision_world_system())
.flush()
.add_system(systems::moving_system())
.add_thread_local(systems::camera_system())
.add_system(colliders::update_collision_world_system())
.flush()
.add_thread_local(systems::move_movable_system())
.flush()
.add_thread_local(systems::move_camera_to_player_system())
.flush()
.add_thread_local(systems::camera_system())
.flush()
.add_thread_local(systems::draw_level_layer_system(background))
.add_thread_local(systems::draw_tmp_player_system())
.add_thread_local(systems::draw_level_layer_system(foreground))
// .add_thread_local(systems::draw_debug_colliders_system())
.build();
InGameState {
......@@ -71,12 +83,21 @@ impl gamestate::State for InGameState {
}
fn event(&mut self, event: gamestate::Event) -> gamestate::Transition {
let mut cam = self.resources.get_mut::<resources::Camera>().unwrap();
use legion::IntoQuery;
let player = self.resources.get::<resources::Player>().unwrap().0.clone();
let player_movable = <&mut components::Movable>::query()
.get_mut(&mut self.world, player)
.unwrap();
match event {
gamestate::Event::KeyDown("w") => cam.position.y -= 50.0,
gamestate::Event::KeyDown("a") => cam.position.x -= 50.0,
gamestate::Event::KeyDown("s") => cam.position.y += 50.0,
gamestate::Event::KeyDown("d") => cam.position.x += 50.0,
gamestate::Event::KeyDown("w") => player_movable.velocity.y = -50.0,
gamestate::Event::KeyUp("w") => player_movable.velocity.y = 0.0,
gamestate::Event::KeyDown("a") => player_movable.velocity.x = -50.0,
gamestate::Event::KeyUp("a") => player_movable.velocity.x = 0.0,
gamestate::Event::KeyDown("s") => player_movable.velocity.y = 50.0,
gamestate::Event::KeyUp("s") => player_movable.velocity.y = 0.0,
gamestate::Event::KeyDown("d") => player_movable.velocity.x = 50.0,
gamestate::Event::KeyUp("d") => player_movable.velocity.x = 0.0,
_ => (),
}
gamestate::Transition::Keep
......
......@@ -122,7 +122,7 @@ impl Collider {
"g[inkscape\\:groupmode=\"layer\"][inkscape\\:label=\"collider\"] rect",
);
if nodes.length() > 0 {
crate::console_warn!("Unknown elements in collider layer");
panic!("Unknown elements in collider layer! Only <circle /> and <rect /> are allowed right now.");
}
colliders
......@@ -182,7 +182,8 @@ impl SvgLevel {
height,
} => {
let collider = colliders::Collider::new_rect_collider(*width, *height);
let position = components::Position::new(*x, *y);
// Position must be the center of the rectangle
let position = components::Position::new(x + width / 2.0, y + height / 2.0);
world.push((collider, position));
}
}
......
use crate::colliders;
use crate::components;
use crate::resources;
use ncollide2d::shape;
const DEBUG_COLLIDER_COLOR: resources::Color = resources::Color {
red: 1.0,
green: 0.0,
blue: 0.0,
alpha: 0.5,
};
#[legion::system(for_each)]
pub fn draw_debug_colliders(
position: &components::Position,
collider: &colliders::Collider,
#[resource] rendering: &mut resources::Rendering,
) {
rendering.set_fill_style(&DEBUG_COLLIDER_COLOR);
if let Some(ball) = collider.shape.downcast_ref::<shape::Ball<f32>>() {
rendering.begin_path();
rendering.arc(
position.0.x as f64,
position.0.y as f64,
ball.radius as f64,
0.0,
std::f64::consts::TAU,
);
rendering.fill();
} else if let Some(cuboid) = collider.shape.downcast_ref::<shape::Cuboid<f32>>() {
rendering.fill_rect(
position.0.x as f64 - cuboid.half_extents.x as f64,
position.0.y as f64 - cuboid.half_extents.y as f64,
cuboid.half_extents.x as f64 * 2.0,
cuboid.half_extents.y as f64 * 2.0,
);
}
}
mod camera;
mod draw_colliders;
mod level;
mod moving;
mod tmp_player;
pub use camera::camera_system;
pub use draw_colliders::draw_debug_colliders_system;
pub use level::draw_level_layer_system;
pub use moving::moving_system;
pub use moving::{move_camera_to_player_system, move_movable_system};
pub use tmp_player::draw_tmp_player_system;
use crate::colliders;
use crate::components;
use crate::resources;
use legion::IntoQuery;
#[legion::system]
pub fn moving(#[resource] camera: &mut resources::Camera) {
camera.position.x += 0.1;
#[read_component(components::Position)]
pub fn move_camera_to_player(
world: &legion::world::SubWorld,
#[resource] camera: &mut resources::Camera,
#[resource] player: &resources::Player,
) {
let mut positions = <&components::Position>::query();
let pos = positions.get(world, player.0).unwrap().0;
camera.position = nalgebra::Point2::new(pos.x as f64, pos.y as f64);
}
#[legion::system(for_each)]
pub fn move_movable(
position: &mut components::Position,
movable: &mut components::Movable,
collider: &colliders::Collider,
#[resource] clock: &resources::Clock,
#[resource] collision_world: &colliders::CollisionWorld,
) {
let mut actual_velocity = movable.velocity.clone();
// First, check if the entity is colliding. If so, zero out its movement "into" the collision
// before advancing the position.
for pair in collision_world
.world
.contacts_with(collider.handle.unwrap(), false)
.unwrap()
{
if let Some(contact) = pair.3.deepest_contact() {
// Length of the projection of the velocity onto the normal. If it's greater than 0,
// we're moving "into" the collider and thus, this component of the velocity should be
// zeroed out.
let d: f32 = contact.contact.normal.dot(&actual_velocity);
if d > 0.0 {
actual_velocity -= contact.contact.normal.into_inner() * d;
}
}
}
position.0 += actual_velocity * clock.frame_delta();
}
use crate::colors;
use crate::components;
use crate::resources;
#[legion::system(for_each)]
pub fn draw_tmp_player(
_player: &components::Player,
pos: &components::Position,
#[resource] rendering: &mut resources::Rendering,
) {
rendering.begin_path();
rendering.set_fill_style(&colors::PRIMARY3_SHADE1);
rendering.arc(
pos.0.x as f64,
pos.0.y as f64,
50.0,
0.0,
std::f64::consts::TAU,
);
rendering.fill();
}
This diff is collapsed.
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment