Difference between revisions of "Animation System"
(testing the new templates) |
(→Application methods: Added animations.xml example for application method at the bottom. I could probably figure out how to do this in code as well, but I only use Lua/XML so I'll stick to that...) |
||
Line 121: | Line 121: | ||
== Application methods == | == Application methods == | ||
− | Affectors offer 3 different "application methods". One is absolute, meaning that the keyframe value is taken as absolute and is directly set to the property after interpolation. The other 2 are relative. That means that when the animation starts, the old values of properties are saved and later used when animating. AM_Relative means saved_value + interpolated_value, AM_RelativeMultiply means saved_value * interpolated_float | + | Affectors offer 3 different "application methods". One is absolute, meaning that the keyframe value is taken as absolute and is directly set to the property after interpolation. The other 2 are relative. That means that when the animation starts, the old values of properties are saved and later used when animating. AM_Relative means saved_value + interpolated_value, AM_RelativeMultiply means saved_value * interpolated_float. |
+ | |||
+ | === animations.xml example: === | ||
+ | In the following example we have changed the rotation affector to do relative rotation instead of absolute, which is default. This means that whatever rotation the window had when the animation started it will add the new rotation to that value. The absolute method would have reset the rotation to 0 in the first key frame and then proceeded to rotate it by 10 degrees during 0.3 seconds. | ||
+ | <code> | ||
+ | <Animations> | ||
+ | <AnimationDefinition name="Testing" duration="0.3" replayMode="once"> | ||
+ | <Affector property="Alpha" interpolator="float"> | ||
+ | <KeyFrame position="0.0" value="1.0" /> | ||
+ | <KeyFrame position="0.3" value="0.5" /> | ||
+ | </Affector> | ||
+ | <Affector property="YRotation" interpolator="float" applicationMethod="relative"> | ||
+ | <KeyFrame position="0.0" value="0" /> | ||
+ | <KeyFrame position="0.3" value="10" /> | ||
+ | </Affector> | ||
+ | </AnimationDefinition> | ||
+ | </Animations> | ||
+ | </code> |
Revision as of 18:22, 17 November 2010
Written for CEGUI 0.7
Works with versions 0.7.x (obsolete)
Requires at least version
0.7.2
Contents
Basic concepts
In UI lots of animations are likely to be shared (for example widget specific animation), so CEGUI splits the animation between definition and instance. You can have one animation definition animating large amount of Windows, having only one animation per window is also fine (one animation with only one instance). Animation definitions aren't tied to window type, the only condition is that the target must have all affected (target properties of all affectors inside the animation) and source properties (all source properties in key frames, if any).
Purpose of animation classes
Animation Definition (class CEGUI::Animation)
This class holds defining data for animations. Specifically duration, replay mode, auto start and list of Affectors. This class doesn't directly animate anything. You have to instantiate it via CEGUI::AnimationManager::instantiateAnimation.
Affector (class CEGUI::Affector)
Affector affects (changes the value of) one property. It holds application method (more about that later), target property, used interpolator and list of key frames.
Key Frame (class CEGUI::KeyFrame)
Key frame represents the value of affected property at given time position. It holds value, source property and time position. Key frames can use values of properties of the affected window. If source property is not empty, property is saved when animation starts and used as value for this key frame.
Animation Instance (class CEGUI::AnimationInstance)
This class uses animation definition to animate Window. It holds animation position and playback speed.
Animation Manager (class CEGUI::AnimationManager
Singleton class. You use it to create new animation definitions and instantiate existing animation definitions. (You should not construct Animation definitions or Animation instances directly, always use Animation Manager).
Interpolator (class CEGUI::Interpolator)
To be able to animate smoothly between 2 discrete keyframes, interpolators had to be introduced. You don't have to use them directly, everything is done for you in the animation classes. You only need to understand their internals if you need custom interpolator (as all basic interpolators are included in CEGUI, if you find some property that can't be animated with stock interpolators, let us know).
Defining animations
Via code
This sample animation takes 0.3 seconds and in that time, it fades the window and rotates it 10 degrees around Y axis.
<cpp/>
{
CEGUI::Animation* anim = CEGUI::AnimationManager::getSingleton().createAnimation("Testing"); anim->setDuration(0.3f); // duration in seconds anim->setReplayMode(CEGUI::Animation::RM_Once); // when this animation is started, only play it once, then stop // now we define affector inside our Testing animation { // this affector changes YRotation and interpolates keyframes with float interpolator CEGUI::Affector* affector = anim->createAffector("YRotation", "float"); // at 0.0 seconds, the animation should set YRotation to 10.0 degrees affector->createKeyFrame(0.0f, "0.0"); // at 0.3 seconds, YRotation should be 0 degrees and animation should progress towards this in an accelerating manner affector->createKeyFrame(0.3f, "10.0", CEGUI::KeyFrame::P_QuadraticAccelerating); } // animation can have more than one affectors! lets define another affector that changes Alpha { // this affector will again use float interpolator CEGUI::Affector* affector = anim->createAffector("Alpha", "float"); affector->createKeyFrame(0.0f, "1.0"); // at 0.0 seconds, set alpha to 0.5 affector->createKeyFrame(0.3f, "0.5", CEGUI::KeyFrame::P_QuadraticDecelerating); // at 1.0 seconds, set alpha to 1.0, now decelerating! }
}
Via Falagard looknfeel XML
<WidgetLook ...>
...
<AnimationDefinition name="Testing" duration="0.3" replayMode="once"> <Affector property="Alpha" interpolator="float"> <KeyFrame position="0.0" value="1.0" /> <KeyFrame position="0.3" value="0.5" /> </Affector> <Affector property="YRotation" interpolator="float"> <KeyFrame position="0.0" value="0" /> <KeyFrame position="0.3" value="10" /> </Affector> </AnimationDefinition>
</WidgetLook>
Via separate animation list XML (AnimationManager::loadAnimationsFromXML)
<Animations>
<AnimationDefinition name="Testing" duration="0.3" replayMode="once"> <Affector property="Alpha" interpolator="float"> <KeyFrame position="0.0" value="1.0" /> <KeyFrame position="0.3" value="0.5" /> </Affector> <Affector property="YRotation" interpolator="float"> <KeyFrame position="0.0" value="0" /> <KeyFrame position="0.3" value="10" /> </Affector> </AnimationDefinition>
</Animations>
Instantiating animations
If you created the animation in code or via separate animation definition file, you have to instantiate it (only Falagard instantiates and sets target automatically).
<cpp/>
CEGUI::AnimationInstance* instance = CEGUI::AnimationManager::getSingleton().instantiateAnimation(anim);
// after we instantiate the animation, we have to set it's target window
instance->setTargetWindow(targetWindow);
// at this point, you can start this instance and see the results instance->start();
Connecting events and animations
You often want your animation to start when something happens (Mouse gets over the widget, new FrameWindow is opened, etc). You have two options to achieve this.
If you already are using XML to define animations, the easiest solution is to use the auto event subscription facility. You add pairs of Event name and action that should happen with the animation ("Start", "Stop", "Pause", "Unpause", etc.) and when target window is set, these subscriptions are automatically made for your convenienve.
Both Falagard and Animations.xml approaches are the same with regards to this. This XML snippet starts the animation when mouse enters the target window's area.
<AnimationDefinition name="Testing" duration="0.3" replayMode="once">
... <Subscription event="MouseEntersArea" action="Start" />
</AnimationDefinition>
Doing this in code is much more powerful (you can subscribe to a window that's different than target window) but also more messy:
<cpp/>
windowWithEvent->subscribeEvent(CEGUI::Window::EventMouseEntersArea, CEGUI::Event::Subscriber(&CEGUI::AnimationInstance::handleStart, instance));
Progression
As I created the implementation, I noticed that linear animations look really boring and predictable. Often times accelerating or decelerating anims will look much better and lively. To make creating those easier, I introduced progression to key frames. This enum tells the system how to progress towards the key frame that holds the progression (that means progression on the first key frame is never used!). The default is linear, meaning that progress towards the keyframe is completely linear. Try the other options yourself to see the differences.
Application methods
Affectors offer 3 different "application methods". One is absolute, meaning that the keyframe value is taken as absolute and is directly set to the property after interpolation. The other 2 are relative. That means that when the animation starts, the old values of properties are saved and later used when animating. AM_Relative means saved_value + interpolated_value, AM_RelativeMultiply means saved_value * interpolated_float.
animations.xml example:
In the following example we have changed the rotation affector to do relative rotation instead of absolute, which is default. This means that whatever rotation the window had when the animation started it will add the new rotation to that value. The absolute method would have reset the rotation to 0 in the first key frame and then proceeded to rotate it by 10 degrees during 0.3 seconds.
<Animations>
<AnimationDefinition name="Testing" duration="0.3" replayMode="once"> <Affector property="Alpha" interpolator="float"> <KeyFrame position="0.0" value="1.0" /> <KeyFrame position="0.3" value="0.5" /> </Affector> <Affector property="YRotation" interpolator="float" applicationMethod="relative"> <KeyFrame position="0.0" value="0" /> <KeyFrame position="0.3" value="10" /> </Affector> </AnimationDefinition>
</Animations>