mirror of
https://github.com/brl/mutter.git
synced 2024-11-27 10:30:47 -05:00
5da2cf7b1a
* clutter-animation.sgml: Fix warning * creating-your-own-behaviours.sgml: Add a paragraph about setting an initial state on the actors.
601 lines
18 KiB
Plaintext
601 lines
18 KiB
Plaintext
<chapter id="clutter-animations">
|
|
<chapterinfo>
|
|
<author>
|
|
<firstname>Matthew</firstname>
|
|
<surname>Allum</surname>
|
|
<affiliation>
|
|
<address>
|
|
<email>mallum@openedhand.com</email>
|
|
</address>
|
|
</affiliation>
|
|
</author>
|
|
</chapterinfo>
|
|
|
|
<title>Creating Animations with Clutter</title>
|
|
|
|
<para>
|
|
|
|
With Clutter using hardware accelration for graphics rendering,
|
|
complex and fast animations are possible. This chapter describes basic
|
|
techniques and the utilities Clutter provides in aiding animation
|
|
creation.
|
|
|
|
</para>
|
|
|
|
<section id="clutter-animation-basic">
|
|
<title>Basic Animations</title>
|
|
|
|
<para>
|
|
|
|
The most basic way to create animations with Clutter is via the use of
|
|
g_timeout_add(). This enables a callback function to be called at a
|
|
defined interval. The callback function can then modify actors visual
|
|
properties as to produce an animation.
|
|
|
|
</para>
|
|
|
|
<example id="clutter-timeout-example">
|
|
<para>
|
|
Simple Rotation...
|
|
</para>
|
|
<programlisting>
|
|
struct RotationClosure {
|
|
ClutterActor *actor;
|
|
ClutterFixed final_angle;
|
|
ClutterFixed current_angle;
|
|
};
|
|
|
|
static gboolean
|
|
rotate_actor (gpointer data)
|
|
{
|
|
RotationClosure *clos = data;
|
|
|
|
clutter_actor_set_rotationx (clos->actor, clos->current_angle, 0, 0, 0);
|
|
|
|
clos->current_angle += CFX_ONE;
|
|
|
|
if (clos->current_angle == clos->final_angle)
|
|
return FALSE;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
...
|
|
RotationClosure clos = { NULL, }
|
|
|
|
clos.actor = an_actor;
|
|
clos.final_angle = CLUTTER_FLOAT_TO_FIXED (360.0);
|
|
clos.current_angle = 0;
|
|
|
|
g_timeout_add (1000 / 360, /* fps to interval in milliseconds */
|
|
rotate_actor,
|
|
&clos);
|
|
</programlisting>
|
|
</example>
|
|
|
|
<note><title>Priorities</title>
|
|
<para>
|
|
|
|
%G_PRIORITY_DEFAULT should always be used as the timeouts priority
|
|
(in case of g_timeout_add_full()) as not to intefere with Clutter's
|
|
scheduling of repaints and input event handling.
|
|
|
|
</para>
|
|
</note>
|
|
|
|
</section>
|
|
<section id="clutter-animation-timelines">
|
|
<title>Timelines</title>
|
|
<para>
|
|
#ClutterTimeline<!-- -->s abstract a set period of time with a set frame
|
|
rate at which to call a provided callback.
|
|
</para>
|
|
|
|
<para>
|
|
#ClutterTimeline<!-- -->s also extend the timeout sources functionality
|
|
further by:
|
|
</para>
|
|
|
|
<orderedlist>
|
|
<listitem><para>Having a set duration (in milliseconds) and a set
|
|
'frame rate' - that is, the rate at which the callback is
|
|
called</para></listitem>
|
|
<listitem><para>Passing current progress information to the
|
|
callback</para></listitem>
|
|
<listitem><para>Handling 'dropped frames' and guarenteeing the set
|
|
duration by skipping over frames if the callback cannot keep up with
|
|
the set frame rate</para></listitem>
|
|
<listitem><para>Querying the number of milliseconds elapsed between
|
|
the current and previous callback.</para></listitem>
|
|
<listitem><para>Allowing the timeline to be modified on the fly as
|
|
well as being stopped, started, looped, rewound and
|
|
reversed</para></listitem>
|
|
<listitem><para>Using a #ClutterTimeoutPool to more efficiently
|
|
schedule multiple timeout sources without incurring in potential
|
|
starvation of the main loop slices</para></listitem>
|
|
</orderedlist>
|
|
|
|
<para>
|
|
A Timeline is created with;
|
|
</para>
|
|
|
|
<programlisting>
|
|
clutter_timeline_new (n_frames, frames_per_seconds);
|
|
</programlisting>
|
|
|
|
<para>
|
|
Taking a number of frames and a frames per second, or by;
|
|
</para>
|
|
|
|
<programlisting>
|
|
clutter_timeline_new_for_duration (msecs);
|
|
</programlisting>
|
|
|
|
<para>
|
|
Which takes the duration of the timeline in milliseconds with a
|
|
default frame rate (See clutter_get_default_frame_rate()).
|
|
</para>
|
|
|
|
<para>
|
|
|
|
The speed, duration and number of frames of the timeline then be
|
|
modifed via the objects properties and API calls. The timeline can
|
|
be made to loop by settings it "loop" property to %TRUE.
|
|
|
|
</para>
|
|
<para>
|
|
|
|
The timelines is started via clutter_timeline_start() and its
|
|
playback further manipulated by the clutter_timeline_pause(),
|
|
clutter_timeline_stop(), clutter_timeline_rewind() and
|
|
clutter_timeline_skip() calls.
|
|
|
|
</para>
|
|
<para>
|
|
|
|
By attaching a handler to the timeline's ClutterTimeline::new-frame
|
|
signal a timeline can then be used to drive an animation by altering
|
|
an actor's visual properties in this callback. The callback looks like:
|
|
|
|
</para>
|
|
<programlisting>
|
|
void
|
|
on_new_frame (ClutterTimeline *timeline,
|
|
gint frame_num,
|
|
gpointer user_data)
|
|
{
|
|
|
|
}
|
|
</programlisting>
|
|
<para>
|
|
|
|
The <literal>frame_num</literal> parameter is set to the timeline's
|
|
current frame number (which is between 0 and the "num-frames" property).
|
|
This value can be used to compute the state of a particular animation
|
|
that is dependant on the frame numer. The clutter_timeline_get_progress()
|
|
function can also be used to get a normalised value of the timeline's
|
|
current position between 0 and 1.
|
|
|
|
</para>
|
|
<para>
|
|
|
|
Timelines can also be played in reverse by setting the direction using
|
|
clutter_timeline_set_direction(), and can also have a one-time delay set
|
|
before they begin playing by using clutter_timeline_set_delay().
|
|
|
|
</para>
|
|
<para>
|
|
|
|
Timelines can also control a pyshical simulation; the
|
|
clutter_timeline_get_delta() function allows retrieving the number of
|
|
frames and milliseconds elapsed since the previous callback to ensure
|
|
the physics engine to be able to take the actual time elapsed between
|
|
iterations into account.
|
|
|
|
</para>
|
|
|
|
<example id="clutter-timeline-example">
|
|
<para>
|
|
The following example demonstrates rotating an actor with a timeline.
|
|
</para>
|
|
<programlisting>
|
|
#include <clutter/clutter.h>
|
|
|
|
void
|
|
on_new_frame (ClutterTimeline *timeline,
|
|
gint frame_num,
|
|
gpointer data)
|
|
{
|
|
ClutterActor *actor = CLUTTER_ACTOR(data);
|
|
|
|
clutter_actor_set_rotation (actor, CLUTTER_Z_AXIS,
|
|
(gdouble) frame_num,
|
|
clutter_actor_get_width (actor) / 2,
|
|
clutter_actor_get_height (actor) / 2,
|
|
0);
|
|
}
|
|
|
|
int
|
|
main (int argc, char *argv[])
|
|
{
|
|
ClutterTimeline *timeline;
|
|
|
|
ClutterActor *stage, *actor;
|
|
GdkPixbuf *pixbuf;
|
|
|
|
clutter_init (&argc, &argv);
|
|
|
|
stage = clutter_stage_get_default ();
|
|
|
|
pixbuf = gdk_pixbuf_new_from_file ("an-image.png", NULL);
|
|
|
|
actor = clutter_texture_new_from_pixbuf (pixbuf);
|
|
|
|
clutter_container_add_actor (CLUTTER_CONTAINER (stage), actor);
|
|
|
|
clutter_actor_set_position (actor, 100, 100);
|
|
|
|
timeline = clutter_timeline_new_for (360, 60); /* a degree per frame */
|
|
clutter_timeline_set_loop (timeline, TRUE);
|
|
|
|
g_signal_connect (timeline, "new-frame", G_CALLBACK (on_new_frame), actor);
|
|
|
|
clutter_actor_show_all (stage);
|
|
|
|
clutter_timeline_start (timeline);
|
|
|
|
clutter_main();
|
|
|
|
return 0;
|
|
}
|
|
</programlisting>
|
|
</example>
|
|
|
|
<note><para>
|
|
Multiple timelines can be sequenced in order by means of the
|
|
#ClutterScore. See the #ClutterScore documentation for more details on
|
|
using this.
|
|
</para></note>
|
|
|
|
</section>
|
|
<section id="clutter-animation-behaviours">
|
|
<title>Behaviours</title>
|
|
<para>
|
|
|
|
With a large application containing many animations, the use of just
|
|
timelines can become unweldy and difficult to manage with much code
|
|
duplication in the new-frame handlers that can require over complex
|
|
code changes for minor animation modifications. To ease these
|
|
problems the #ClutterAlpha and #ClutterBehaviour classes were created.
|
|
|
|
</para>
|
|
<para>
|
|
|
|
#ClutterAlpha and #ClutterBehaviour attempt to generalise the
|
|
new-frame function by defining common actions or behaviours that can
|
|
be quickly modified, applied to multiple actors or mixed on a single
|
|
actor.
|
|
|
|
</para>
|
|
<para>
|
|
|
|
A ClutterAlpha is simply a 'function of time' (not pixel alpha!). It
|
|
is created by referencing a source timeline and a function which
|
|
produces a value between 0 and %CLUTTER_ALPHA_MAX dependant on the
|
|
timeline position. Various prebuilt alpha functions are included
|
|
with Clutter these include
|
|
|
|
</para>
|
|
<para>
|
|
|
|
<variablelist>
|
|
<varlistentry>
|
|
<term>%CLUTTER_ALPHA_RAMP_INC</term>
|
|
<listitem><simpara>Increasing ramp function</simpara></listitem>
|
|
</varlistentry>
|
|
<varlistentry>
|
|
<term>%CLUTTER_ALPHA_RAMP_DEC</term>
|
|
<listitem><simpara>Decreasing ramp function</simpara></listitem>
|
|
</varlistentry>
|
|
<varlistentry>
|
|
<term>%CLUTTER_ALPHA_RAMP</term>
|
|
<listitem><simpara>Full ramp function</simpara></listitem>
|
|
</varlistentry>
|
|
<varlistentry>
|
|
<term>%CLUTTER_ALPHA_SINE_INC</term>
|
|
<listitem><simpara>Increasing sine function</simpara></listitem>
|
|
</varlistentry>
|
|
<varlistentry>
|
|
<term>%CLUTTER_ALPHA_SINE_DEC</term>
|
|
<listitem><simpara>Decreasing sine function</simpara></listitem>
|
|
</varlistentry>
|
|
<varlistentry>
|
|
<term>%CLUTTER_ALPHA_SINE_HALF</term>
|
|
<listitem><simpara>Half sine function</simpara></listitem>
|
|
</varlistentry>
|
|
<varlistentry>
|
|
<term>%CLUTTER_ALPHA_SINE</term>
|
|
<listitem><simpara>Full sine function</simpara></listitem>
|
|
</varlistentry>
|
|
<varlistentry>
|
|
<term>%CLUTTER_ALPHA_SQUARE</term>
|
|
<listitem><simpara>Square waveform ("step") function</simpara></listitem>
|
|
</varlistentry>
|
|
<varlistentry>
|
|
<term>%CLUTTER_ALPHA_SMOOTHSTEP_INC</term>
|
|
<listitem><simpara>Increasing smooth transition step
|
|
function</simpara></listitem>
|
|
</varlistentry>
|
|
<varlistentry>
|
|
<term>%CLUTTER_ALPHA_SMOOTHSTEP_DEC</term>
|
|
<listitem><simpara>Decreasing smooth transition step
|
|
function</simpara></listitem>
|
|
</varlistentry>
|
|
<varlistentry>
|
|
<term>%CLUTTER_ALPHA_EXP_INC</term>
|
|
<listitem><simpara>Increasing exponential function</simpara></listitem>
|
|
</varlistentry>
|
|
<varlistentry>
|
|
<term>%CLUTTER_ALPHA_EXP_DEC</term>
|
|
<listitem><simpara>Decreasing exponential function</simpara></listitem>
|
|
</varlistentry>
|
|
</variablelist>
|
|
</para>
|
|
<para>
|
|
|
|
A Behaviour is created with a #ClutterAlpha and a set of limits for
|
|
whatever the behaviour modifys actor wise. The current #ClutterAlpha
|
|
value is then mapped to a value between these limits and this value
|
|
set on any applied actors. With the #ClutterAlpha's underlying
|
|
timeline playing the produced value will change and the behaviour
|
|
will animate the actor.
|
|
|
|
</para>
|
|
<para>
|
|
|
|
A #ClutterBehaviour is effectively 'driven' by a supplied #ClutterAlpha and
|
|
when then applied to an actor it will modify a visual property or
|
|
feature of the actor dependant on the Alpha's value. For example a
|
|
path based behaviour applied to an actor will alter its position
|
|
along the path dependant on the current alpha value over time. The
|
|
actual motion will depend on the chosen #ClutterAlphaFunc - a
|
|
#CLUTTER_ALPHA_RAMP_INC making it to move at constant speed along the
|
|
path, a #CLUTTER_ALPHA_SINE making it alternate from one end of the
|
|
path to the other with non constant speed.
|
|
|
|
</para>
|
|
<para>
|
|
|
|
Multiple behaviours can of course be applied to an actor as well as
|
|
a single behaviour being applied to multiple actors. The separation
|
|
of timelines, alphas and behaviours allows for a single timeline to
|
|
drive many behaviours each potentially using different alpha
|
|
functions. Behaviour parameters can also be changed on the fly.
|
|
|
|
</para>
|
|
|
|
<para>
|
|
|
|
<figure id="behaviour-path-alpha">
|
|
<title>Effects of alpha functions on a path</title>
|
|
<graphic fileref="path-alpha-func.png" format="PNG"/>
|
|
<blockquote>
|
|
The actors position between the path's end points directly correlates
|
|
to the #ClutterAlpha's current alpha value driving the behaviour. With
|
|
the #ClutterAlpha's function set to %CLUTTER_ALPHA_RAMP_INC the actor
|
|
will follow the path at a constant velocity, but when changing to
|
|
%CLUTTER_ALPHA_SINE_INC the actor initially accelerates before quickly
|
|
decelerating.
|
|
</blockquote>
|
|
</figure>
|
|
|
|
</para>
|
|
<para>
|
|
|
|
The behaviours included in Clutter are
|
|
|
|
</para>
|
|
<para>
|
|
|
|
<variablelist>
|
|
<varlistentry>
|
|
<term>#ClutterBehaviourBspline</term>
|
|
<listitem><simpara>Moves actors along a B-spline path</simpara></listitem>
|
|
</varlistentry>
|
|
<varlistentry>
|
|
<term>#ClutterBehaviourDepth</term>
|
|
<listitem><simpara>Changes the depth of actors</simpara></listitem>
|
|
</varlistentry>
|
|
<varlistentry>
|
|
<term>#ClutterBehaviourEllipse</term>
|
|
<listitem><simpara>Moves actors along an ellipsis</simpara></listitem>
|
|
</varlistentry>
|
|
<varlistentry>
|
|
<term>#ClutterBehaviourOpacity</term>
|
|
<listitem><simpara>Changes the opacity of actors</simpara></listitem>
|
|
</varlistentry>
|
|
<varlistentry>
|
|
<term>#ClutterBehaviourPath</term>
|
|
<listitem><simpara>Moves actors along a path</simpara></listitem>
|
|
</varlistentry>
|
|
<varlistentry>
|
|
<term>#ClutterBehaviourRotate</term>
|
|
<listitem><simpara>Rotates actors along an axis</simpara></listitem>
|
|
</varlistentry>
|
|
<varlistentry>
|
|
<term>#ClutterBehaviourScale</term>
|
|
<listitem><simpara>Changes the scaling factors of
|
|
actors</simpara></listitem>
|
|
</varlistentry>
|
|
</variablelist>
|
|
|
|
</para>
|
|
|
|
<example id="clutter-timeline-example">
|
|
<para>
|
|
The following example demonstrates an ellipse behaviour in action.
|
|
</para>
|
|
<programlisting>
|
|
#include <clutter/clutter.h>
|
|
|
|
int
|
|
main (int argc, char *argv[])
|
|
{
|
|
ClutterTimeline *timeline;
|
|
ClutterBehaviour *behave;
|
|
ClutterAlpha *alpha;
|
|
ClutterActor *stage, *actor;
|
|
GdkPixbuf *pixbuf;
|
|
|
|
clutter_init (&argc, &argv);
|
|
|
|
stage = clutter_stage_get_default ();
|
|
|
|
pixbuf = gdk_pixbuf_new_from_file ("ohpowers.png", NULL);
|
|
|
|
actor = clutter_texture_new_from_pixbuf (pixbuf);
|
|
|
|
clutter_container_add_actor (CLUTTER_CONTAINER (stage), actor);
|
|
|
|
timeline = clutter_timeline_new_for_duration (4000); /* milliseconds */
|
|
clutter_timeline_set_loop (timeline, TRUE);
|
|
|
|
/* Set an alpha func to power the behaviour */
|
|
alpha = clutter_alpha_new_full (timeline,
|
|
CLUTTER_ALPHA_SINE,
|
|
NULL, NULL);
|
|
|
|
behave = clutter_behaviour_ellipse_new (alpha,
|
|
200, /* center x */
|
|
200, /* center y */
|
|
400, /* width */
|
|
300, /* height */
|
|
CLUTTER_ROTATE_CW, /* direction */
|
|
0.0, /* initial angle */
|
|
360.0); /* final angle */
|
|
|
|
clutter_behaviour_apply (behave, actor);
|
|
|
|
clutter_actor_show_all (stage);
|
|
|
|
clutter_timeline_start (timeline);
|
|
|
|
clutter_main();
|
|
|
|
/* clean up */
|
|
g_object_unref (behave);
|
|
g_object_unref (timeline);
|
|
|
|
return 0;
|
|
}
|
|
|
|
</programlisting>
|
|
</example>
|
|
|
|
<note>Behaviour parameters can be changed whilst a animation is running</note>
|
|
|
|
<para>
|
|
There can be many #ClutterAlpha's attached to a single timeline. There
|
|
can be many behaviours for a #ClutterAlpha. There can be many behaviours
|
|
applied to an actor. A #ClutterScore can be used to chain many behaviour
|
|
together.
|
|
</para>
|
|
|
|
<warning><para>Combining behaviours that effect the same actor properties
|
|
(i.e two separate paths) will cause unexpected results. The values
|
|
will not be merged in any way with essentially a the last applied
|
|
behaviour taking precedence.</para></warning>
|
|
|
|
<para>
|
|
Tips for implementing a new behaviour can be found <link
|
|
linkend="creating-your-own-behaviours">here</link>.
|
|
</para>
|
|
|
|
</section>
|
|
<section id="clutter-animation-effects">
|
|
<title>Effects</title>
|
|
|
|
<para>
|
|
|
|
Clutter effects API provide a simplified abstraction for firing simple
|
|
transitions from code. Clutter effects are created from a
|
|
#ClutterEffectTemplate which is an abstraction of a timeline and
|
|
an alpha function. An effect template can be created with:
|
|
|
|
</para>
|
|
<programlisting>
|
|
ClutterEffectTemplate *etemplate =
|
|
clutter_effect_template_new_for_duration (2000, CLUTTER_ALPHA_RAMP_INC);
|
|
</programlisting>
|
|
<para>
|
|
|
|
This will create an effect template lasting 2000 milliseconds (2
|
|
seconds) and use an alpha function of %CLUTTER_ALPHA_RAMP_INC, there
|
|
are other more advanced forms for creating effect templates from
|
|
existing timelines, as well as attaching a callback to be called
|
|
with user_data when the effect template is destroyed.
|
|
|
|
</para>
|
|
<para>
|
|
|
|
When we have an effect template we can create a temporary behaviour
|
|
animating an actor simply by issuing:
|
|
|
|
</para>
|
|
<programlisting>
|
|
clutter_effect_move (etemplate, actor, 23, 42, NULL, NULL);
|
|
</programlisting>
|
|
<para>
|
|
and the actor will move from its current position to the coordinates
|
|
(23, 42) in 2 seconds. Effects can also be stacked, so calling:
|
|
</para>
|
|
<programlisting>
|
|
clutter_effect_move (etemplate, actor, 23, 42, NULL, NULL);
|
|
clutter_effect_fade (etemplate, actor, 0, NULL, NULL);
|
|
</programlisting>
|
|
<para>
|
|
The actor will move and fade out at the same time.
|
|
</para>
|
|
<para>
|
|
|
|
Since effects return a #ClutterTimeline, you can stop an effect from
|
|
immediatly happening by calling clutter_timeline_stop () on the
|
|
returned timeline.
|
|
|
|
</para>
|
|
<para>
|
|
|
|
The timeline and all the effect infrastructure is unreferenced as soon
|
|
as the timeline emits the ClutterTimeline::completed signal.
|
|
|
|
</para>
|
|
</section>
|
|
|
|
<section id="clutter-animation-conclusion">
|
|
<title>Conclusion</title>
|
|
<para>
|
|
|
|
Clutter provides a number of utility classes to aid animations and
|
|
complex animations can be produced by combining the various features
|
|
provided.
|
|
|
|
</para>
|
|
<para>
|
|
|
|
Of course animations can be created outside of Clutter Utilities,
|
|
they are not expected to cover every kind of possible animation
|
|
scenario.
|
|
|
|
</para>
|
|
<para>
|
|
|
|
The animation functionality in Clutter is primarily suited to
|
|
building animations with a set or finite running time - i.e transitions
|
|
and the like. For animations involving variable input (such as touchscreen
|
|
handling) physical simulations may be more suited.
|
|
|
|
</para>
|
|
|
|
</section>
|
|
</chapter>
|