-
Notifications
You must be signed in to change notification settings - Fork 32
Keyframed Values
osu! uses Commands to animate sprites. These have the format start time
, end time
, start value
, end value
.
When you have multiple commands in a row, this translates into repeating yourself a lot:
sprite.Fade(0, 1000, 0, .5f);
sprite.Fade(1000, 2000, .5f, .7f);
sprite.Fade(2000, 3000, .7f, .3f);
sprite.Fade(3000, 4000, .3f, .1f);
sprite.Fade(4000, 5000, .1f, 0);
Keyframes are simpler and just use time
, value
. The same set of commands would look like this:
opacity.Add(0, 0)
.Add(1000, .5f)
.Add(2000, .7f)
.Add(3000, .3f)
.Add(4000, .1f)
.Add(5000, 0);
Once you have defined a set of keyframes, you'll want to convert them to commands. This is what the ForEachPair
method is for:
opacity.ForEachPair((start, end) => sprite.Fade(start.Time, end.Time, start.Value, end.Value));
The constructor to create keyframed values looks like this:
var opacity = new KeyframedValue<float>(interpolate);
The interpolate
parameter tells the KeyframedValue
how to interpolate values between keyframes. It can be left to null
if you aren't going to call the ValueAt(double time)
method.
Interpolating functions can be found in StorybrewCommon.Animations.InterpolatingFunctions
:
var opacity = new KeyframedValue<float>(InterpolatingFunctions.Float);
Keyframes are mainly used to simplify large amount of commands. You can see an example of that in the spectrum scripts, Spectrum and RadialSpectrum.
This simplification is based on the Ramer–Douglas–Peucker algorithm; the main idea is to take the value furthest away from the line, as long as it is farther than a threshold. Values that are close enough can be removed:
This is done by the methods Simplify1dKeyframes
, Simplify2dKeyframes
, Simplify3dKeyframes
. The one you want to use depends on how many parameters the command would take:
- MoveX, MoveY, Rotate, Scale and Fade takes one value so you'd use
Simplify1dKeyframes
- Move and ScaleVec take two so you'd use
Simplify2dKeyframes
; - Color takes three, so you'd use
Simplify3dKeyframes
Simplifying the keyframes defined in the example above would look like this:
opacity.Simplify1dKeyframes(tolerance, v => v);
The tolerance
parameter is the distance threshold from which keyframes can be removed.
The v => v
part is used to convert the keyframes values to a float
that the method works with. Since the values are already floats, there is nothing to do. The same thing usually applies to Simplify2dKeyframes
, where the values needed are already Vector2
. If you were simplifying colors, you'd have to convert from Color4
to Vector3
:
colors.Simplify3dKeyframes(tolerance, v => new Vector3(v.R, v.G, v.B));
using StorybrewCommon.Animations;
using StorybrewCommon.Scripting;
namespace StorybrewScripts
{
public class OpacitySample : StoryboardObjectGenerator
{
public override void Generate()
{
var sprite = GetLayer("").CreateSprite("image.png");
var opacity = new KeyframedValue<float>(null);
opacity.Add(0, 0)
.Add(1000, .5f)
.Add(2000, .7f)
.Add(3000, .3f)
.Add(4000, .1f)
.Add(5000, 0);
opacity.Simplify1dKeyframes(.3f, v => v);
opacity.ForEachPair((start, end) => sprite.Fade(start.Time, end.Time, start.Value, end.Value));
}
}
}
Need help with something that isn't in the wiki? Try contacting Damnae in osu! or Discord.