-
Notifications
You must be signed in to change notification settings - Fork 33
different system for state transition #105
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: development
Are you sure you want to change the base?
Conversation
the last commit videos
before: 2025-03-28_05-04-37.mp4after: 2025-03-28_05-06-13.mp4 |
added looping cleaned up the constructor functions and added a continue segment (for moving weight from current value) and updated the example
we can merge the base balancer into the transition system (i'm thinking "we're already looping the states here, and less loops = faster") but i'm not sure how correct that is. and it becomes an even bigger mess than it already is! xD horror (code)
pub fn system_animate_transition(
mut query: Query<(Entity, &mut UiState, &mut UiStateAnimation)>,
time: Res<Time<Virtual>>,
mut commands: Commands,
) {
let mut should_recompute = false;
for (entity, mut manager, mut animations) in &mut query {
let mut total_nonbase_weight = 0.0;
for (state, anim) in animations.iter_mut() {
// we want to check if we reached a trig separately so we can trigger it
// and resume animation without skipping a frame
if let Some(Seg::Trig) = anim.segments.get(anim.current_seg) {
commands.trigger_targets(AnimTrig(state), entity);
anim.step();
}
if let Some(seg) = anim.segments.get(anim.current_seg) {
match seg {
Seg::To(target, duration) => {
if !manager.states.contains_key(state) {
manager.states.insert(*state, 0.);
}
let weight = manager.states.get_mut(state).unwrap();
if anim.value == *target {
anim.step();
} else if anim.value > *target {
anim.value = (anim.value - time.delta_secs() / *duration).max(*target);
*weight = anim.value;
} else {
anim.value = (anim.value + time.delta_secs() / *duration).min(*target);
*weight = anim.value;
}
should_recompute = true;
}
Seg::Curved(target, duration, f) => {
if !manager.states.contains_key(state) {
manager.states.insert(*state, 0.);
}
let weight = manager.states.get_mut(state).unwrap();
if anim.value == *target {
anim.step();
} else if anim.value > *target {
anim.value = (anim.value - time.delta_secs() / *duration).max(*target);
*weight = f(anim.value);
} else {
anim.value = (anim.value + time.delta_secs() / *duration).min(*target);
*weight = f(anim.value);
}
should_recompute = true;
}
Seg::Continue(target, duration) => {
if !manager.states.contains_key(state) {
manager.states.insert(*state, 0.);
}
let weight = manager.states.get_mut(state).unwrap();
if *weight == *target {
anim.step();
} else if *weight > *target {
*weight = (*weight - time.delta_secs() / *duration).max(*target);
} else {
*weight = (*weight + time.delta_secs() / *duration).min(*target);
}
should_recompute = true;
}
Seg::Hold(duration) => {
if *duration <= anim.elapsed_duration {
anim.elapsed_duration = 0.;
anim.step();
} else {
anim.elapsed_duration += time.delta_secs();
}
}
Seg::Trig => {}
}
}
// Normalize the active nobase state weights
if *state != "base" {
if let Some(weight) = manager.states.get(state) {
total_nonbase_weight += weight;
}
}
}
// Decrease base transition based on other states
if should_recompute {
if let Some(value) = manager.states.get_mut("base") {
*value = (1.0 - total_nonbase_weight).clamp(0.0, 1.0);
}
}
}
if should_recompute {
commands.trigger(RecomputeUiLayout);
}
} |
for this i'm thinking about adding a Trig variant to the Seg enum. whenever the transitioning system finds this segment, it triggers an event (targeting this entity, and maybe with info of the state being animated) and it jumps to the next segment. this way you can do things at any stage of an animation on any state for any specific entity |
idk why i thought that was right
we always check and insert, so this is just unnecessary indentation
the workaround (previous commit) avoids this issue videos
before: (spawning with the observer) 2025-03-30_19-16-17.mp4after: (spawning with an event) 2025-03-30_19-18-22.mp4 |
and use Time<Real> (we're the ui, we stop for nothing)
thinking about this since yesterday #99 (reply in thread) |
i've done something on top of this that's a little more... conceptually adventurous xD you can animate different fields separately 2025-04-11_06-15-17.mp4 |
useful for directly modifying the layout
check the example to see how it works, and let me know what you think and if there's any limitations/issues with this
still need to edit the docs to reflect the changes