working animation

This commit is contained in:
Dietrich 2022-08-11 17:14:42 +02:00
parent 1d1787b8c0
commit 4d49dd44ba
Signed by: dietrich
GPG Key ID: F0CE5A20AB5C4B27
2 changed files with 154 additions and 76 deletions

View File

@ -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::<Path>)
.register_inspectable::<Colors>()
.register_inspectable::<TurtleCommands>();
}
@ -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<TurtleCommand>,
lines: Vec<TurtleGraphElement>,
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<Transform> {
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<Transform>, 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<TweenCompleted>, // 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<Path> 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<Transform>, With<TurtleShape>>,
mut query_event: EventReader<TweenCompleted>, // 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<Input<KeyCode>>,
mut qry: Query<&mut Animator<Transform>, With<TurtleShape>>,
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 => (),
}
};
}
}

View File

@ -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<Transform>, TurtleGraphElement) {
let start = orientation;
let end = orientation + (angle_to_turn * PI / 180.);
) -> (Tween<Transform>, 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<Transform>, TurtleGraphElement) {
let start = position;
let end = position + (Vec2::from_angle(orientation) * length);
pub fn turtle_move(state: &mut TurtleState, length: f32) -> (Tween<Transform>, 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)
}