making progress on modular text animation systems
parent
8a5a8d39d4
commit
09d48951bc
@ -1 +1,3 @@
|
||||
pub mod debug;
|
||||
|
||||
pub mod text;
|
||||
|
||||
@ -0,0 +1,191 @@
|
||||
use std::time::Duration;
|
||||
|
||||
///
|
||||
/// Animated Text
|
||||
///
|
||||
/// The goal of this code is to get a rudimentary "Text Animation" system in place.
|
||||
///
|
||||
/// The use cases are:
|
||||
/// * Typing Text; ala RPG dialogs like Pokemon
|
||||
///
|
||||
/// Eventually adding more would be cool, like movement animations, but those get really
|
||||
/// complicated really fast...
|
||||
///
|
||||
use bevy::prelude::*;
|
||||
|
||||
pub struct AnimatedTextPlugin;
|
||||
|
||||
impl Plugin for AnimatedTextPlugin {
|
||||
fn build(&self, app: &mut App) {
|
||||
app.add_system(manage_texts).add_system(animate_texts);
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Bundle, Default)]
|
||||
pub struct AnimatedTextBundle {
|
||||
pub text_bundle: TextBundle,
|
||||
pub animated_text: AnimatedText,
|
||||
}
|
||||
|
||||
/// Animated Text Marker
|
||||
/// Use this to filter out entities managed by Text Animation systems
|
||||
#[derive(Component, Default)]
|
||||
pub struct AnimatedText {
|
||||
animation_type: Option<TextAnimationType>,
|
||||
animation_status: TextAnimationStatus,
|
||||
animation_duration: Option<Duration>,
|
||||
}
|
||||
|
||||
/// Animated Text component
|
||||
///
|
||||
/// Handles all of the logistics of running text animation
|
||||
impl AnimatedText {
|
||||
pub fn new(animation_type: TextAnimationType) -> Self {
|
||||
AnimatedText {
|
||||
animation_type: Some(animation_type),
|
||||
..default()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn play(&mut self) {
|
||||
self.animation_status = TextAnimationStatus::Playing;
|
||||
self.animation_duration = match self.animation_type {
|
||||
None => None,
|
||||
Some(TextAnimationType::Typing(seconds)) => Some(Duration::from_secs_f32(seconds)),
|
||||
};
|
||||
}
|
||||
|
||||
pub fn stop(&mut self) {
|
||||
self.animation_status = TextAnimationStatus::Stopped;
|
||||
self.animation_duration = None;
|
||||
}
|
||||
}
|
||||
|
||||
pub enum TextAnimationType {
|
||||
Typing(f32), // Typing text out for duration
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
enum TextAnimationStatus {
|
||||
#[default]
|
||||
Stopped,
|
||||
Playing,
|
||||
}
|
||||
|
||||
/// Manage individual text entities
|
||||
///
|
||||
/// Animated text entities need to conform to different shapes for functional reasons.
|
||||
/// For example if each letter needs to be a different color, each TextSection must be a single
|
||||
/// character rather than a word or paragraph.
|
||||
///
|
||||
/// Any time a text is updated we need to ensure it conforms to the needs of it's animation
|
||||
///
|
||||
/// FIXME: Only update according to animation type
|
||||
fn manage_texts(mut texts: Query<&mut Text, Changed<Text>>) {
|
||||
// Check if any Text entities are "dirty"
|
||||
let dirty = texts
|
||||
.iter()
|
||||
.any(|text| text.sections.iter().any(|section| section.value.len() > 1));
|
||||
// For each text
|
||||
// For each section
|
||||
// If section length > 1
|
||||
// Break into length-1 strings
|
||||
if dirty {
|
||||
texts.iter_mut().for_each(|mut text| {
|
||||
// Replace the existing sections with broken down sections
|
||||
// Each Text Section is a single character
|
||||
// On it's own this should not cause the text to look different
|
||||
// But means we can modify each text section individually in color
|
||||
text.sections = text
|
||||
.sections
|
||||
.iter()
|
||||
.map(|section| {
|
||||
section.value.chars().map(|c| TextSection {
|
||||
value: c.into(),
|
||||
style: section.style.clone(),
|
||||
})
|
||||
})
|
||||
.flatten()
|
||||
.collect();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
fn animate_texts(
|
||||
mut query: Query<(&mut Text, &mut AnimatedText), With<AnimatedText>>,
|
||||
time: Res<Time>,
|
||||
) {
|
||||
for (mut text, mut animated_text) in query.iter_mut() {
|
||||
match animated_text.animation_status {
|
||||
TextAnimationStatus::Stopped => (),
|
||||
TextAnimationStatus::Playing => match animated_text.animation_type {
|
||||
None => (),
|
||||
Some(TextAnimationType::Typing(seconds)) => {
|
||||
match animated_text.animation_duration {
|
||||
None => {
|
||||
// We have just started the animation, so set the duration appropriately
|
||||
animated_text.animation_duration =
|
||||
Some(Duration::from_secs_f32(seconds));
|
||||
}
|
||||
Some(mut inner) => {
|
||||
// We are continuing the animation, so decrement the remaining duration
|
||||
inner = Duration::from_secs_f32(
|
||||
inner.as_secs_f32() - time.delta().as_secs_f32(),
|
||||
);
|
||||
|
||||
todo!("Do the work of actually animating the text here");
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// PERF: Why does this slow down immediatly?
|
||||
// if *duration > Duration::ZERO {
|
||||
// *duration = duration.saturating_sub(time.delta());
|
||||
//
|
||||
// let percentage = 1.0 - (duration.as_secs_f32() / 30.0);
|
||||
//
|
||||
// let mut text = texts.single_mut();
|
||||
//
|
||||
// let len_total = desired.iter().fold(0, |acc, curr| acc + curr.len());
|
||||
// let mut len_total = ((len_total as f32 * percentage) as usize).min(len_total as usize);
|
||||
//
|
||||
// for (section, desired) in text.sections.iter_mut().zip(desired.iter()) {
|
||||
// // This section is empty, initialize with capacity
|
||||
// if len_total == 0 {
|
||||
// break;
|
||||
//
|
||||
// // Total amount of remaining text is greater than this section
|
||||
// // Set this text block to the full section
|
||||
// } else if len_total >= desirted.len() {
|
||||
// // info!("len_total >= desired.len()");
|
||||
// len_total -= desired.len();
|
||||
// if section.value.len() != desired.len() {
|
||||
// // info!("value != desired");
|
||||
// section.value = desired.clone();
|
||||
// }
|
||||
//
|
||||
// // Total remaining text is less than this section
|
||||
// // Set to a sub-string of text
|
||||
// } else if len_total < desired.len() {
|
||||
// // info!("len total < desired len");
|
||||
// // Difference between current value and desired length
|
||||
// let diff = desired
|
||||
// .split_at(section.value.len())
|
||||
// .1
|
||||
// .split_at(len_total - section.value.len())
|
||||
// .0
|
||||
// .into();
|
||||
// // info!("adding value {}", diff);
|
||||
// section.value.push_str(diff);
|
||||
// len_total = 0;
|
||||
//
|
||||
// // Undefined behavior
|
||||
// } else {
|
||||
// info!("Unexpected text animation situation");
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
Loading…
Reference in New Issue