working animation
This commit is contained in:
parent
1d1787b8c0
commit
4d49dd44ba
184
src/turtle.rs
184
src/turtle.rs
@ -2,13 +2,13 @@ use std::time::Duration;
|
|||||||
|
|
||||||
use bevy::prelude::*;
|
use bevy::prelude::*;
|
||||||
use bevy_inspector_egui::{Inspectable, RegisterInspectable};
|
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::{
|
use bevy_tweening::{
|
||||||
lens::TransformPositionLens, Animator, EaseFunction, Sequence, Tween, TweenCompleted,
|
component_animator_system, lens::TransformPositionLens, Animator, EaseFunction, Lens, Tween,
|
||||||
TweeningPlugin, TweeningType,
|
TweenCompleted, TweeningPlugin, TweeningType,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{turtle_movement::turtle_move, turtle_shapes};
|
use crate::turtle_shapes;
|
||||||
|
|
||||||
pub struct TurtlePlugin;
|
pub struct TurtlePlugin;
|
||||||
|
|
||||||
@ -18,6 +18,7 @@ impl Plugin for TurtlePlugin {
|
|||||||
.add_startup_system(setup)
|
.add_startup_system(setup)
|
||||||
.add_system(keypresses)
|
.add_system(keypresses)
|
||||||
.add_system(draw_lines)
|
.add_system(draw_lines)
|
||||||
|
.add_system(component_animator_system::<Path>)
|
||||||
.register_inspectable::<Colors>()
|
.register_inspectable::<Colors>()
|
||||||
.register_inspectable::<TurtleCommands>();
|
.register_inspectable::<TurtleCommands>();
|
||||||
}
|
}
|
||||||
@ -65,7 +66,7 @@ impl Default for Turtle {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Turtle {
|
impl Turtle {
|
||||||
pub fn set_color(&mut self, color: Color) {
|
/* pub fn set_color(&mut self, color: Color) {
|
||||||
self.colors.color = color;
|
self.colors.color = color;
|
||||||
}
|
}
|
||||||
pub fn set_fill_color(&mut self, color: Color) {
|
pub fn set_fill_color(&mut self, color: Color) {
|
||||||
@ -79,13 +80,20 @@ impl Turtle {
|
|||||||
.commands
|
.commands
|
||||||
.push(TurtleCommand::Forward(Length(100.0)));
|
.push(TurtleCommand::Forward(Length(100.0)));
|
||||||
self
|
self
|
||||||
}
|
} */
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Component, Inspectable)]
|
#[derive(Component, Inspectable)]
|
||||||
pub struct TurtleCommands {
|
pub struct TurtleCommands {
|
||||||
commands: Vec<TurtleCommand>,
|
commands: Vec<TurtleCommand>,
|
||||||
lines: Vec<TurtleGraphElement>,
|
lines: Vec<TurtleGraphElement>,
|
||||||
|
state: TurtleState,
|
||||||
|
}
|
||||||
|
#[derive(Inspectable)]
|
||||||
|
pub struct TurtleState {
|
||||||
|
pub start: Vec2,
|
||||||
|
pub heading: f32,
|
||||||
|
pub index: u64,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TurtleCommands {
|
impl TurtleCommands {
|
||||||
@ -93,50 +101,45 @@ impl TurtleCommands {
|
|||||||
Self {
|
Self {
|
||||||
commands,
|
commands,
|
||||||
lines: vec![],
|
lines: vec![],
|
||||||
|
state: TurtleState {
|
||||||
|
start: Vec2::ZERO,
|
||||||
|
heading: 0f32.to_radians(),
|
||||||
|
index: 0,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TurtleCommands {
|
impl TurtleCommands {
|
||||||
fn generate_tweenable(&mut self) -> Sequence<Transform> {
|
fn get_next(&mut self) -> Option<(Tween<Transform>, TurtleGraphElement)> {
|
||||||
self.lines.clear();
|
let index = self.state.index;
|
||||||
let mut seq = Sequence::with_capacity(self.commands.len());
|
let next_index = index + 1;
|
||||||
let mut pos = Vec2::ZERO;
|
|
||||||
let mut ori: f32 = 0.;
|
if let Some(command) = self.commands.get(self.state.index as usize) {
|
||||||
for (index, op) in self.commands.iter().enumerate() {
|
let res = match command {
|
||||||
match op {
|
|
||||||
TurtleCommand::Forward(Length(x)) => {
|
TurtleCommand::Forward(Length(x)) => {
|
||||||
let (target, animation, line) = turtle_move(pos, ori, *x as f32, index as f32);
|
crate::turtle_movement::turtle_move(&mut self.state, *x as f32)
|
||||||
self.lines.push(line);
|
|
||||||
seq = seq.then(animation);
|
|
||||||
pos = target;
|
|
||||||
}
|
}
|
||||||
TurtleCommand::Backward(Length(x)) => {
|
TurtleCommand::Backward(Length(x)) => {
|
||||||
let (target, animation, line) = turtle_move(pos, ori, -*x as f32, index as f32);
|
crate::turtle_movement::turtle_move(&mut self.state, -*x as f32)
|
||||||
self.lines.push(line);
|
|
||||||
seq = seq.then(animation);
|
|
||||||
pos = target;
|
|
||||||
}
|
}
|
||||||
TurtleCommand::Left(Angle(x)) => {
|
TurtleCommand::Left(Angle(x)) => {
|
||||||
let (nori, tween, line) = crate::turtle_movement::turtle_turn(ori, *x as f32);
|
crate::turtle_movement::turtle_turn(&mut self.state, *x as f32)
|
||||||
ori = nori;
|
|
||||||
seq = seq.then(tween);
|
|
||||||
self.lines.push(line);
|
|
||||||
}
|
}
|
||||||
TurtleCommand::Right(Angle(x)) => {
|
TurtleCommand::Right(Angle(x)) => {
|
||||||
let (nori, tween, line) = crate::turtle_movement::turtle_turn(ori, -*x as f32);
|
crate::turtle_movement::turtle_turn(&mut self.state, -*x as f32)
|
||||||
ori = nori;
|
|
||||||
seq = seq.then(tween);
|
|
||||||
self.lines.push(line);
|
|
||||||
}
|
}
|
||||||
TurtleCommand::PenUp => todo!(),
|
TurtleCommand::PenUp => todo!(),
|
||||||
TurtleCommand::PenDown => todo!(),
|
TurtleCommand::PenDown => todo!(),
|
||||||
TurtleCommand::Circle => todo!(),
|
TurtleCommand::Circle => todo!(),
|
||||||
TurtleCommand::Pause => todo!(),
|
TurtleCommand::Pause => todo!(),
|
||||||
|
};
|
||||||
|
self.state.index = next_index;
|
||||||
|
Some(res)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
seq
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Inspectable, Default)]
|
#[derive(Inspectable, Default)]
|
||||||
@ -184,7 +187,7 @@ fn setup(mut commands: Commands) {
|
|||||||
TweeningType::PingPong,
|
TweeningType::PingPong,
|
||||||
// Animation time (one way only; for ping-pong it takes 2 seconds
|
// Animation time (one way only; for ping-pong it takes 2 seconds
|
||||||
// to come back to start).
|
// to come back to start).
|
||||||
Duration::from_secs(1),
|
Duration::from_millis(500),
|
||||||
// The lens gives access to the Transform component of the Entity,
|
// The lens gives access to the Transform component of the Entity,
|
||||||
// for the Animator to animate it. It also contains the start and
|
// for the Animator to animate it. It also contains the start and
|
||||||
// end values respectively associated with the progress ratios 0. and 1.
|
// end values respectively associated with the progress ratios 0. and 1.
|
||||||
@ -208,42 +211,121 @@ fn setup(mut commands: Commands) {
|
|||||||
.insert(TurtleShape);
|
.insert(TurtleShape);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn draw_lines(
|
struct MyCustomLens {
|
||||||
mut commands: Commands,
|
start: Vec2,
|
||||||
tcmd: Query<&TurtleCommands>,
|
end: Vec2,
|
||||||
mut query_event: EventReader<TweenCompleted>, // TODO: howto attach only to the right event?
|
}
|
||||||
) {
|
|
||||||
for ev in query_event.iter() {
|
|
||||||
let index = ev.user_data;
|
|
||||||
|
|
||||||
for t in tcmd.iter() {
|
impl Lens<Path> for MyCustomLens {
|
||||||
let t = t.lines.get(index as usize).unwrap();
|
fn lerp(&mut self, target: &mut Path, ratio: f32) {
|
||||||
match t {
|
let line = shapes::Line(self.start, self.start + ((self.end - self.start) * ratio));
|
||||||
TurtleGraphElement::TurtleLine { start, end } => {
|
*target = ShapePath::build_as(&line);
|
||||||
commands.spawn_bundle(GeometryBuilder::build_as(
|
}
|
||||||
&Line(*start, *end),
|
}
|
||||||
|
|
||||||
|
#[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 {
|
DrawMode::Outlined {
|
||||||
fill_mode: FillMode::color(Color::MIDNIGHT_BLUE),
|
fill_mode: FillMode::color(Color::MIDNIGHT_BLUE),
|
||||||
outline_mode: StrokeMode::new(Color::BLACK, 1.0),
|
outline_mode: StrokeMode::new(Color::BLACK, 1.0),
|
||||||
},
|
},
|
||||||
Transform::identity(),
|
Transform::identity(),
|
||||||
));
|
),
|
||||||
}
|
name: Name::new(format!("Line {}", index)),
|
||||||
TurtleGraphElement::Noop => (),
|
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(
|
fn keypresses(
|
||||||
|
mut commands: Commands,
|
||||||
keys: Res<Input<KeyCode>>,
|
keys: Res<Input<KeyCode>>,
|
||||||
mut qry: Query<&mut Animator<Transform>, With<TurtleShape>>,
|
mut qry: Query<&mut Animator<Transform>, With<TurtleShape>>,
|
||||||
mut tcmd: Query<&mut TurtleCommands>,
|
mut tcmd: Query<&mut TurtleCommands>,
|
||||||
) {
|
) {
|
||||||
if keys.just_pressed(KeyCode::W) {
|
if keys.just_pressed(KeyCode::W) {
|
||||||
let mut tcmd = tcmd.single_mut();
|
let mut tcmd = tcmd.single_mut();
|
||||||
let c = tcmd.generate_tweenable();
|
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();
|
let mut shap = qry.single_mut();
|
||||||
shap.set_tweenable(c);
|
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 => (),
|
||||||
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,14 +6,14 @@ use bevy_tweening::{
|
|||||||
EaseFunction, Tween, TweeningType,
|
EaseFunction, Tween, TweeningType,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::turtle::TurtleGraphElement;
|
use crate::turtle::{TurtleGraphElement, TurtleState};
|
||||||
|
|
||||||
pub fn turtle_turn(
|
pub fn turtle_turn(
|
||||||
orientation: f32,
|
state: &mut TurtleState,
|
||||||
angle_to_turn: f32,
|
angle_to_turn: f32,
|
||||||
) -> (f32, Tween<Transform>, TurtleGraphElement) {
|
) -> (Tween<Transform>, TurtleGraphElement) {
|
||||||
let start = orientation;
|
let start = state.heading;
|
||||||
let end = orientation + (angle_to_turn * PI / 180.);
|
let end = state.heading + (angle_to_turn * PI / 180.);
|
||||||
let animation = Tween::new(
|
let animation = Tween::new(
|
||||||
// Use a quadratic easing on both endpoints.
|
// Use a quadratic easing on both endpoints.
|
||||||
EaseFunction::QuadraticInOut,
|
EaseFunction::QuadraticInOut,
|
||||||
@ -26,20 +26,16 @@ pub fn turtle_turn(
|
|||||||
// for the Animator to animate it. It also contains the start and
|
// for the Animator to animate it. It also contains the start and
|
||||||
// end values respectively associated with the progress ratios 0. and 1.
|
// end values respectively associated with the progress ratios 0. and 1.
|
||||||
TransformRotateZLens { start, end },
|
TransformRotateZLens { start, end },
|
||||||
);
|
)
|
||||||
|
.with_completed_event(state.index as u64);
|
||||||
let line = TurtleGraphElement::Noop;
|
let line = TurtleGraphElement::Noop;
|
||||||
let orientation = end % (2. * PI);
|
state.heading = end % (2. * PI);
|
||||||
(orientation, animation, line)
|
(animation, line)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn turtle_move(
|
pub fn turtle_move(state: &mut TurtleState, length: f32) -> (Tween<Transform>, TurtleGraphElement) {
|
||||||
position: Vec2,
|
let start = state.start;
|
||||||
orientation: f32,
|
let end = state.start + (Vec2::from_angle(state.heading) * length);
|
||||||
length: f32,
|
|
||||||
index: f32,
|
|
||||||
) -> (Vec2, Tween<Transform>, TurtleGraphElement) {
|
|
||||||
let start = position;
|
|
||||||
let end = position + (Vec2::from_angle(orientation) * length);
|
|
||||||
let turtle_movement_animation = Tween::new(
|
let turtle_movement_animation = Tween::new(
|
||||||
// accelerate and decelerate
|
// accelerate and decelerate
|
||||||
EaseFunction::QuadraticInOut,
|
EaseFunction::QuadraticInOut,
|
||||||
@ -53,8 +49,8 @@ pub fn turtle_move(
|
|||||||
end: end.extend(0.),
|
end: end.extend(0.),
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
.with_completed_event(index as u64);
|
.with_completed_event(state.index as u64);
|
||||||
let line = TurtleGraphElement::TurtleLine { start, end };
|
let line = TurtleGraphElement::TurtleLine { start, end };
|
||||||
|
state.start = end;
|
||||||
(end, turtle_movement_animation, line)
|
(turtle_movement_animation, line)
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user