Starting to do physics
This commit is contained in:
		
							parent
							
								
									4da27c5777
								
							
						
					
					
						commit
						3e8ae2d496
					
				
							
								
								
									
										
											BIN
										
									
								
								assets/textures/mario.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								assets/textures/mario.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 2.5 KiB | 
| @ -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 | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -1,8 +1,22 @@ | ||||
| use std::time::Duration; | ||||
| 
 | ||||
| use std::ops::{ | ||||
|     Index, | ||||
|     IndexMut | ||||
| }; | ||||
| 
 | ||||
| /// Converts a duration into an animation frame number.
 | ||||
| pub fn duration_as_frame(duration: &Duration, total: usize) -> i32 { | ||||
|     let secs = duration_as_f32(duration); | ||||
| 
 | ||||
|     (secs * 4.0) as i32 % total as i32 | ||||
| } | ||||
| 
 | ||||
| /// Converts a duration into its number of seconds.
 | ||||
| pub fn duration_as_f32(duration: &Duration) -> f32 { | ||||
|     duration.as_secs() as f32 + duration.subsec_nanos() as f32 / 1_000_000_000.0 | ||||
| } | ||||
| 
 | ||||
| /// A generic matrix type, useful for levels.
 | ||||
| pub struct Matrix<T> { | ||||
| 
 | ||||
|  | ||||
| @ -13,3 +13,9 @@ pub mod texture; | ||||
| 
 | ||||
| /// This module contains the math tools that will be used.
 | ||||
| pub mod math; | ||||
| 
 | ||||
| /// This module contains our wrapper of SFML window.
 | ||||
| pub mod renderer; | ||||
| 
 | ||||
| /// This module contains everything related to physics.
 | ||||
| pub mod physics; | ||||
|  | ||||
							
								
								
									
										10
									
								
								src/engine/physics/mod.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								src/engine/physics/mod.rs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,10 @@ | ||||
| use sfml::system::Vector2; | ||||
| 
 | ||||
| /// The gravity force.
 | ||||
| pub const G: Vector2<f32> = Vector2 { x: 0.0, y: 50.0 * 32.0 }; | ||||
| 
 | ||||
| /// The friction with the ground.
 | ||||
| pub const GROUND_FRICTION: Vector2<f32> = Vector2 { x: 0.5, y: 1.0 }; | ||||
| 
 | ||||
| /// The speed of a jump.
 | ||||
| pub const JUMP_SPEED: Vector2<f32> = Vector2 { x: 0.0, y: -600.0 }; | ||||
							
								
								
									
										110
									
								
								src/engine/renderer/mod.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										110
									
								
								src/engine/renderer/mod.rs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,110 @@ | ||||
| use std::time::Instant; | ||||
| 
 | ||||
| use sfml::graphics::{ | ||||
|     RenderWindow, | ||||
|     RenderTarget, | ||||
|     Sprite, | ||||
|     Color, | ||||
|     IntRect, | ||||
| }; | ||||
| 
 | ||||
| use sfml::window::{ | ||||
|     Style, | ||||
|     Event, | ||||
| }; | ||||
| 
 | ||||
| use sfml::system::{ | ||||
|     Vector2, | ||||
| }; | ||||
| 
 | ||||
| use engine::texture::{Texture, TextureManager}; | ||||
| use engine::scene::Scene; | ||||
| 
 | ||||
| /// Our custom drawable trait.
 | ||||
| pub trait Drawable { | ||||
|     /// Returns the texture of the drawable.
 | ||||
|     fn texture(&self) -> Texture; | ||||
| 
 | ||||
|     /// Returns the coordinates to use on the texture.
 | ||||
|     fn texture_rect(&self) -> IntRect; | ||||
| 
 | ||||
|     /// Returns the position on which the drawable should be drawn.
 | ||||
|     fn position(&self) -> Vector2<f32>; | ||||
| } | ||||
| 
 | ||||
| /// The game window.
 | ||||
| pub struct Renderer { | ||||
| 
 | ||||
|     /// The window on which the rendering will be done.
 | ||||
|     window: RenderWindow, | ||||
| 
 | ||||
|     /// The texture manager needed by the renderer.
 | ||||
|     texture_manager: TextureManager, | ||||
| 
 | ||||
|     /// The global timer of the renderer.
 | ||||
|     started: Instant, | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| impl Renderer { | ||||
|     /// Creates a new renderer.
 | ||||
|     pub fn new(width: u32, height: u32) -> Renderer { | ||||
| 
 | ||||
|         let mut window = RenderWindow::new( | ||||
|             (width, height), | ||||
|             "Bomberman", | ||||
|             Style::CLOSE, | ||||
|             &Default::default(), | ||||
|         ); | ||||
|         window.set_vertical_sync_enabled(true); | ||||
|         window.set_framerate_limit(60); | ||||
| 
 | ||||
|         Renderer { | ||||
|             window: window, | ||||
|             texture_manager: TextureManager::new(), | ||||
|             started: Instant::now(), | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     /// Returns the animation state of the renderer, between 0 and 3.
 | ||||
|     pub fn animation_state(&self) -> i32 { | ||||
|         let duration = Instant::now().duration_since(self.started); | ||||
|         let ns = 1_000_000_000 * duration.as_secs() + duration.subsec_nanos() as u64; | ||||
|         let result = (ns as usize / 250_000_000) % 4; | ||||
|         result as i32 | ||||
|     } | ||||
| 
 | ||||
|     /// Clears the window.
 | ||||
|     pub fn clear(&mut self) { | ||||
|         self.window.clear(&Color::rgb(0, 0, 0)); | ||||
|     } | ||||
| 
 | ||||
|     /// Draws a drawable.
 | ||||
|     pub fn draw<D: Drawable>(&mut self, drawable: &D) { | ||||
|         let texture = self.texture_manager.get(drawable.texture()); | ||||
|         let mut sprite = Sprite::with_texture(&texture); | ||||
|         sprite.set_texture_rect(&drawable.texture_rect()); | ||||
| 
 | ||||
|         use sfml::graphics::Transformable; | ||||
|         sprite.set_position(drawable.position()); | ||||
| 
 | ||||
|         self.window.draw(&sprite); | ||||
|     } | ||||
| 
 | ||||
|     /// Draws a scene on the window.
 | ||||
|     pub fn draw_scene(&mut self, scene: &Scene) { | ||||
|         for c in scene.characters() { | ||||
|             self.draw(c); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     /// Triggers the display.
 | ||||
|     pub fn display(&mut self) { | ||||
|         self.window.display(); | ||||
|     } | ||||
| 
 | ||||
|     /// Check if there are new events, returns the top of the stack.
 | ||||
|     pub fn poll_event(&mut self) -> Option<Event> { | ||||
|         self.window.poll_event() | ||||
|     } | ||||
| } | ||||
| @ -1,11 +1,6 @@ | ||||
| use std::time::Duration; | ||||
| 
 | ||||
| use sfml::window::Event; | ||||
| use sfml::graphics::{ | ||||
|     RenderTarget, | ||||
|     RenderStates, | ||||
|     Drawable, | ||||
| }; | ||||
| 
 | ||||
| use engine::character::Character; | ||||
| 
 | ||||
| @ -14,6 +9,7 @@ pub struct Scene { | ||||
| 
 | ||||
|     /// The characters contained in the scene.
 | ||||
|     characters: Vec<Character>, | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| impl Scene { | ||||
| @ -32,8 +28,15 @@ impl Scene { | ||||
| 
 | ||||
|     /// Updates the whole scene.
 | ||||
|     pub fn update(&mut self, duration: &Duration) { | ||||
| 
 | ||||
|         for c in &mut self.characters { | ||||
|             c.update(duration); | ||||
| 
 | ||||
|             if c.position.y > 500.0 { | ||||
|                 c.position.y = 500.0; | ||||
|                 c.speed.y = 0.0; | ||||
|                 c.ground_collision(); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
| @ -43,22 +46,12 @@ impl Scene { | ||||
|             c.manage_event(event); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl Drawable for Scene { | ||||
|     fn draw<'a: 'shader, 'texture, 'shader, 'shader_texture>( | ||||
|         &'a self, | ||||
|         target: &mut RenderTarget, | ||||
|         states: RenderStates<'texture, 'shader, 'shader_texture> | ||||
|     ) { | ||||
|     /// Returns a reference to the characters of the scene.
 | ||||
|     pub fn characters(&self) -> &Vec<Character> { | ||||
|         &self.characters | ||||
|     } | ||||
| 
 | ||||
|         for c in &self.characters { | ||||
|             let mut s = RenderStates::default(); | ||||
|             s.transform = states.transform.clone(); | ||||
|             s.blend_mode = states.blend_mode; | ||||
|             c.draw(target, s); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| /// Trait that needs to be implemented for everything that can be updatable.
 | ||||
|  | ||||
| @ -57,12 +57,13 @@ macro_rules! make_textures { | ||||
| } | ||||
| 
 | ||||
| make_textures!( | ||||
|     Mario, mario, make_mario_texture, "../../../assets/textures/mario.png", | ||||
| ); | ||||
| 
 | ||||
| impl TextureManager { | ||||
|     /// Creates a textures from an array of bytes.
 | ||||
|     fn make_texture_from_bytes(bytes: Vec<u8>) -> SfTexture { | ||||
|         SfTexture::from_memory(&bytes, &IntRect::new(0, 0, 500, 500)) | ||||
|         SfTexture::from_memory(&bytes, &IntRect::new(0, 0, 256, 256)) | ||||
|             .expect("Failed to create texture") | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -7,21 +7,14 @@ use std::time::{ | ||||
| }; | ||||
| 
 | ||||
| use sfml::window::{ | ||||
|     ContextSettings, | ||||
|     Style, | ||||
|     Event, | ||||
|     Key, | ||||
| }; | ||||
| 
 | ||||
| use sfml::graphics::{ | ||||
|     RenderWindow, | ||||
|     RenderTarget, | ||||
|     Color, | ||||
| }; | ||||
| 
 | ||||
| use rusty::engine::scene::Scene; | ||||
| use rusty::engine::character::Character; | ||||
| use rusty::engine::controls::Controls; | ||||
| use rusty::engine::renderer::Renderer; | ||||
| 
 | ||||
| fn main() { | ||||
|     let game_width = 800; | ||||
| @ -32,27 +25,16 @@ fn main() { | ||||
| 
 | ||||
|     let mut scene = Scene::new(); | ||||
|     scene.add(character); | ||||
|     scene.add(Character::new()); | ||||
| 
 | ||||
|     let context_settings = ContextSettings { | ||||
|         ..Default::default() | ||||
|     }; | ||||
| 
 | ||||
|     let mut window = RenderWindow::new( | ||||
|         (game_width, game_height), | ||||
|         "Free Rusty Maker", | ||||
|         Style::CLOSE, | ||||
|         &context_settings, | ||||
|     ); | ||||
| 
 | ||||
|     window.set_framerate_limit(60); | ||||
|     let mut renderer = Renderer::new(game_width, game_height); | ||||
| 
 | ||||
|     let mut after_loop = None; | ||||
|     let mut running = true; | ||||
| 
 | ||||
|     loop { | ||||
| 
 | ||||
|         // Manage the events
 | ||||
|         while let Some(event) = window.poll_event() { | ||||
|         while let Some(event) = renderer.poll_event() { | ||||
| 
 | ||||
|             match event { | ||||
|                 // Quit the game if window is closed
 | ||||
| @ -71,15 +53,23 @@ fn main() { | ||||
|         } | ||||
| 
 | ||||
|         // Manage the physics
 | ||||
|         // ...
 | ||||
|         let duration = if let Some(instant) = after_loop { | ||||
|             Instant::now().duration_since(instant) | ||||
|         } else { | ||||
|             Duration::from_millis(20) | ||||
|         }; | ||||
| 
 | ||||
|         after_loop = Some(Instant::now()); | ||||
| 
 | ||||
|         scene.update(&duration); | ||||
| 
 | ||||
|         // Do the rendering
 | ||||
|         window.clear(&Color::rgb(50, 200, 50)); | ||||
|         window.draw(&scene); | ||||
|         renderer.clear(); | ||||
|         renderer.draw_scene(&scene); | ||||
| 
 | ||||
|         // Display and manage the frame rate
 | ||||
|         window.display(); | ||||
|         renderer.display(); | ||||
| 
 | ||||
| 
 | ||||
|         if ! running { | ||||
|             break; | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user