specs-physics

[Fork] Integration of Amethyst, SPECS and Nphysics together.
git clone https://git.jojolepro.com/specs-physics.git
Log | Files | Refs | README | LICENSE

commit 3de1eca78baa0d9f3434c482aa75b5055af5a5b9
parent c1fcfed98c47602cf4266188fcf36b0635ca97be
Author: Joël Lupien <jojolepromain@gmail.com>
Date:   Mon, 11 Feb 2019 04:34:58 -0500

Timestep debug info (#19)

* Better error message for physics stepper timestep change.

* More debug messages for physics stepper.

* Added a way to change the timestep at runtime. Added reset and timestep change keybinds for the amethyst example.

Diffstat:
Mexamples/amethyst.rs | 55+++++++++++++++++++++++++++++++++++++++++++++----------
Msrc/systems/mod.rs | 12+-----------
Msrc/systems/physics_stepper.rs | 17++++++++---------
3 files changed, 54 insertions(+), 30 deletions(-)

diff --git a/examples/amethyst.rs b/examples/amethyst.rs @@ -1,13 +1,15 @@ use amethyst::assets::{Handle, Loader}; use amethyst::core::nalgebra::{Matrix3, Vector3}; use amethyst::core::specs::world::Builder; +use amethyst::core::specs::Join; use amethyst::core::{GlobalTransform, Transform, TransformBundle}; use amethyst::renderer::{ AmbientColor, Camera, DisplayConfig, DrawShaded, Light, Material, MaterialDefaults, MeshData, MeshHandle, Pipeline, PointLight, PosNormTex, RenderBundle, Rgba, ScreenDimensions, Shape, - Stage, Texture, + Stage, Texture, VirtualKeyCode, }; -use amethyst::{Application, GameData, GameDataBuilder, SimpleState, StateData}; +use amethyst::input::{is_close_requested, is_key_down}; +use amethyst::{Application, GameData, GameDataBuilder, SimpleState, StateData, StateEvent, SimpleTrans, Trans}; use nphysics_ecs_dumb::ncollide::shape::{Ball, ShapeHandle}; use nphysics_ecs_dumb::nphysics::math::Velocity; use nphysics_ecs_dumb::nphysics::object::Material as PhysicsMaterial; @@ -45,7 +47,7 @@ impl SimpleState for GameState { (resolution.width(), resolution.height()) }; - let camera_transform = Transform::from(Vector3::new(0.0, 0.0, 0.0)); + let camera_transform = Transform::from(Vector3::new(0.0, 5.0, 5.0)); // Add Camera data.world @@ -81,7 +83,7 @@ impl SimpleState for GameState { .create_entity() .with(sphere_handle.clone()) .with(material.clone()) - .with(Transform::from(Vector3::new(0.0, 3.0, -10.0))) + .with(Transform::from(Vector3::new(0.0, 15.0, -10.0))) .with(GlobalTransform::default()) .with(DynamicBody::new_rigidbody_with_velocity( Velocity::linear(0.0, 1.0, 0.0), @@ -148,6 +150,45 @@ impl SimpleState for GameState { material.clone(), );*/ } + + fn handle_event( + &mut self, + data: StateData<'_, GameData<'_, '_>>, + event: StateEvent, + ) -> SimpleTrans { + if let StateEvent::Window(event) = &event { + // Exit if user hits Escape or closes the window + if is_close_requested(&event) || is_key_down(&event, VirtualKeyCode::Escape) { + return Trans::Quit; + } + + // + if is_key_down(&event, VirtualKeyCode::T) { + *data.world.write_resource::<TimeStep>() = TimeStep::Fixed(1./120.); + println!("Setting timestep to 1./120."); + } + + if is_key_down(&event, VirtualKeyCode::Y) { + *data.world.write_resource::<TimeStep>() = TimeStep::Fixed(1./60.); + println!("Setting timestep to 1./60."); + } + + if is_key_down(&event, VirtualKeyCode::S) { + *data.world.write_resource::<TimeStep>() = TimeStep::SemiFixed(TimeStepConstraint::new( + vec![1. / 240., 1. / 120., 1. / 60.], + 0.4, + Duration::from_millis(50), + Duration::from_millis(500), + )) + } + + // Reset the example + if is_key_down(&event, VirtualKeyCode::Space) { + *(&mut data.world.write_storage::<Transform>(), &data.world.read_storage::<DynamicBody>()).join().next().unwrap().0.translation_mut() = Vector3::new(0.0, 15.0, -10.0); + } + } + Trans::None + } } fn main() -> amethyst::Result<()> { @@ -181,12 +222,6 @@ fn main() -> amethyst::Result<()> { .with_bundle( PhysicsBundle::new() .with_dep(&["transform_system"]) - .with_timestep(TimeStep::SemiFixed(TimeStepConstraint::new( - vec![1. / 240., 1. / 120., 1. / 60.], - 0.4, - Duration::from_millis(50), - Duration::from_millis(500), - ))) .with_timestep_iter_limit(20), )? .with_bundle(RenderBundle::new(pipe, Some(display_config)))?; diff --git a/src/systems/mod.rs b/src/systems/mod.rs @@ -9,8 +9,6 @@ use amethyst::core::specs::DispatcherBuilder; use amethyst::error::Error; use core::result::Result; -use crate::time_step::TimeStep; - pub use self::physics_stepper::PhysicsStepperSystem; pub use self::sync_bodies_from_physics::*; pub use self::sync_bodies_to_physics::SyncBodiesToPhysicsSystem; @@ -25,7 +23,6 @@ pub const SYNC_BODIES_FROM_PHYSICS_SYSTEM: &str = "sync_bodies_from_physics_syst pub struct PhysicsBundle<'a> { dep: &'a [&'a str], - timestep: TimeStep, timestep_iter_limit: i32, } @@ -33,7 +30,6 @@ impl Default for PhysicsBundle<'_> { fn default() -> Self { Self { dep: Default::default(), - timestep: Default::default(), timestep_iter_limit: 10, } } @@ -49,12 +45,6 @@ impl<'a> PhysicsBundle<'a> { self } - /// Set the timestep to use for the `PhysicsStepperSystem` - pub fn with_timestep(mut self, timestep: TimeStep) -> Self { - self.timestep = timestep; - self - } - /// Set the maximum number of physics timesteps to simulate in a single run of the `PhysicsStepperSystem` pub fn with_timestep_iter_limit(mut self, timestep_iter_limit: i32) -> Self { self.timestep_iter_limit = timestep_iter_limit; @@ -82,7 +72,7 @@ impl<'a, 'b, 'c> SystemBundle<'a, 'b> for PhysicsBundle<'c> { ); builder.add( - PhysicsStepperSystem::new(self.timestep, self.timestep_iter_limit), + PhysicsStepperSystem::new(self.timestep_iter_limit), PHYSICS_STEPPER_SYSTEM, &[ SYNC_BODIES_TO_PHYSICS_SYSTEM, diff --git a/src/systems/physics_stepper.rs b/src/systems/physics_stepper.rs @@ -1,7 +1,7 @@ use crate::time_step::TimeStep; use crate::PhysicsWorld; use amethyst::core::Time; -use amethyst::ecs::{Read, System, WriteExpect}; +use amethyst::ecs::{Read, System, WriteExpect, Write}; use std::f32::EPSILON; use std::time::Instant; @@ -13,7 +13,6 @@ const TIME_STEP_DECREASE_HYSTERESIS: f32 = 1.5; /// Simulates a step of the physics world. pub struct PhysicsStepperSystem { - intended_timestep: TimeStep, timestep_iter_limit: i32, time_accumulator: f32, avg_step_time: Option<f32>, @@ -22,7 +21,6 @@ pub struct PhysicsStepperSystem { impl Default for PhysicsStepperSystem { fn default() -> Self { PhysicsStepperSystem { - intended_timestep: TimeStep::Fixed(1. / 120.), timestep_iter_limit: 10, time_accumulator: 0., avg_step_time: None, @@ -31,9 +29,8 @@ impl Default for PhysicsStepperSystem { } impl PhysicsStepperSystem { - pub fn new(intended_timestep: TimeStep, timestep_iter_limit: i32) -> Self { + pub fn new(timestep_iter_limit: i32) -> Self { PhysicsStepperSystem { - intended_timestep, timestep_iter_limit, time_accumulator: 0., avg_step_time: None, @@ -42,11 +39,11 @@ impl PhysicsStepperSystem { } impl<'a> System<'a> for PhysicsStepperSystem { - type SystemData = (WriteExpect<'a, PhysicsWorld>, Read<'a, Time>); + type SystemData = (WriteExpect<'a, PhysicsWorld>, Read<'a, Time>, Write<'a, TimeStep>); // Simulate world using the current time frame - fn run(&mut self, (mut physical_world, time): Self::SystemData) { - let (timestep, mut change_timestep) = match &mut self.intended_timestep { + fn run(&mut self, (mut physical_world, time, mut intended_timestep): Self::SystemData) { + let (timestep, mut change_timestep) = match &mut *intended_timestep { TimeStep::Fixed(timestep) => (*timestep, false), TimeStep::SemiFixed(constraint) => { let mut timestep = (constraint.current_timestep(), false); @@ -94,11 +91,12 @@ impl<'a> System<'a> for PhysicsStepperSystem { }; if (physical_world.timestep() - timestep).abs() > EPSILON && !change_timestep { - warn!("Physics world timestep out of sync with intended timestep! Leave me alone!!!"); + warn!("Physics world timestep out of sync with intended timestep! Physics timestep: {}, Requested timestep: {}", physical_world.timestep(), timestep); change_timestep = true; } if change_timestep { + trace!("Changing physics timestep to {}", timestep); // reset average when changing timestep self.avg_step_time = None; physical_world.set_timestep(timestep); @@ -110,6 +108,7 @@ impl<'a> System<'a> for PhysicsStepperSystem { while steps <= self.timestep_iter_limit && self.time_accumulator >= timestep { let physics_time = Instant::now(); + trace!("Stepping physics system. Step: {}, Timestep: {}, Time accumulator: {}", steps, timestep, self.time_accumulator); physical_world.step(); let physics_time = physics_time.elapsed();