根据教程,我们应学习ClutterAnimation,但是在这里我碰到了一些问题,我不确定是否是Clutter1.0的问题,从某种意义上看,Clutter0.9比Clutter1.0更为稳定。我们已经一而再地讨论了clutter的兼容,但是我想仍需要再三讨论这个问题。现在,我们学习类似的处理效果ClutterBehaviour,我认为所有ClutterAnmation均可以通过ClutterBehaviour来进行处理。消灭问题或者绕开问题,一直是中国传统哲学的一个特点。
学习资料来源: Using Behaviour , Example ,以及clutter 1.0的联机文档。
在ClutterTimeline中,可以根据new-frame触发来进行动态图像处理,但是new-frame的触发频率依赖于monitor,我们不能精确地控制动作发生的时间过程。而ClutterBehaviour提供一种在指定时间内完成某个动作的方式。
下面是一个例子,我们去除了具体的动作效果,先讨论ClutterBehaviour如何工作。我们提供了两个动态效果,例如如果图像从大到小,那么另一个效果就是从小到大,已是的整个动作连贯。
#include <clutter/clutter.h>
#include <stdlib.h>int main(int argc,char * argv[]){
ClutterColor stage_color = { 0x00, 0x00, 0x00, 0xff };
clutter_init (&argc, &argv);/* Get the stage and set its size and color: */
ClutterActor *stage = clutter_stage_get_default ();
clutter_actor_set_size (stage, 800, 600);
clutter_stage_set_color (CLUTTER_STAGE (stage), &stage_color);/* Add a rectangle to the stage: */
ClutterActor * rect = clutter_texture_new_from_file("4.png",NULL);
clutter_actor_set_position (rect, 200, 200);
clutter_container_add_actor (CLUTTER_CONTAINER (stage), rect);
clutter_actor_show (rect);/* Show the stage: */
clutter_actor_show (stage);/* 加两个顺序执行的timeline,周期均为10秒 */
ClutterScore * score = clutter_score_new();
clutter_score_set_loop(score,TRUE);
ClutterTimeline *timeline1 = clutter_timeline_new(10000);
ClutterTimeline *timeline2 = clutter_timeline_new(10000);
clutter_score_append(score,NULL,timeline1);
clutter_score_append(score,timeline1,timeline2);/* ClutterAlpha是个很有趣的参数,简单说他将某个运动的模式和timeline进行捆绑,例如我们定义在某个timeline的有效时间内,动态的效果是线性的。如果这个效果作用于一个移动的动作(在下面定义),则表示匀速运动,如果亮度的渐变方式,表示均速变化。Clutter提供了丰富的模式,详细可以参见联机文档:/usr/share/gtk-doc/html/clutter/clutter-Implicit-Animations.html#CLUTTER-LINEAR--CAPS。*/
ClutterAlpha *alpha1 = clutter_alpha_new_full (timeline1, CLUTTER_LEANAR );
ClutterAlpha *alpha2 = clutter_alpha_new_full (timeline2, CLUTTER_EASE_OUT_BOUNCE);
g_object_unref (timeline1);
g_object_unref (timeline2);/* … …,这里我们创建两个动作behaviour1和behaviour2,具体在后面讨论,先略去*/
/* 将某个动作和具体的actor关联。这个actor将在timeline的有效时间内,根据运动模型,进行具体的动作*/
clutter_behaviour_apply (behaviour1, rect);
clutter_behaviour_apply (behaviour2, rect);
clutter_score_start(score);clutter_main ();
return EXIT_SUCCESS;
}
Clutter提供了六种的动作方式,具体的可以参见联机文档,我们在下面分别讨论:
这里给出两种动作,一种是椭圆运动方式。如果我们想使用逆时针旋转,方向设置为CLUTTER_ROTATE_CCW,但是初始角度要大于最后角度,应设置为……,CLUTTER_ROTATE_CCW,360.0,0);请注意角度是不能超过360度,如果需要连续旋转两周,可以设置两个timeline,分别旋转1周。后一种是Z轴的depth,但是在平面的图像中,我确实比较难理解Z的变化,从现在上看类似于Scale,而且不会小于图像本上大小。
ClutterBehaviour *behaviour1 = clutter_behaviour_ellipse_new ( alpha1,
300, /* center x */
200, /* center y */
400, /* width */
300, /* height */
CLUTTER_ROTATE_CW, /* direction */
0.0, /* initial angle */
360.0); /* final angle */ClutterBehaviour *behaviour2 = clutter_behaviour_depth_new ( alpha2,
200,
20
);
一个clutter的可能bug的处理 :如果我们不是要求马上进行动态运动,即我们不在main中调用clutter_score_start(),而是在程序的其他地方,例如某个回调函数,在检测到用户某个实现后触发,我们会发现actor并不在我们期待的位置。不在我们创建actor时指定的问题,也不在我们运动要求的初始位置。对于运动,clutter的默认初始位置为(0,0),如果是椭圆运动,默认运动中心为(0,0),根据我们运动范围和初始角度,将位置设置为(200,0),这显然不是我们所期待。在这种情况下,我们可以在 clutter_behaviour_apply后面加上clutter_actor_set_position(rect,500,200)来修正我们的位置。 我不实际了解depth动作的具体含义,这个不清除如何矫正。但是在这个例子中他的作用相当于scale,我们用scale的变化方式替代,scale是可以修正的。
下面是亮度渐变的方式。从暗到亮,在从亮到暗,循环进行。
ClutterBehaviour *behaviour1 = clutter_behaviour_opacity_new( alpha1,40,255);
ClutterBehaviour *behaviour2 = clutter_behaviour_opacity_new( alpha2,255,40);
路径移动,可以进行直线移动,也可以进行曲线移动。请注意clutter_path_add_move_to是非常重要的。如果不设置,初始位置为(0,0)。这是耗费了小时计算的时间才找到的解决方法。
ClutterPath * path1 = clutter_path_new ();
clutter_path_add_move_to (path1,100,100);
clutter_path_add_line_to (path1,600,400);
ClutterBehaviour *behaviour1 = clutter_behaviour_path_new(alpha1,path1);
ClutterPath * path2 = clutter_path_new();
clutter_path_add_move_to(path2,600,400);
clutter_path_add_curve_to(path2,400,500,200,500,100,100);
ClutterBehaviour *behaviour2 = clutter_behaviour_path_new( alpha2,path2);
指定移动的路线,可以设置多个点,移动的方式是直线
ClutterKnot knot1[3],knot2[2];
knot1[0].x = 50;
knot1[0].y = 50;
knot1[1].x = 100;
knot1[1].y = 300;
knot1[2].x= 600;
knot1[2].y= 400;
knot2[0].x = 600;
knot2[0].y = 400;
knot2[1].x= 50;
knot2[1].y= 50;ClutterBehaviour *behaviour1 = clutter_behaviour_path_new_with_knots( alpha1,knot1,3);
ClutterBehaviour *behaviour2 = clutter_behaviour_path_new_with_knots( alpha2,knot2,2);
旋转,同样如果需要反时针转动,设置为CLUTTER_ROTATE_CCW,并且初始角度大于最终角度。
ClutterBehaviour *behaviour1 = clutter_behaviour_rotate_new( alpha1,CLUTTER_Y_AXIS,CLUTTER_ROTATE_CW,0,360.0);
clutter_behaviour_rotate_set_center((ClutterBehaviourRotate *)behaviour1,20,0,50);
ClutterBehaviour *behaviour2 = clutter_behaviour_rotate_new( alpha2,CLUTTER_X_AXIS,CLUTTER_ROTATE_CW,0,360.0);
大小变化,这个比较简单。
ClutterBehaviour *behaviour1 = clutter_behaviour_scale_new(alpha1,0.5,0.5,3,2);
ClutterBehaviour *behaviour2 = clutter_behaviour_scale_new(alpha2,3,2,0.5,0.5);
关于动态效果 :
下面的这个图很好地表明的动态效果和时间轴的关系。
各类的动态效果,可以看下图,来自联机文档:
附:可能的一些bug
ClutterBehavior初始位置的设定可能存在bug。例如clutter_behaviour_ellipse_new,这是个椭圆运动,无论我们如何设置椭圆的中心,clutter会按照中心为(0,0),根据范围,和初始角度设置初始位置。同样对于直线运动方式,初始会设置在(0,0),都需要修正。
如果我们将动作变化在main函数中触发貌似正常,但是如果我们屏蔽了时间轴(或者时间轴集)的启动,就很清楚地看到clutterBehavior设置的初始位置,显然不是我们预计的。
如果我们在callback函数中设置clutterBehavior或者启动时间轴,就会在触发的时候,在clutter自认为的初始位置中闪现,除非我们在clutter_behaviour_apply后面用set_position来进行修正。这等于需要我们重新计算位置。
如果变化不是位置的变化,例如depth的变化,那么在我们apply的时候就设置为会初始化,而不是在时间轴启动的时候才进行。这会影响我们的实现,因为变化可能是ClutterScore非第一个时间轴,例如是第二个时间轴,他将会在score启动的时候,在其初始状态上进行闪现。这会严重影响我们的效果。因此需要避免相关情况。
这是一个测试的例子:
#include <clutter/clutter.h>
#include <stdlib.h>
ClutterActor *rect = NULL;
ClutterBehaviour *behaviour1 = NULL , * behaviour2 = NULL;
ClutterScore * score = NULL;
void set_motion(){
score = clutter_score_new();
clutter_score_set_loop(score,TRUE);
ClutterTimeline *timeline1 = clutter_timeline_new(10000);
ClutterTimeline *timeline2 = clutter_timeline_new(10000);
clutter_score_append(score,NULL,timeline1);
//clutter_score_append(score,timeline1,timeline2);
ClutterAlpha *alpha1 = clutter_alpha_new_full (timeline1,
CLUTTER_EASE_IN_OUT_QUAD);//CLUTTER_EASE_IN_SINE);
ClutterAlpha *alpha2 = clutter_alpha_new_full (timeline2,
CLUTTER_EASE_OUT_BOUNCE);//CLUTTER_EASE_IN_SINE);
g_object_unref (timeline1);
//g_object_unref (timeline2);
ClutterBehaviour *behaviour1 = clutter_behaviour_ellipse_new(
alpha1,
300, /* center x */
200, /* center y */
400, /* width */
300, /* height */
CLUTTER_ROTATE_CCW, /* direction */
360.0, /* initial angle */
0.0); /* final angle */
ClutterBehaviour *behaviour2 = clutter_behaviour_depth_new(
alpha2,
200,
20
);
clutter_behaviour_apply (behaviour1, rect);
//clutter_behaviour_apply (behaviour2, rect);
clutter_actor_set_position(rect,500,200);
clutter_score_start(score);
}
static gboolean on_key_press(ClutterStage * stage ,
ClutterEvent * event,
gpointer *user_data){
float x = 0;
float y = 0;
clutter_actor_get_position (rect, &x, &y);
g_print ("rect at (%f, %f)/n", x,y);
set_motion();
return TRUE;
}
int main(int argc,char * argv[]){
ClutterColor stage_color = { 0x00, 0x00, 0x00, 0xff };
//ClutterColor rect_color = { 0xff, 0xff, 0xff, 0x99 };
clutter_init (&argc, &argv);
/* Get the stage and set its size and color: */
ClutterActor *stage = clutter_stage_get_default ();
clutter_actor_set_size (stage, 800, 600);
clutter_stage_set_color (CLUTTER_STAGE (stage), &stage_color);
/* Add a rectangle to the stage: */
//rect = clutter_rectangle_new_with_color (&rect_color);
//clutter_actor_set_size (rect, 40, 40);
rect = clutter_texture_new_from_file("4.png",NULL);
clutter_actor_set_position (rect, 500, 200);
clutter_container_add_actor (CLUTTER_CONTAINER (stage), rect);
clutter_actor_show (rect);
/* Show the stage: */
clutter_actor_show (stage);
g_signal_connect(stage,"key_press_event",G_CALLBACK(on_key_press),NULL);
//set_motion();
clutter_main ();
return EXIT_SUCCESS;
}
相关链接:
我的Clutter相关博客