1 #include <clutter/clutter.h>
2 #include <cairo/cairo.h>
5 #define PADDLE_SIZE 48.0
6 #define PADDLE_THICKNESS 8.0
9 #define DASH_LENGTH 12.0
10 #define ARENA_WIDTH 320.0
11 #define ARENA_HEIGHT 240.0
13 #define MINPOS (PADDLE_THICKNESS * 2)
14 #define MAXPOS (ARENA_HEIGHT - PADDLE_SIZE - (PADDLE_THICKNESS * 2))
17 * NOTE: This is a completely brain-dead way to implement pong, but helped
18 * me familiarise with Clutter paths and such.
21 static const ClutterColor green = { 0x0, 0xff, 0x0, 0xff };
27 ClutterActor *paddle1;
34 ClutterActor *paddle2;
38 /* Paddle independent */
43 ClutterTimeline *timeline;
45 ClutterBehaviour *behaviour;
53 pong_arena_actor_create (PongData *data)
55 ClutterActor *group, *actor;
59 group = clutter_group_new ();
62 actor = clutter_rectangle_new_with_color (&green);
63 geom.x = 0; geom.y = 0;
64 geom.width = ARENA_WIDTH; geom.height = PADDLE_THICKNESS;
65 clutter_actor_set_geometry (actor, &geom);
66 clutter_actor_show (actor);
67 clutter_group_add (CLUTTER_GROUP (group), actor);
70 actor = clutter_rectangle_new_with_color (&green);
71 geom.y = ARENA_HEIGHT - PADDLE_THICKNESS;
72 clutter_actor_set_geometry (actor, &geom);
73 clutter_actor_show (actor);
74 clutter_group_add (CLUTTER_GROUP (group), actor);
76 /* Dotted line down the middle */
77 geom.x = (ARENA_WIDTH / 2) - (PADDLE_THICKNESS / 2);
78 geom.width = PADDLE_THICKNESS;
79 geom.height = PADDLE_THICKNESS * 2;
80 for (i = 0; i < ARENA_HEIGHT / (PADDLE_THICKNESS * 2); i+= 2) {
81 geom.y = i * PADDLE_THICKNESS * 2;
82 actor = clutter_rectangle_new_with_color (&green);
83 clutter_actor_set_geometry (actor, &geom);
84 clutter_actor_show (actor);
85 clutter_group_add (CLUTTER_GROUP (group), actor);
92 pong_ball_actor_create (PongData *data)
94 ClutterActor *actor, *group;
97 actor = clutter_cairo_texture_new (BALL_SIZE, BALL_SIZE);
98 cr = clutter_cairo_texture_create (CLUTTER_CAIRO_TEXTURE (actor));
101 cairo_set_operator (cr, CAIRO_OPERATOR_CLEAR);
104 cairo_set_operator (cr, CAIRO_OPERATOR_OVER);
105 cairo_set_source_rgba (cr, 0, 1.0, 0, 1.0);
108 cairo_arc (cr, BALL_SIZE/2.0, BALL_SIZE/2.0,
109 BALL_SIZE/2.0, 0, 2*M_PI);
114 group = clutter_group_new ();
115 clutter_group_add (CLUTTER_GROUP (group), actor);
116 clutter_actor_set_position (actor, -BALL_SIZE/2, -BALL_SIZE/2);
117 clutter_actor_show (actor);
122 static ClutterActor *
123 pong_paddle_actor_create (PongData *data)
125 ClutterGeometry geom;
128 actor = clutter_rectangle_new_with_color (&green);
129 geom.x = 0; geom.y = 0;
130 geom.width = PADDLE_THICKNESS;
131 geom.height = PADDLE_SIZE;
132 clutter_actor_set_geometry (actor, &geom);
138 pong_ball_path_calculate (PongData *data, gdouble ax, gdouble ay, gdouble w,
141 gdouble x, y, dx, dy, angle;
143 x = clutter_actor_get_x (data->ball);
144 y = clutter_actor_get_y (data->ball);
149 /* Work out destination */
150 if (data->angle < M_PI * 0.5) {
151 /* Travelling up-right */
152 dx = x + (tan (data->angle) * (y - ay - (BALL_SIZE/2)));
153 if (dx > w - (BALL_SIZE/2)) {
154 dx = w - (BALL_SIZE/2);
155 dy = y - ((dx - x) / tan (data->angle));
157 dy = ay + (BALL_SIZE / 2);
159 } else if (data->angle < M_PI) {
160 /* Travelling down-right */
161 angle = M_PI - data->angle;
162 dx = x + (tan (angle) * (h - y));
163 if (dx > w - (BALL_SIZE/2)) {
164 dx = w - (BALL_SIZE/2);
165 dy = y + ((dx - x) / tan (angle));
167 dy = h - (BALL_SIZE/2);
169 } else if (data->angle < M_PI * 1.5) {
170 /* Travelling down-left */
171 angle = data->angle - M_PI;
172 dx = x - (tan (angle) * (h - y));
173 if (dx < (BALL_SIZE/2)) {
175 dy = y + ((x - ax) / tan (angle));
177 dy = h - (BALL_SIZE/2);
180 /* Travelling up-left */
181 angle = (M_PI * 2) - data->angle;
182 dx = x - (tan (angle) * (y - ay - (BALL_SIZE/2)));
183 if (dx < (BALL_SIZE/2)) {
185 dy = y - ((x - ax) / tan (angle));
187 dy = ay + (BALL_SIZE / 2);
191 clutter_timeline_set_duration (data->timeline, MAX (1000/FPS,
192 (guint)(1000 * (ABS (dx - x)/w) * data->speed)));
193 data->end.x = (gint)dx;
194 data->end.y = (gint)dy;
198 pong_path_end_cb (ClutterTimeline *timeline,
201 /* Figure out the new angle of the ball after a collision */
204 x = clutter_actor_get_x (data->ball);
205 y = clutter_actor_get_y (data->ball);
207 /*g_debug ("%d, %d, %lf", x, y, data->angle);*/
209 /* Work out new travel angle after collisions */
210 if ((x >= (ARENA_WIDTH - (BALL_SIZE/2))) ||
211 (x <= (BALL_SIZE/2)))
212 data->angle = -data->angle;
214 while (data->angle > M_PI*2) data->angle -= M_PI*2;
215 while (data->angle < 0) data->angle += M_PI*2;
217 if (y <= PADDLE_THICKNESS + (BALL_SIZE/2)) {
218 if (data->angle < M_PI * 0.5) {
219 data->angle = M_PI - data->angle;
220 } else if (data->angle > M_PI * 1.5) {
222 ((M_PI * 2.0) - data->angle);
224 } else if (y >= ARENA_HEIGHT - PADDLE_THICKNESS -
226 if (data->angle < M_PI) {
227 data->angle = M_PI - data->angle;
228 } else if (data->angle < M_PI * 1.5) {
229 data->angle = (M_PI * 2.0) -
230 (data->angle - M_PI);
234 while (data->angle > M_PI*2) { data->angle -= M_PI*2; }
235 while (data->angle < 0) { data->angle += M_PI*2; }
237 pong_ball_path_calculate (data,
240 ARENA_HEIGHT - PADDLE_THICKNESS);
242 clutter_path_clear (data->path);
243 clutter_path_add_move_to (data->path, data->start.x, data->start.y);
244 clutter_path_add_line_to (data->path, data->end.x, data->end.y );
246 clutter_timeline_start (data->timeline);
248 /*g_debug ("%d, %d, %lf", data->end.x,
249 data->end.y, data->angle);*/
253 pong_key_press_event_cb (ClutterStage *stage, ClutterEvent *event,
256 guint key_symbol = clutter_event_get_key_symbol (event);
258 if ((key_symbol != CLUTTER_p) && (data->pause)) {
260 clutter_timeline_start (data->timeline);
267 clutter_main_quit ();
282 data->pause = !data->pause;
284 clutter_timeline_pause (data->timeline);
286 clutter_timeline_start (data->timeline);
293 pong_key_release_event_cb (ClutterStage *stage, ClutterEvent *event,
296 guint key_symbol = clutter_event_get_key_symbol (event);
318 pong_new_frame_cb (ClutterTimeline *timeline, gint frame_num,
321 if (data->up1 ^ data->down1) {
322 data->position1 = MAX (MINPOS, MIN (MAXPOS, data->position1 +
323 (data->down1 ? PADDLE_SPEED : 0) -
324 (data->up1 ? PADDLE_SPEED : 0)));
325 clutter_actor_set_position (data->paddle1, PADDLE_THICKNESS,
329 if (data->up2 ^ data->down2) {
330 data->position2 = MAX (MINPOS, MIN (MAXPOS, data->position2 +
331 (data->down2 ? PADDLE_SPEED : 0) -
332 (data->up2 ? PADDLE_SPEED : 0)));
333 clutter_actor_set_position (data->paddle2, ARENA_WIDTH -
334 (PADDLE_THICKNESS * 2), data->position2);
339 main (int argc, char **argv)
343 const ClutterColor stage_color = { 0x00, 0x00, 0x00, 0xff };
345 clutter_init (&argc, &argv);
347 data.arena = pong_arena_actor_create (&data);
348 data.paddle1 = pong_paddle_actor_create (&data);
349 data.paddle2 = pong_paddle_actor_create (&data);
350 data.ball = pong_ball_actor_create (&data);
352 clutter_actor_set_position (data.paddle1, PADDLE_THICKNESS,
353 PADDLE_THICKNESS * 2);
354 clutter_actor_set_position (data.paddle2,
355 ARENA_WIDTH - (PADDLE_THICKNESS * 2),
356 PADDLE_THICKNESS * 2);
366 data.timeline = clutter_timeline_new (2000);
367 data.alpha = clutter_alpha_new_full (data.timeline, CLUTTER_LINEAR);
368 data.path = clutter_path_new();
369 data.behaviour = clutter_behaviour_path_new (data.alpha, data.path);
371 data.angle = ((M_PI * 1.8));
374 clutter_actor_set_position (data.ball, ARENA_WIDTH/2, ARENA_HEIGHT/2);
376 stage = clutter_stage_get_default ();
377 clutter_stage_set_color (CLUTTER_STAGE (stage), &stage_color);
379 clutter_group_add (CLUTTER_GROUP (stage), data.arena);
380 clutter_group_add (CLUTTER_GROUP (stage), data.paddle1);
381 clutter_group_add (CLUTTER_GROUP (stage), data.paddle2);
382 clutter_group_add (CLUTTER_GROUP (stage), data.ball);
384 clutter_actor_show_all (CLUTTER_ACTOR (stage));
385 clutter_actor_set_scale (CLUTTER_ACTOR (stage),
386 CLUTTER_STAGE_WIDTH () / ARENA_WIDTH,
387 CLUTTER_STAGE_HEIGHT () / ARENA_HEIGHT);
389 pong_ball_path_calculate (&data,
392 ARENA_HEIGHT - PADDLE_THICKNESS);
393 clutter_behaviour_apply (data.behaviour, data.ball);
395 clutter_path_add_move_to (data.path, data.start.x, data.start.y);
396 clutter_path_add_line_to (data.path, data.end.x, data.end.y );
398 g_signal_connect_after (data.timeline, "completed",
399 G_CALLBACK (pong_path_end_cb), &data);
400 g_signal_connect (data.timeline, "new_frame",
401 G_CALLBACK (pong_new_frame_cb), &data);
403 g_signal_connect (stage, "key-press-event",
404 G_CALLBACK (pong_key_press_event_cb), &data);
405 g_signal_connect (stage, "key-release-event",
406 G_CALLBACK (pong_key_release_event_cb), &data);