Starting to do physics
This commit is contained in:
+149
-32
@@ -1,82 +1,199 @@
|
||||
use std::time::Duration;
|
||||
use std::time::{
|
||||
Instant,
|
||||
Duration,
|
||||
};
|
||||
|
||||
use sfml::system::Vector2;
|
||||
use sfml::window::Event;
|
||||
use sfml::graphics::{
|
||||
Drawable,
|
||||
RenderStates,
|
||||
RenderTarget,
|
||||
Sprite,
|
||||
Color,
|
||||
};
|
||||
use sfml::graphics::IntRect;
|
||||
use sfml::window::Key;
|
||||
|
||||
use engine::scene::Updatable;
|
||||
use engine::controls::Controls;
|
||||
use engine::renderer::Drawable;
|
||||
use engine::texture::Texture;
|
||||
use engine::physics;
|
||||
use engine::math::{
|
||||
duration_as_f32,
|
||||
duration_as_frame,
|
||||
};
|
||||
|
||||
/// The different sides a character can face.
|
||||
pub enum Side {
|
||||
/// The character looks to the left.
|
||||
Left,
|
||||
|
||||
/// The character looks to the right.
|
||||
Right,
|
||||
}
|
||||
|
||||
impl Side {
|
||||
/// Returns the side corresponding to the force.
|
||||
///
|
||||
/// Returns None if the force is null.
|
||||
pub fn from_force(force: Vector2<f32>) -> Option<Side> {
|
||||
if force.x > 0.0 {
|
||||
Some(Side::Right)
|
||||
} else if force.x < 0.0 {
|
||||
Some(Side::Left)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the offset in the texture.
|
||||
pub fn offset(&self) -> i32 {
|
||||
match *self {
|
||||
Side::Left => 0,
|
||||
Side::Right => 32,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A character, enemy or controllable.
|
||||
pub struct Character {
|
||||
|
||||
/// The position of the character.
|
||||
position: Vector2<f32>,
|
||||
pub position: Vector2<f32>,
|
||||
|
||||
/// The speed of the character.
|
||||
speed: Vector2<f32>,
|
||||
pub speed: Vector2<f32>,
|
||||
|
||||
/// If the player is controlling a character.
|
||||
controls: Option<Controls>,
|
||||
|
||||
/// The side of the character.
|
||||
side: Side,
|
||||
|
||||
/// The counter of jumps.
|
||||
///
|
||||
/// When it's 0, the character can no longer jump.
|
||||
jump_counter: usize,
|
||||
|
||||
/// The maximum number of jumps a character can do.
|
||||
///
|
||||
/// It's reset when the character hits the ground.
|
||||
max_jump: usize,
|
||||
|
||||
/// The timer of the character's animation.
|
||||
animation_timer: Option<Instant>,
|
||||
}
|
||||
|
||||
impl Character {
|
||||
/// Creates a character in (0, 0).
|
||||
pub fn new() -> Character {
|
||||
|
||||
fn generic(controls: Option<Controls>) -> Character {
|
||||
Character {
|
||||
position: Vector2::new(0.0, 0.0),
|
||||
speed: Vector2::new(0.0, 0.0),
|
||||
controls: None,
|
||||
controls: controls,
|
||||
side: Side::Right,
|
||||
jump_counter: 1,
|
||||
max_jump: 1,
|
||||
animation_timer: None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates a character in (0, 0).
|
||||
pub fn new() -> Character {
|
||||
Character::generic(None)
|
||||
}
|
||||
|
||||
/// Creates a character with the specified controls.
|
||||
pub fn with_controls(controls: Controls) -> Character {
|
||||
Character {
|
||||
position: Vector2::new(0.0, 0.0),
|
||||
speed: Vector2::new(0.0, 0.0),
|
||||
controls: Some(controls),
|
||||
}
|
||||
Character::generic(Some(controls))
|
||||
}
|
||||
|
||||
/// Sets the position of the character.
|
||||
pub fn set_position<S: Into<Vector2<f32>>>(&mut self, position: S) {
|
||||
self.position = position.into();
|
||||
}
|
||||
|
||||
/// Makes the character jump.
|
||||
pub fn jump(&mut self) {
|
||||
if self.jump_counter > 0 {
|
||||
self.jump_counter -= 1;
|
||||
self.speed.y = physics::JUMP_SPEED.y;
|
||||
}
|
||||
}
|
||||
|
||||
/// Resets the jump counter.
|
||||
pub fn ground_collision(&mut self) {
|
||||
self.jump_counter = self.max_jump;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
impl Updatable for Character {
|
||||
fn update(&mut self, duration: &Duration) {
|
||||
|
||||
let mut force: Vector2<f32> = Vector2::new(0.0, 0.0);
|
||||
|
||||
// Manage the input of the player
|
||||
if Key::Left.is_pressed() {
|
||||
force.x -= 1.0;
|
||||
}
|
||||
|
||||
if Key::Right.is_pressed() {
|
||||
force.x += 1.0;
|
||||
}
|
||||
|
||||
if let Some(side) = Side::from_force(force) {
|
||||
|
||||
self.side = side;
|
||||
|
||||
if self.animation_timer.is_none() {
|
||||
self.animation_timer = Some(Instant::now());
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
self.animation_timer = None;
|
||||
|
||||
}
|
||||
|
||||
|
||||
let duration = duration_as_f32(duration);
|
||||
|
||||
// Compute acceleration
|
||||
let accel = physics::G;
|
||||
|
||||
// Compute speed
|
||||
self.speed.x *= physics::GROUND_FRICTION.x;
|
||||
self.speed += accel * duration + force * 64.0;
|
||||
|
||||
// Compute position
|
||||
self.position += self.speed * duration;
|
||||
|
||||
}
|
||||
|
||||
fn manage_event(&mut self, event: &Event) {
|
||||
match event {
|
||||
|
||||
Event::KeyPressed {
|
||||
code: Key::Return, ..
|
||||
} => self.jump(),
|
||||
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Drawable for Character {
|
||||
fn draw<'a: 'shader, 'texture, 'shader, 'shader_texture>(
|
||||
&'a self,
|
||||
target: &mut RenderTarget,
|
||||
states: RenderStates<'texture, 'shader, 'shader_texture>
|
||||
) {
|
||||
fn texture(&self) -> Texture {
|
||||
Texture::Mario
|
||||
}
|
||||
|
||||
use sfml::graphics::Transformable;
|
||||
let mut sprite = Sprite::new();
|
||||
sprite.set_origin((32.0, 0.0));
|
||||
sprite.set_position(self.position);
|
||||
sprite.set_color(&if self.controls.is_some() {
|
||||
Color::rgb(255, 0, 0)
|
||||
fn texture_rect(&self) -> IntRect {
|
||||
let frame = if let Some(started) = self.animation_timer {
|
||||
duration_as_frame(&Instant::now().duration_since(started), 2)
|
||||
} else {
|
||||
Color::rgb(255, 255, 255)
|
||||
});
|
||||
sprite.draw(target, states);
|
||||
0
|
||||
};
|
||||
|
||||
IntRect::new(self.side.offset(), frame * 32, 32, 32)
|
||||
}
|
||||
|
||||
fn position(&self) -> Vector2<f32> {
|
||||
self.position
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user