Morpheus: A versatile, fluent API for crafting smooth and dynamic animations in C#.
Its primary goal is to offer a clean and convenient API, enabling seamless fire-and-forget animations. Behold:
Morpheus
.Animate(myObject)
.Property("X").From(0).To(10)
.Property("Y").From(50).To(75)
.Property("Z").From(2.0).To(10)
.Property("Position").From(Vector2.Zero).To(new Vector2(15, 10))
.For(TimeSpan.FromSeconds(1))
.Start();
Keep in mind that Morpheus
focus on convenience and readability, rather than purely performance. If you need to animate millions of objects in performance-critical applications, Morpheus
may not be the best choice for you. It is good enough for most games, though.
Morpheus
has a main static manager that updates all the running animations, and can either update itself automatically using a timer or wait for update calls from the host application.
This class also provides an API to start building a new Animation
on a given target or type, via the Animation Builder
object.
The Animation Builder
implements a fluent API to set the properties you want to animate, and will eventually generate and start the animation.
Morpheus
don't know the objects it animates, and relay on reflection to access and animate its properties. You can also provide a setter method to animate objects that are passed by-value (rather than by-ref), or to handle animations in a different way.
There are 3 steps to starting an animation:
- Creating the builder for a given target or type.
- Settings From and To values for the object fields.
- Set animation duration and and fire it.
In code, it looks like this:
Morpheus
.Animate(myObject) // step 1
.Property("X").From(0).To(10) // step 2
.For(TimeSpan.FromSeconds(1)).Start(); // step 3
Once Start()
is called, the animation will be managed by Morpheus
and progress itself until it ends. Animations that play once will also remove themselves when done, so you can fire and forget about them.
There are two ways to play animations: fire & forget, or spawning an Animation
instance and manage it yourself.
When you only need to play an animation once from start to end, always prefer the fire & forget method, by simply calling Start()
(or StartReverse()
for playing backward).
This method will use objects pool and be relatively RAM and CPU efficient.
In addition, you should always prefer to reuse existing Animation Builder
rather than creating new ones.
For better reusability, you can create an Animation Builder
for just a type without a target, then fire it on multiple instances:
var builder = Morpheus
.Animate<MyClassType>()
.Property("X").From(0).To(10)
.For(TimeSpan.FromSeconds(1))
.StartOn(target1)
.StartOn(target2, 2f); // <-- this instance will run x2 times faster
If you must use an Animation
instance and can't just fire & forget (for example if you need repeating animations, change offset in the middle, change speed etc.), use the Spawn()
method to create an Animation
instance, then Start()
to start playing it.
Just keep in mind that this way will generate garbage and won't use the objects pool.
Morpheus
is the main static class that manage everything. This is also our API entry point.
Let's review its API:
Spawn a new AnimationBuilder
to build an animation for a given object.
When you use this method you can use either Start()
to fire the animation, or StartOn()
to apply animation on a different target.
AnimationBuilder
will be described in details in next API section.
Spawn a new AnimationBuilder
to build an animation for a class type.
When you use this method be sure to fire animations using StartOn()
, since there won't be a default target to use.
AnimationBuilder
will be described in details in next API section.
Start running Morpheus
updates in background, using a timer.
If you don't call this method, you need to call Update(dt)
every frame from your host application.
Stop running Morpheus
in updates in background.
Perform a single update step with time passed, in seconds, since last update.
You only need to call this if you don't use StartInBackground()
.
Get how many animations are currently running.
Stop and remove all running animations.
Clear internal caches.
- Objects Pool: will clear the internal objects pool used for animations instances.
- Reflections Cache: will clear internal cache that store reflection results, such as field / property infos.
By default, both flags will be set to true.
If set (by default defined to 1/60) and we get an update step with delta time bigger than this value, the Update call will be broken down into sub-steps with the size of this factor.
Will limit the maximum amount of sub-steps when MaxUpdateTime
is used.
AnimationBuilder
is an object you get from calling Morpheus.Animate()
, and its used to define and start animations.
All the methods in AnimationBuilder
return itself for chaining, except for the methods to spawn a new animation instance, and methods that start defining a new property (explained below).
It has the following API:
Start configuring an animation ('From' and 'To' values) for a given field or property name. Due to C# limitations, this API is only permitted if the object you're animating is by-ref type (class) and not by-value (struct).
The property name has to be the exact name of the field or property that you want to morph during the animation. The types that are supported by default are:
- float
- byte
- int
- Vector2
- Vector3
- Vector4
- Rectangle
- RectangleF
- Color
- Point
If you need to support other types, you can register a custom resolver with Morpheus.ObjectsInterpolation.RegisterResolver(type, resolver))
.
Calling Property(name)
will return an object with interface that implements only one method: From()
. This method will define the From()
value you wish to start the animation from.
After calling From()
you will get an object with only one method: To()
. This method is used to define the value you wish to end the animation at.
After calling To()
, you will get back the AnimationBuilder
instance.
This means that in order to define a property animation, you use the following syntax:
builder.Property("SomeField").From(5).To(10)
// and here you can define more properties to animate..
Or to define more than one property with different types:
builder
.Property("SomeField").From(5).To(10)
.Property("SomeVector").From(Vector2.Zero).To(new Vector2(10, 5))
.Property("SomeColor").From(Color.Red).To(Color.Blue)
// here you can call Start() to begin the animation, or set other properties first.
Setter
is the same as Property
, but instead of letting Morpheus
access the field or property by name and change it internally, it will call a method you provide with the updated animated value. Then you can take this value and apply it however you like.
Usage example:
// the following will animate a Vector2 target, even though its by-value, using a Setter.
Vector2 target = new Vector2();
Morpheus
.Animate(target)
.Setter((Vector2 x) => target = x).From(Vector2.Zero).To(new Vector2(10, 5))
.For(TimeSpan.FromSeconds(1))
.Start();
Set the duration of the animation from TimeSpan. By default, animations duration is 1 second.
Usage example:
builder.For(TimeSpan.FromSeconds(1)).Start();
Set the default object to build the animation from.
If you create the builder for a specific target (using Animate(target)
method) it will already be set.
If you set a target, you can fire animation instances with Start()
.
If you don't set a target, you need to call StartOn(target)
instead.
Usage example:
builder.On(someObject).Start();
Set a callback method to call when animation ends.
Usage example:
builder.Then(() => { Console.WriteLine("Ended!"); }).Start();
Start playing the animation once on the currently set target, with an optional speed factor.
This method will not return the Animation
instance, but will utilize internal objects pool to reduce garbage generation.
Same as Start()
but in reverse.
Same as Start()
, but will play the animation on a given target instead of using whatever target is currently set.
Use this method to reuse the same animation builder on multiple instances efficiently.
Same as Start()
but in reverse.
Will spawn and return an Animation
instance on the currently set target.
The returned animation is in stopped state, and you need to call Start
to play it.
Note that this method does not use the internal object pool and will always create a new instance.
Same as Spawn()
, but will spawn the animation on a given target instead of using whatever target is currently set.
Animation
is the object Morpheus
uses internally to manage animation instances. Normally you don't need to use this class directly, unless you need more control over the animations and choose to use Spawn()
instead of Start()
.
The Animation
API also provide a fluent interface, and provide better control over specific animation instances.
Morpheus
provides control over the interpolation type of every property you choose to animate. When you call the To()
method to provide the animation end value, you can provide an interpolation method that will be used to calculate the animated value.
For example:
Morpheus
.Animate(target)
.Property("X").From(0).To(10, Morpheus.Interpolation.BounceEaseIn)
.Property("Y").From(0).To(10)
.For(TimeSpan.FromSeconds(2))
.Start();
Morpheus.Interpolation
provide some basic common types you can use out of the box, or you can provide your own method.
By default, interpolation will use a simple linear interpolation (lerp).
A demo project to illustrate basic animations with different interpolation types can be found in project Morpheus.Demo.RayLib.
It uses RayLib
for rendering.
A test project can be found under Morpheus.Tests.
- To prevent flickering, fixed so that targets will be set with animation
From
values as soon as animation starts. - Made
ClearCache
andRemoveAll
thread safe. - Added option to remove all animations tied to a specific target.
- Added option to set animations
To
value as a getter method. - Removed exception if calling
Start()
on already playing animation, orStop()
on non playing animation. It caused more harm than good and the behavior is pretty well defined so no need for exception. - Made
Then()
in Animation Builder also affect last spawned animation.
- Fixed bug with
Then()
in Animation Builder.
- Added flag to control if to play animations frame 0 as soon as they are created.
- Added 'Delay' to animations.
Morpheus
is distributed with the MIT license and can be used for any purpose.