Open TheNachoBIT opened 2 months ago
Alright, i think this is ready for review! :D
In here i'll show how the Timelines & Sequences API works:
A timeline is like a director: It takes care of storing the sequences and it contains a "Sequence Player" that allows you to play all of the animations that are stored in a specified framerate.
let mut timeline_one = Timeline::new(Framerate::Fixed(24.0)); // Fixed Timeline
let mut timeline_two = Timeline::new(Framerate::Interpolated(24.0)); // Interpolated Timeline
You can play forwards, and backwards based on what you want, and you can use Timestamps, Timecodes and Durations.
let mut timeline = Timeline::new(Framerate::Interpolated(24.0));
// If you're in a game/real-time context, you can use
// your engine's delta time to play the timeline.
timeline.add_by_duration(time.delta());
// And also play it backwards.
timeline.sub_by_duration(time.delta());
Sequences store all of the keyframes of the animation of a specific variable.
This is an example for creating sequences in a timeline:
let mut t = Timeline::new(Framerate::Interpolated(12.0));
// Let's create a Sequence called "count", that'll be an 'f64'.
let s: &mut Sequence<f64> = t.new_sequence("count").unwrap();
// "count" will be an animation that goes from 0 to 1 in one second.
s.add_keyframe_at_timestamp(Keyframe { value: 0.0 }, tcode_hms!(00:00:00));
s.add_keyframe_at_timestamp(Keyframe { value: 1.0 }, tcode_hms!(00:00:01));
As long as the type contains interpoli's Tween
trait, it can be animated.
Here's an example with kurbo's Vec2
:
let s: &mut Sequence<Vec2> = t.new_sequence("position").unwrap();
s.add_keyframe_at_timestamp(
Keyframe {
value: Vec2::new(200.0, 200.0),
},
&tcode_hms!(00:00:00),
);
s.add_keyframe_at_timestamp(
Keyframe {
value: Vec2::new(300.0, 200.0),
},
&tcode_hms!(00:00:01),
);
s.add_keyframe_at_timestamp(
Keyframe {
value: Vec2::new(800.0, 400.0),
},
&tcode_hms!(00:00:02),
);
s.add_keyframe_at_timestamp(
Keyframe {
value: Vec2::new(700.0, 500.0),
},
&tcode_hms!(00:00:03),
);
To finally use the animation's result, by taking the Vec2 example, you can get the value via tween_by_name()
:
let player_position: Vec2 = t.tween_by_name::<Vec2>("position");
If you're aiming for performance, you can get the sequence's pointer and store it as a reference.
let pointer = t.get_sequence_pointer("position").unwrap();
// More code here...
let player_position: Vec2 = t.tween_by_pointer::<Vec2>(pointer);
Here's an example of the Timelines that you can see and run for yourself in here. (TODO for myself: Add instructions and record video).
These work like Timelines, but the size of the Sequences are always known at compile-time, so they can only have one type (or you can use an enum if you want to store different types).
let mut timeline: StaticTimeline<Vec2> = StaticTimeline::new(Framerate::Fixed(24.0));
let sequence = timeline.new_sequence("sequence").unwrap();
sequence.add_keyframes_at_timestamp(vec![
(
Keyframe {
value: Vec2::new(0.0, 1.0),
},
&tcode_hmsf!(00:00:01:00),
),
(
Keyframe {
value: Vec2::new(1.0, 1.0),
},
&tcode_hmsf!(00:00:02:00),
),
(
Keyframe {
value: Vec2::new(1.0, 2.0),
},
&tcode_hmsf!(00:00:03:00),
),
]);
This gives even more performance at the cost of flexibility, so if you're in a situation where you need to squeeze as much performance as you want, you can use these.
I'm gonna need a ton of help and wisdom from everyone, because there's probably a lot of stuff that i'm doing wrong or can be handled much better. So any kind of input is appreciated c:
I'm making this a draft for now, this PR is to document the progress i've been making so far with this experiment.
This experiment can also fail. If you see any kind of flaw, please feel free to point it out.
The final goals of this (if succeeds) are:
Progress.
isize
instead off64
).as_string()
prettier (print out '00:00:00:00' instead of '0:0:0:0').TO-DO In Review
SMPTE Timecodes (Update 2).
SMPTE Timecodes are time units that are set like a clock: HH:MM:SS:FF (Hours, Minutes, Seconds, Frames).
For convenience, there's a macro that helps visualize this concept better in code:
~(Sadly, Rust's macros don't allow me to use ":" so i have to use ";" instead, if there's a workaround for this, please let me know)~
EDIT: I fixed it!
Timecodes can be used as a Timestamp, or as a value for a Timeline, by setting the Framerate:
There's
Framerate::Fixed(n)
andFramerate::Interpolated(n)
. Once Timelines become a thing, fixed framerates will round up to the nearest frame when the tween is calculated, which will be useful for frame-by-frame animations. Interpolated frames on the other hand, will interpolate regardless of the framerate you're running (unless you explicitly set "hold" frames).Timecodes with a framerate can advance and reverse by frames, seconds, minutes, hours and use
Duration
(Instant
coming soon).Example: Play one second frame-by-frame.
Example: Add by Duration.
Example: Sub by Duration.
The draft repository contains more examples in form of tests inside of
lib.rs
.This is all of the progress for now, for any questions, please feel free to ask :)