From 4d49dd44ba10d99cd115b8eda123716e1ab8037b Mon Sep 17 00:00:00 2001 From: Dietrich Date: Thu, 11 Aug 2022 17:14:42 +0200 Subject: [PATCH] working animation --- src/turtle.rs | 196 +++++++++++++++++++++++++++++------------ src/turtle_movement.rs | 34 ++++--- 2 files changed, 154 insertions(+), 76 deletions(-) diff --git a/src/turtle.rs b/src/turtle.rs index 877ed33..8980593 100644 --- a/src/turtle.rs +++ b/src/turtle.rs @@ -2,13 +2,13 @@ use std::time::Duration; use bevy::prelude::*; use bevy_inspector_egui::{Inspectable, RegisterInspectable}; -use bevy_prototype_lyon::{prelude::*, shapes::Line}; +use bevy_prototype_lyon::{entity::ShapeBundle, prelude::*, shapes::Line}; use bevy_tweening::{ - lens::TransformPositionLens, Animator, EaseFunction, Sequence, Tween, TweenCompleted, - TweeningPlugin, TweeningType, + component_animator_system, lens::TransformPositionLens, Animator, EaseFunction, Lens, Tween, + TweenCompleted, TweeningPlugin, TweeningType, }; -use crate::{turtle_movement::turtle_move, turtle_shapes}; +use crate::turtle_shapes; pub struct TurtlePlugin; @@ -18,6 +18,7 @@ impl Plugin for TurtlePlugin { .add_startup_system(setup) .add_system(keypresses) .add_system(draw_lines) + .add_system(component_animator_system::) .register_inspectable::() .register_inspectable::(); } @@ -65,7 +66,7 @@ impl Default for Turtle { } impl Turtle { - pub fn set_color(&mut self, color: Color) { + /* pub fn set_color(&mut self, color: Color) { self.colors.color = color; } pub fn set_fill_color(&mut self, color: Color) { @@ -79,13 +80,20 @@ impl Turtle { .commands .push(TurtleCommand::Forward(Length(100.0))); self - } + } */ } #[derive(Component, Inspectable)] pub struct TurtleCommands { commands: Vec, lines: Vec, + state: TurtleState, +} +#[derive(Inspectable)] +pub struct TurtleState { + pub start: Vec2, + pub heading: f32, + pub index: u64, } impl TurtleCommands { @@ -93,49 +101,44 @@ impl TurtleCommands { Self { commands, lines: vec![], + state: TurtleState { + start: Vec2::ZERO, + heading: 0f32.to_radians(), + index: 0, + }, } } } impl TurtleCommands { - fn generate_tweenable(&mut self) -> Sequence { - self.lines.clear(); - let mut seq = Sequence::with_capacity(self.commands.len()); - let mut pos = Vec2::ZERO; - let mut ori: f32 = 0.; - for (index, op) in self.commands.iter().enumerate() { - match op { + fn get_next(&mut self) -> Option<(Tween, TurtleGraphElement)> { + let index = self.state.index; + let next_index = index + 1; + + if let Some(command) = self.commands.get(self.state.index as usize) { + let res = match command { TurtleCommand::Forward(Length(x)) => { - let (target, animation, line) = turtle_move(pos, ori, *x as f32, index as f32); - self.lines.push(line); - seq = seq.then(animation); - pos = target; + crate::turtle_movement::turtle_move(&mut self.state, *x as f32) } TurtleCommand::Backward(Length(x)) => { - let (target, animation, line) = turtle_move(pos, ori, -*x as f32, index as f32); - self.lines.push(line); - seq = seq.then(animation); - pos = target; + crate::turtle_movement::turtle_move(&mut self.state, -*x as f32) } TurtleCommand::Left(Angle(x)) => { - let (nori, tween, line) = crate::turtle_movement::turtle_turn(ori, *x as f32); - ori = nori; - seq = seq.then(tween); - self.lines.push(line); + crate::turtle_movement::turtle_turn(&mut self.state, *x as f32) } TurtleCommand::Right(Angle(x)) => { - let (nori, tween, line) = crate::turtle_movement::turtle_turn(ori, -*x as f32); - ori = nori; - seq = seq.then(tween); - self.lines.push(line); + crate::turtle_movement::turtle_turn(&mut self.state, -*x as f32) } TurtleCommand::PenUp => todo!(), TurtleCommand::PenDown => todo!(), TurtleCommand::Circle => todo!(), TurtleCommand::Pause => todo!(), - } + }; + self.state.index = next_index; + Some(res) + } else { + None } - seq } } @@ -184,7 +187,7 @@ fn setup(mut commands: Commands) { TweeningType::PingPong, // Animation time (one way only; for ping-pong it takes 2 seconds // to come back to start). - Duration::from_secs(1), + Duration::from_millis(500), // The lens gives access to the Transform component of the Entity, // for the Animator to animate it. It also contains the start and // end values respectively associated with the progress ratios 0. and 1. @@ -208,42 +211,121 @@ fn setup(mut commands: Commands) { .insert(TurtleShape); } -fn draw_lines( - mut commands: Commands, - tcmd: Query<&TurtleCommands>, - mut query_event: EventReader, // TODO: howto attach only to the right event? -) { - for ev in query_event.iter() { - let index = ev.user_data; +struct MyCustomLens { + start: Vec2, + end: Vec2, +} - for t in tcmd.iter() { - let t = t.lines.get(index as usize).unwrap(); - match t { - TurtleGraphElement::TurtleLine { start, end } => { - commands.spawn_bundle(GeometryBuilder::build_as( - &Line(*start, *end), - DrawMode::Outlined { - fill_mode: FillMode::color(Color::MIDNIGHT_BLUE), - outline_mode: StrokeMode::new(Color::BLACK, 1.0), - }, - Transform::identity(), - )); - } - TurtleGraphElement::Noop => (), - } +impl Lens for MyCustomLens { + fn lerp(&mut self, target: &mut Path, ratio: f32) { + let line = shapes::Line(self.start, self.start + ((self.end - self.start) * ratio)); + *target = ShapePath::build_as(&line); + } +} + +#[derive(Bundle)] +struct TurtleDrawLine { + #[bundle] + line: ShapeBundle, + name: Name, + marker: LineMarker, +} + +#[derive(Component, Default)] +struct LineMarker; + +impl TurtleDrawLine { + fn new(start: Vec2, _end: Vec2, index: u64) -> Self { + Self { + line: GeometryBuilder::build_as( + &Line(start, start), + DrawMode::Outlined { + fill_mode: FillMode::color(Color::MIDNIGHT_BLUE), + outline_mode: StrokeMode::new(Color::BLACK, 1.0), + }, + Transform::identity(), + ), + name: Name::new(format!("Line {}", index)), + marker: LineMarker, } } } +fn draw_lines( + mut commands: Commands, + mut tcmd: Query<&mut TurtleCommands>, + mut turtle: Query<&mut Animator, With>, + mut query_event: EventReader, // TODO: howto attach only to the right event? +) { + for _ev in query_event.iter() { + let mut tcmd = tcmd.single_mut(); + if let Some((turtle_animation, graph_element_to_draw)) = tcmd.get_next() { + let mut turtle = turtle.single_mut(); + turtle.set_tweenable(turtle_animation); + match graph_element_to_draw { + TurtleGraphElement::TurtleLine { start, end } => { + let line_animator = Animator::new(Tween::new( + // Use a quadratic easing on both endpoints. + EaseFunction::QuadraticInOut, + // Loop animation back and forth. + TweeningType::Once, + // Animation time (one way only; for ping-pong it takes 2 seconds + // to come back to start). + Duration::from_millis(500), + // The lens gives access to the Transform component of the Entity, + // for the Animator to animate it. It also contains the start and + // end values respectively associated with the progress ratios 0. and 1. + MyCustomLens { start, end }, + )); + commands + .spawn_bundle(TurtleDrawLine::new(start, end, tcmd.state.index)) + .insert(line_animator); + } + TurtleGraphElement::Noop => println!("No drawing!"), + } + } else { + println!("nothing to draw") + }; + } +} + fn keypresses( + mut commands: Commands, keys: Res>, mut qry: Query<&mut Animator, With>, mut tcmd: Query<&mut TurtleCommands>, ) { if keys.just_pressed(KeyCode::W) { let mut tcmd = tcmd.single_mut(); - let c = tcmd.generate_tweenable(); - let mut shap = qry.single_mut(); - shap.set_tweenable(c); + tcmd.state = TurtleState { + start: Vec2::ZERO, + heading: 0., + index: 0, + }; + if let Some((turtle_animation, graph_element_to_draw)) = tcmd.get_next() { + let mut shap = qry.single_mut(); + shap.set_tweenable(turtle_animation); + match graph_element_to_draw { + TurtleGraphElement::TurtleLine { start, end } => { + let line_animator = Animator::new(Tween::new( + // Use a quadratic easing on both endpoints. + EaseFunction::QuadraticInOut, + // Loop animation back and forth. + TweeningType::Once, + // Animation time (one way only; for ping-pong it takes 2 seconds + // to come back to start). + Duration::from_millis(500), + // The lens gives access to the Transform component of the Entity, + // for the Animator to animate it. It also contains the start and + // end values respectively associated with the progress ratios 0. and 1. + MyCustomLens { start, end }, + )); + commands + .spawn_bundle(TurtleDrawLine::new(start, end, tcmd.state.index)) + .insert(line_animator); + } + TurtleGraphElement::Noop => (), + } + }; } } diff --git a/src/turtle_movement.rs b/src/turtle_movement.rs index f867590..d061806 100644 --- a/src/turtle_movement.rs +++ b/src/turtle_movement.rs @@ -6,14 +6,14 @@ use bevy_tweening::{ EaseFunction, Tween, TweeningType, }; -use crate::turtle::TurtleGraphElement; +use crate::turtle::{TurtleGraphElement, TurtleState}; pub fn turtle_turn( - orientation: f32, + state: &mut TurtleState, angle_to_turn: f32, -) -> (f32, Tween, TurtleGraphElement) { - let start = orientation; - let end = orientation + (angle_to_turn * PI / 180.); +) -> (Tween, TurtleGraphElement) { + let start = state.heading; + let end = state.heading + (angle_to_turn * PI / 180.); let animation = Tween::new( // Use a quadratic easing on both endpoints. EaseFunction::QuadraticInOut, @@ -26,20 +26,16 @@ pub fn turtle_turn( // for the Animator to animate it. It also contains the start and // end values respectively associated with the progress ratios 0. and 1. TransformRotateZLens { start, end }, - ); + ) + .with_completed_event(state.index as u64); let line = TurtleGraphElement::Noop; - let orientation = end % (2. * PI); - (orientation, animation, line) + state.heading = end % (2. * PI); + (animation, line) } -pub fn turtle_move( - position: Vec2, - orientation: f32, - length: f32, - index: f32, -) -> (Vec2, Tween, TurtleGraphElement) { - let start = position; - let end = position + (Vec2::from_angle(orientation) * length); +pub fn turtle_move(state: &mut TurtleState, length: f32) -> (Tween, TurtleGraphElement) { + let start = state.start; + let end = state.start + (Vec2::from_angle(state.heading) * length); let turtle_movement_animation = Tween::new( // accelerate and decelerate EaseFunction::QuadraticInOut, @@ -53,8 +49,8 @@ pub fn turtle_move( end: end.extend(0.), }, ) - .with_completed_event(index as u64); + .with_completed_event(state.index as u64); let line = TurtleGraphElement::TurtleLine { start, end }; - - (end, turtle_movement_animation, line) + state.start = end; + (turtle_movement_animation, line) }