Updated demos to use DALi clang-format
[platform/core/uifw/dali-demo.git] / examples / blocks / blocks-example.cpp
1 /*
2  * Copyright (c) 2020 Samsung Electronics Co., Ltd.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  *
16  */
17
18 #include <algorithm>
19 #include <iostream>
20 #include <map>
21 #include <sstream>
22 #include <string>
23
24 #include <dali-toolkit/dali-toolkit.h>
25 #include <dali-toolkit/devel-api/visuals/visual-properties-devel.h>
26 #include <dali/dali.h>
27 #include "shared/view.h"
28
29 using namespace Dali;
30 using namespace Dali::Toolkit;
31 using namespace DemoHelper;
32
33 namespace
34 {
35 const char* BACKGROUND_IMAGE(DEMO_IMAGE_DIR "background-blocks.jpg");
36 const char* TOOLBAR_IMAGE(DEMO_IMAGE_DIR "top-bar.png");
37 const char* APPLICATION_TITLE("DALi Blocks");
38 const char* BALL_IMAGE          = DEMO_IMAGE_DIR "blocks-ball.png";
39 const char* PADDLE_IMAGE        = DEMO_IMAGE_DIR "blocks-paddle.png";
40 const char* PADDLE_HANDLE_IMAGE = DEMO_IMAGE_DIR "blocks-paddle-handle.png";
41
42 const char* BRICK_IMAGE_PATH[] = {DEMO_IMAGE_DIR "blocks-brick-1.png",
43                                   DEMO_IMAGE_DIR "blocks-brick-2.png",
44                                   DEMO_IMAGE_DIR "blocks-brick-3.png",
45                                   DEMO_IMAGE_DIR "blocks-brick-4.png"};
46
47 const int     TOTAL_BRICKS(4); ///< Total bricks in game.
48 const Vector3 ICON_SIZE(100.0f, 100.0f, 0.0f);
49
50 const float   SCREEN_MARGIN    = 10.0f;                       ///< Margin indentation around screen
51 const Vector3 MENU_BUTTON_SIZE = Vector3(0.15f, 0.05f, 1.0f); ///< Standard Menu Buttons.
52
53 const float   MAX_ANIMATION_DURATION = 60.0f;            ///< 60 seconds animations. Long enough for ball to hit an obstacle.
54 const float   BALL_VELOCITY          = 300.0f;           ///< Ball velocity in pixels/second.
55 const float   MAX_VELOCITY           = 500.0f;           ///< Max. velocity in pixels/second.
56 const Vector3 PADDLE_COLLISION_MARGIN(0.0f, 0.0f, 0.0f); ///< Collision margin for ball-paddle detection.
57 const Vector3 BRICK_COLLISION_MARGIN(0.0f, 0.0f, 0.0f);  ///< Collision margin for ball-brick detection.
58 const Vector3 INITIAL_BALL_DIRECTION(1.0f, 1.0f, 0.0f);  ///< Initial ball direction.
59
60 const std::string WOBBLE_PROPERTY_NAME("wobbleProperty");       ///< Wobble property name.
61 const std::string COLLISION_PROPERTY_NAME("collisionProperty"); ///< Collision property name.
62
63 const Vector2 BRICK_SIZE(0.1f, 0.05f);           ///< Brick size relative to width of window.
64 const Vector2 BALL_SIZE(0.05f, 0.05f);           ///< Ball size relative to width of window.
65 const Vector2 PADDLE_SIZE(0.2f, 0.05f);          ///< Paddle size relative to width of window.
66 const Vector2 PADDLE_HANDLE_SIZE(0.3f, 0.3f);    ///< Paddle handle size relative to width of window.
67 const Vector2 BALL_START_POSITION(0.5f, 0.8f);   ///< Ball start position relative to window size.
68 const Vector2 PADDLE_START_POSITION(0.5f, 0.9f); ///< Paddler start position relative to window size.
69 const Vector2 PADDLE_HIT_MARGIN(0.1, 0.15f);     ///< Extra hit Area for Paddle when touching.
70
71 const int TOTAL_LIVES(3);  ///< Total lives in game before it's game over!
72 const int TOTAL_LEVELS(3); ///< 3 Levels total, then repeats.
73
74 // constraints ////////////////////////////////////////////////////////////////
75
76 /**
77  * CollisionCircleRectangleConstraint generates a collision vector
78  * between two actors a (circle) and b (rectangle)
79  */
80 struct CollisionCircleRectangleConstraint
81 {
82   /**
83    * Collision Constraint constructor
84    * The adjust (optional) parameter can be used to add a margin
85    * to the actors. A +ve size will result in larger collisions,
86    * while a -ve size will result in tighter collisions.
87    *
88    * @param[in] adjustPosition (optional) Adjusts the position offset of detection
89    * @param[in] adjustSize (optional) Adjusts the rectangular size of detection
90    */
91   CollisionCircleRectangleConstraint(Vector3 adjustPosition = Vector3::ZERO,
92                                      Vector3 adjustSize     = Vector3::ZERO)
93   : mAdjustPosition(adjustPosition),
94     mAdjustSize(adjustSize)
95   {
96   }
97
98   /**
99    * Generates collision vector indicating whether Actor's A and B
100    * have overlapped eachother, and the relative position of Actor B to A.
101    *
102    * @param[in,out] current The current collision-property
103    * @param[in] inputs Contains:
104    *                    Actor A's Position property.
105    *                    Actor B's Position property.
106    *                    Actor A's Size property.
107    *                    Actor B's Size property.
108    * @return The collision vector is returned.
109    */
110   void operator()(Vector3& current, const PropertyInputContainer& inputs)
111   {
112     const Vector3& a      = inputs[0]->GetVector3();
113     const Vector3  b      = inputs[1]->GetVector3() + mAdjustPosition;
114     const Vector3& sizeA  = inputs[2]->GetVector3();
115     const Vector3& sizeB  = inputs[3]->GetVector3();
116     const Vector3  sizeA2 = sizeA * 0.5f;                 // circle radius
117     const Vector3  sizeB2 = (sizeB + mAdjustSize) * 0.5f; // rectangle half rectangle.
118
119     // get collision relative to a (rectangle).
120     Vector3 delta = a - b;
121
122     // reduce rectangle to 0.
123     if(delta.x > sizeB2.x)
124     {
125       delta.x -= sizeB2.x;
126     }
127     else if(delta.x < -sizeB2.x)
128     {
129       delta.x += sizeB2.x;
130     }
131     else
132     {
133       delta.x = 0;
134     }
135
136     if(delta.y > sizeB2.y)
137     {
138       delta.y -= sizeB2.y;
139     }
140     else if(delta.y < -sizeB2.y)
141     {
142       delta.y += sizeB2.y;
143     }
144     else
145     {
146       delta.y = 0;
147     }
148
149     // now calculate collision vector vs origin. (assume A is a circle, not ellipse)
150     if(delta.Length() < sizeA2.x)
151     {
152       delta.Normalize();
153       current = delta;
154     }
155     else
156     {
157       current = Vector3::ZERO;
158     }
159   }
160
161   const Vector3 mAdjustPosition; ///< Position Adjustment value
162   const Vector3 mAdjustSize;     ///< Size Adjustment value
163 };
164
165 /**
166  * WobbleConstraint generates a decaying sinusoidial rotation.
167  * The result when applied to an Actor, is the Actor rotating left/right
168  * initially a large amount (deviation degrees, when wobble property is 0.0f)
169  * then eventually coming to a stop (once wobble property reaches 1.0f)
170  */
171 struct WobbleConstraint
172 {
173   /**
174    * Wobble Constraint constructor
175    * Generates a sinusoidial rotation that starts with
176    * high amplitude (deviation), and then decays to zero over input 0.0f to 1.0f
177    *
178    * @param[in] deviation The max. deviation of wobble effect in degrees.
179    */
180   WobbleConstraint(Degree deviation)
181   : mDeviation(deviation)
182   {
183   }
184
185   /**
186    * @param[in,out] current The current rotation property
187    * @param[in] inputs Contains the wobble property (value from 0.0f to 1.0f)
188    * @return The rotation (quaternion) is generated.
189    */
190   void operator()(Quaternion& current, const PropertyInputContainer& inputs)
191   {
192     const float& wobble = inputs[0]->GetFloat();
193
194     float f = sinf(wobble * 10.0f) * (1.0f - wobble);
195
196     current = Quaternion(mDeviation * f, Vector3::ZAXIS);
197   }
198
199   Radian mDeviation; ///< Deviation factor in radians.
200 };
201
202 } // unnamed namespace
203
204 /**
205  * This example shows how to use PropertyNotifications
206  */
207 class ExampleController : public ConnectionTracker
208 {
209 public:
210   /**
211    * Constructor
212    * @param application Application class, stored as reference
213    */
214   ExampleController(Application& application)
215   : mApplication(application),
216     mView(),
217     mContentLayer(),
218     mBall(),
219     mBallStartPosition(),
220     mBallVelocity(),
221     mBallAnimation(),
222     mPaddle(),
223     mPaddleImage(),
224     mPaddleHandle(),
225     mPaddleHitMargin(),
226     mWobbleAnimation(),
227     mWobbleProperty(Property::INVALID_INDEX),
228     mLevelContainer(),
229     mBrickImageMap(),
230     mDragAnimation(),
231     mDragActor(),
232     mRelativeDragPoint(),
233     mDestroyAnimationMap(),
234     mPaddleFullSize(),
235     mLevel(0),
236     mLives(TOTAL_LIVES),
237     mBrickCount(0)
238
239   {
240     // Connect to the Application's Init and orientation changed signal
241     mApplication.InitSignal().Connect(this, &ExampleController::Create);
242   }
243
244   /**
245    * This method gets called once the main loop of application is up and running
246    * @param[in] application Reference to the application instance
247    */
248   void Create(Application& application)
249   {
250     application.GetWindow().KeyEventSignal().Connect(this, &ExampleController::OnKeyEvent);
251
252     // Creates a default view with a default tool bar.
253     // The view is added to the window.
254     Toolkit::ToolBar toolBar;
255     mContentLayer = DemoHelper::CreateView(application,
256                                            mView,
257                                            toolBar,
258                                            BACKGROUND_IMAGE,
259                                            TOOLBAR_IMAGE,
260                                            APPLICATION_TITLE);
261
262     // Add an extra space on the right to center the title text.
263     toolBar.AddControl(Actor::New(), DemoHelper::DEFAULT_VIEW_STYLE.mToolBarButtonPercentage, Toolkit::Alignment::HORIZONTAL_RIGHT);
264
265     // Create the content layer, which is where game actors appear.
266     AddContentLayer();
267   }
268
269 private:
270   /**
271    * Adds a new layer to the window, containing game actors.
272    */
273   void AddContentLayer()
274   {
275     Window        window = mApplication.GetWindow();
276     const Vector3 windowSize(window.GetSize());
277
278     // Ball setup
279     mBallStartPosition = windowSize * Vector3(BALL_START_POSITION);
280     mBall              = CreateImage(BALL_IMAGE);
281     mBall.SetProperty(Actor::Property::POSITION, mBallStartPosition);
282     mBall.SetProperty(Actor::Property::SIZE, BALL_SIZE * windowSize.width);
283     mContentLayer.Add(mBall);
284     mBallVelocity = Vector3::ZERO;
285
286     // Paddle setup
287     mPaddleHitMargin = Vector2(windowSize) * PADDLE_HIT_MARGIN;
288     mPaddle          = Actor::New();
289     mPaddleHandle    = CreateImage(PADDLE_HANDLE_IMAGE);
290     mPaddleImage     = CreateImage(PADDLE_IMAGE);
291     mPaddle.Add(mPaddleHandle);
292     mPaddle.Add(mPaddleImage);
293     mPaddleHandle.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::TOP_CENTER);
294     mPaddleHandle.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::TOP_CENTER);
295     mPaddleHandle.SetProperty(Actor::Property::POSITION, Vector2(0.0f, windowSize.width * 0.0125f));
296     mPaddleImage.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::TOP_CENTER);
297     mPaddleImage.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::TOP_CENTER);
298     mPaddle.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::TOP_LEFT);
299     mPaddle.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::CENTER);
300     mPaddleFullSize = PADDLE_SIZE * windowSize.width;
301     mPaddle.SetProperty(Actor::Property::SIZE, mPaddleFullSize + mPaddleHitMargin);
302     mPaddleHandle.SetProperty(Actor::Property::SIZE, PADDLE_HANDLE_SIZE * windowSize.width);
303     mPaddleImage.SetProperty(Actor::Property::SIZE, mPaddleFullSize);
304
305     mWobbleProperty             = mPaddle.RegisterProperty(WOBBLE_PROPERTY_NAME, 0.0f);
306     Constraint wobbleConstraint = Constraint::New<Quaternion>(mPaddle, Actor::Property::ORIENTATION, WobbleConstraint(Degree(10.0f)));
307     wobbleConstraint.AddSource(LocalSource(mWobbleProperty));
308     wobbleConstraint.Apply();
309
310     mPaddle.SetProperty(Actor::Property::POSITION, windowSize * Vector3(PADDLE_START_POSITION));
311     mContentLayer.Add(mPaddle);
312     mPaddle.TouchedSignal().Connect(this, &ExampleController::OnTouchPaddle);
313     mContentLayer.TouchedSignal().Connect(this, &ExampleController::OnTouchLayer);
314
315     const float margin(BALL_SIZE.width * windowSize.width * 0.5f);
316
317     // Set up notifications for ball's collisions against walls.
318     PropertyNotification leftNotification = mBall.AddPropertyNotification(Actor::Property::POSITION_X, LessThanCondition(margin));
319     leftNotification.NotifySignal().Connect(this, &ExampleController::OnHitLeftWall);
320
321     PropertyNotification rightNotification = mBall.AddPropertyNotification(Actor::Property::POSITION_X, GreaterThanCondition(windowSize.width - margin));
322     rightNotification.NotifySignal().Connect(this, &ExampleController::OnHitRightWall);
323
324     PropertyNotification topNotification = mBall.AddPropertyNotification(Actor::Property::POSITION_Y, LessThanCondition(margin));
325     topNotification.NotifySignal().Connect(this, &ExampleController::OnHitTopWall);
326
327     PropertyNotification bottomNotification = mBall.AddPropertyNotification(Actor::Property::POSITION_Y, GreaterThanCondition(windowSize.height + margin));
328     bottomNotification.NotifySignal().Connect(this, &ExampleController::OnHitBottomWall);
329
330     // Set up notification for ball colliding against paddle.
331     Actor delegate = Actor::New();
332     window.Add(delegate);
333     Property::Index property   = delegate.RegisterProperty(COLLISION_PROPERTY_NAME, Vector3::ZERO);
334     Constraint      constraint = Constraint::New<Vector3>(delegate, property, CollisionCircleRectangleConstraint(-Vector3(0.0f, mPaddleHitMargin.height * 0.575f, 0.0f), -Vector3(mPaddleHitMargin)));
335     constraint.AddSource(Source(mBall, Actor::Property::POSITION));
336     constraint.AddSource(Source(mPaddle, Actor::Property::POSITION));
337     constraint.AddSource(Source(mBall, Actor::Property::SIZE));
338     constraint.AddSource(Source(mPaddle, Actor::Property::SIZE));
339     constraint.Apply();
340
341     PropertyNotification paddleNotification = delegate.AddPropertyNotification(property, GreaterThanCondition(0.0f));
342     paddleNotification.NotifySignal().Connect(this, &ExampleController::OnHitPaddle);
343
344     RestartGame();
345   }
346
347   /**
348    * Restarts Game
349    * Resets Lives count and other stats, and loads level
350    */
351   void RestartGame()
352   {
353     mLives = TOTAL_LIVES;
354     mLevel = 0;
355     mBall.SetProperty(Actor::Property::POSITION, mBallStartPosition);
356     mBallVelocity = Vector3::ZERO;
357     mPaddle.SetProperty(Actor::Property::SIZE, mPaddleFullSize + mPaddleHitMargin);
358     mPaddleImage.SetProperty(Actor::Property::SIZE, mPaddleFullSize);
359
360     LoadLevel(mLevel);
361   }
362
363   /**
364    * Loads level
365    * All existing level content is removed, and new bricks
366    * are added.
367    * @param[in] level Level index to load.
368    */
369   void LoadLevel(int level)
370   {
371     if(mLevelContainer && mLevelContainer.GetParent() == mContentLayer)
372     {
373       mContentLayer.Remove(mLevelContainer);
374     }
375
376     mLevelContainer = Actor::New();
377     mLevelContainer.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::CENTER);
378     mLevelContainer.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER);
379     mLevelContainer.SetResizePolicy(ResizePolicy::FILL_TO_PARENT, Dimension::ALL_DIMENSIONS);
380
381     mContentLayer.Add(mLevelContainer);
382
383     mBrickCount = 0;
384
385     if(mBrickImageMap.Empty())
386     {
387       Vector2       windowSize(mApplication.GetWindow().GetSize());
388       const Vector2 brickSize(BRICK_SIZE * Vector2(windowSize.x, windowSize.x));
389
390       mBrickImageMap["desiredWidth"]  = static_cast<int>(brickSize.width);
391       mBrickImageMap["desiredHeight"] = static_cast<int>(brickSize.height);
392       mBrickImageMap["fittingMode"]   = "SCALE_TO_FILL";
393       mBrickImageMap["samplingMode"]  = "BOX_THEN_LINEAR";
394     }
395
396     switch(level % TOTAL_LEVELS)
397     {
398       case 0:
399       {
400         GenerateLevel0();
401         break;
402       }
403       case 1:
404       {
405         GenerateLevel1();
406         break;
407       }
408       case 2:
409       {
410         GenerateLevel2();
411         break;
412       }
413       default:
414       {
415         break;
416       }
417     } // end switch
418   }
419
420   /**
421    * Generates level 0
422    */
423   void GenerateLevel0()
424   {
425     Vector2       windowSize(mApplication.GetWindow().GetSize());
426     const Vector2 brickSize(BRICK_SIZE * windowSize.width);
427
428     const int     columns = (0.85f * windowSize.width) / brickSize.width;  // 85 percent of the width of the screen covered with bricks.
429     const int     rows    = (0.3f * windowSize.height) / brickSize.height; // 30 percent of the height of the screen covered with bricks.
430     const Vector2 offset((windowSize.x - (columns * brickSize.width)) * 0.5f,
431                          windowSize.y * 0.125f);
432
433     for(int j = 0; j < rows; j++)
434     {
435       for(int i = 0; i < columns; i++)
436       {
437         Actor brick = CreateBrick(Vector2(i * brickSize.width + offset.x, j * brickSize.height + offset.y) + (brickSize * 0.5f), j % TOTAL_BRICKS);
438         mLevelContainer.Add(brick);
439         mBrickCount++;
440       }
441     }
442   }
443
444   /**
445    * Generates level 1
446    */
447   void GenerateLevel1()
448   {
449     Vector2       windowSize(mApplication.GetWindow().GetSize());
450     const Vector2 brickSize(BRICK_SIZE * windowSize.width);
451
452     const int     columns = (0.85f * windowSize.width) / brickSize.width;  // 85 percent of the width of the screen covered with bricks.
453     const int     rows    = (0.3f * windowSize.height) / brickSize.height; // 30 percent of the height of the screen covered with bricks.
454     const Vector2 offset((windowSize.x - (columns * brickSize.width)) * 0.5f,
455                          windowSize.y * 0.125f);
456
457     for(int j = 0; j < rows; j++)
458     {
459       for(int i = 0; i < columns; i++)
460       {
461         int i2         = columns - i - 1;
462         int j2         = rows - j - 1;
463         int brickIndex = std::min(std::min(i, j), std::min(i2, j2)) % TOTAL_BRICKS;
464
465         Actor brick = CreateBrick(Vector2(i * brickSize.width + offset.x, j * brickSize.height + offset.y) + (brickSize * 0.5f), brickIndex);
466
467         mLevelContainer.Add(brick);
468         mBrickCount++;
469       }
470     }
471   }
472
473   /**
474    * Generates level 2
475    */
476   void GenerateLevel2()
477   {
478     Vector2       windowSize(mApplication.GetWindow().GetSize());
479     const Vector2 brickSize(BRICK_SIZE * windowSize.width);
480
481     const int     columns = (0.85f * windowSize.width) / brickSize.width;  // 85 percent of the width of the screen covered with bricks.
482     const int     rows    = (0.3f * windowSize.height) / brickSize.height; // 30 percent of the height of the screen covered with bricks.
483     const Vector2 offset((windowSize.x - (columns * brickSize.width)) * 0.5f,
484                          windowSize.y * 0.125f);
485
486     // lays down bricks in a spiral formation starting at i,j = (0,0) top left corner
487     // travelling right di,dj = (1,0) initially
488     int i  = 0;
489     int j  = 0;
490     int di = 1;
491     int dj = 0;
492
493     // contracting boundaries
494     int left   = 0;
495     int right  = columns - 1;
496     int top    = 2;
497     int bottom = rows - 1;
498
499     // length of current line. we stop laying down bricks when the length is 1 brick or less.
500     int length = 0;
501     while(true)
502     {
503       Actor brick = CreateBrick(Vector2(i * brickSize.width + offset.x, j * brickSize.height + offset.y) + (brickSize * 0.5f), 0);
504       mLevelContainer.Add(brick);
505       i += di;
506       j += dj;
507       bool turn(false);
508       if((i == right) && (di == 1))
509       {
510         right -= 2;
511         turn = true;
512       }
513       if((j == bottom) && (dj == 1))
514       {
515         bottom -= 2;
516         turn = true;
517       }
518       if((i == left) && (di == -1))
519       {
520         left += 2;
521         turn = true;
522       }
523       if((j == top) && (dj == -1))
524       {
525         top += 2;
526         turn = true;
527       }
528       if(turn)
529       {
530         // turn 90 degrees clockwise.
531         std::swap(di, dj);
532         di = -di;
533         if(length <= 1)
534         {
535           break;
536         }
537         length = 0;
538       }
539       length++;
540       mBrickCount++;
541     }
542   }
543
544   /**
545    * Creates a brick at a specified position on the window
546    * @param[in] position the position for the brick
547    * @param[in] type the type of brick
548    * @return The Brick Actor is returned.
549    */
550   Actor CreateBrick(const Vector2& position, int type)
551   {
552     mBrickImageMap["url"] = BRICK_IMAGE_PATH[type];
553     ImageView brick       = ImageView::New();
554     brick.SetProperty(ImageView::Property::IMAGE, mBrickImageMap);
555     brick.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::TOP_LEFT);
556     brick.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::CENTER);
557     brick.SetProperty(Actor::Property::POSITION, position);
558
559     // Add a constraint on the brick between it and the ball generating a collision-property
560     Property::Index property   = brick.RegisterProperty(COLLISION_PROPERTY_NAME, Vector3::ZERO);
561     Constraint      constraint = Constraint::New<Vector3>(brick, property, CollisionCircleRectangleConstraint(BRICK_COLLISION_MARGIN));
562     constraint.AddSource(Source(mBall, Actor::Property::POSITION));
563     constraint.AddSource(Source(brick, Actor::Property::POSITION));
564     constraint.AddSource(Source(mBall, Actor::Property::SIZE));
565     constraint.AddSource(Source(brick, Actor::Property::SIZE));
566     constraint.Apply();
567
568     // Now add a notification on this collision-property
569
570     PropertyNotification brickNotification = brick.AddPropertyNotification(property, GreaterThanCondition(0.0f));
571     brickNotification.NotifySignal().Connect(this, &ExampleController::OnHitBrick);
572
573     return brick;
574   }
575
576   /**
577    * Creates an Image (Helper)
578    *
579    * @param[in] filename the path of the image.
580    */
581   ImageView CreateImage(const std::string& filename)
582   {
583     Property::Map propertyMap;
584     propertyMap.Insert(Visual::Property::TYPE, Visual::IMAGE);
585     propertyMap.Insert(ImageVisual::Property::URL, filename);
586     propertyMap.Insert(DevelVisual::Property::VISUAL_FITTING_MODE, DevelVisual::FILL);
587     ImageView actor = ImageView::New();
588     actor.SetProperty(Toolkit::ImageView::Property::IMAGE, propertyMap);
589     actor.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::TOP_LEFT);
590     actor.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::CENTER);
591     return actor;
592   }
593
594   /**
595    * Continue animation (based on current velocity)
596    */
597   void ContinueAnimation()
598   {
599     if(mBallAnimation)
600     {
601       mBallAnimation.Clear();
602     }
603
604     mBallAnimation = Animation::New(MAX_ANIMATION_DURATION);
605     mBallAnimation.AnimateBy(Property(mBall, Actor::Property::POSITION), mBallVelocity * MAX_ANIMATION_DURATION);
606     mBallAnimation.Play();
607   }
608
609   /**
610    * Signal invoked whenever user touches the Paddle.
611    * @param[in] actor The actor touched
612    * @param[in] event The touch event
613    */
614   bool OnTouchPaddle(Actor actor, const TouchEvent& event)
615   {
616     if(event.GetPointCount() > 0)
617     {
618       if(event.GetState(0) == PointState::DOWN) // Commence dragging
619       {
620         // Get point where user touched paddle (relative to paddle's center)
621         Vector2 screenPoint = event.GetScreenPosition(0);
622         mRelativeDragPoint  = screenPoint;
623         mRelativeDragPoint -= actor.GetCurrentProperty<Vector3>(Actor::Property::POSITION);
624
625         mDragActor     = actor;
626         mDragAnimation = Animation::New(0.25f);
627         mDragAnimation.AnimateTo(Property(mDragActor, Actor::Property::SCALE), Vector3(1.1f, 1.1f, 1.0f), AlphaFunction::EASE_OUT);
628         mDragAnimation.AnimateTo(Property(mPaddleHandle, Actor::Property::COLOR), Vector4(1.0f, 1.0f, 1.0f, 0.0f), AlphaFunction::EASE_OUT);
629         mDragAnimation.Play();
630       }
631     }
632     return false;
633   }
634
635   /**
636    * Signal invoked whenever user touches anywhere on the screen.
637    * @param[in] actor The actor touched
638    * @param[in] event The touch event
639    */
640   bool OnTouchLayer(Actor actor, const TouchEvent& event)
641   {
642     if(event.GetPointCount() > 0)
643     {
644       if(mDragActor)
645       {
646         Vector3 position(event.GetScreenPosition(0));
647         mPaddle.SetProperty(Actor::Property::POSITION, position - mRelativeDragPoint);
648
649         if(event.GetState(0) == PointState::UP) // Stop dragging
650         {
651           mDragAnimation = Animation::New(0.25f);
652           mDragAnimation.AnimateTo(Property(mDragActor, Actor::Property::SCALE), Vector3(1.0f, 1.0f, 1.0f), AlphaFunction::EASE_IN);
653           mDragAnimation.AnimateTo(Property(mPaddleHandle, Actor::Property::COLOR), Vector4(1.0f, 1.0f, 1.0f, 1.0f), AlphaFunction::EASE_OUT);
654           mDragAnimation.Play();
655           mDragActor.Reset();
656         }
657       }
658     }
659     return false;
660   }
661
662   /**
663    * Notification: Ball hit left wall
664    * @param source The notification
665    */
666   void OnHitLeftWall(PropertyNotification& source)
667   {
668     mBallVelocity.x = fabsf(mBallVelocity.x);
669     ContinueAnimation();
670   }
671
672   /**
673    * Notification: Ball hit right wall
674    * @param source The notification
675    */
676   void OnHitRightWall(PropertyNotification& source)
677   {
678     mBallVelocity.x = -fabsf(mBallVelocity.x);
679     ContinueAnimation();
680   }
681
682   /**
683    * Notification: Ball hit top wall
684    * @param source The notification
685    */
686   void OnHitTopWall(PropertyNotification& source)
687   {
688     mBallVelocity.y = fabsf(mBallVelocity.y);
689     ContinueAnimation();
690   }
691
692   /**
693    * Notification: Ball hit bottom wall
694    * @param source The notification
695    */
696   void OnHitBottomWall(PropertyNotification& source)
697   {
698     if(mBallAnimation)
699     {
700       mBallAnimation.Clear();
701     }
702
703     if(mLives > 0)
704     {
705       mLives--;
706       const float f(static_cast<float>(mLives) / TOTAL_LIVES);
707       mBallVelocity = Vector3::ZERO;
708
709       Animation shrink = Animation::New(0.5f);
710       shrink.AnimateTo(Property(mPaddle, Actor::Property::SIZE_WIDTH), mPaddleFullSize.x * f + mPaddleHitMargin.x);
711       shrink.AnimateTo(Property(mPaddleImage, Actor::Property::SIZE_WIDTH), mPaddleFullSize.x * f);
712
713       shrink.FinishedSignal().Connect(this, &ExampleController::OnPaddleShrunk);
714       shrink.Play();
715     }
716   }
717
718   /**
719    * Paddle Shrink Animation complete.
720    * @param[in] source The animation responsible for shrinking the paddle.
721    */
722   void OnPaddleShrunk(Animation& source)
723   {
724     // Reposition Ball in start position, and make ball appear.
725     mBall.SetProperty(Actor::Property::POSITION, mBallStartPosition);
726     mBall.SetProperty(Actor::Property::COLOR, Vector4(1.0f, 1.0f, 1.0f, 0.1f));
727     Animation appear = Animation::New(0.5f);
728     appear.AnimateTo(Property(mBall, Actor::Property::COLOR), Vector4(1.0f, 1.0f, 1.0f, 1.0f));
729     appear.Play();
730
731     if(!mLives)
732     {
733       RestartGame();
734     }
735   }
736
737   /**
738    * Notification: Ball hit paddle
739    * @param source The notification
740    */
741   void OnHitPaddle(PropertyNotification& source)
742   {
743     Actor   delegate        = Actor::DownCast(source.GetTarget());
744     Vector3 collisionVector = delegate.GetCurrentProperty<Vector3>(source.GetTargetProperty());
745     Vector3 ballRelativePosition(mBall.GetCurrentProperty<Vector3>(Actor::Property::POSITION) - mPaddle.GetCurrentProperty<Vector3>(Actor::Property::POSITION));
746     ballRelativePosition.Normalize();
747
748     collisionVector.x += ballRelativePosition.x * 0.5f;
749
750     if(mBallVelocity.LengthSquared() < Math::MACHINE_EPSILON_1)
751     {
752       mBallVelocity += collisionVector * BALL_VELOCITY;
753     }
754     else
755     {
756       const float normalVelocity = fabsf(mBallVelocity.Dot(collisionVector));
757       mBallVelocity += collisionVector * normalVelocity * 2.0f;
758       const float currentSpeed = mBallVelocity.Length();
759       const float limitedSpeed = std::min(currentSpeed, MAX_VELOCITY);
760       mBallVelocity            = mBallVelocity * limitedSpeed / currentSpeed;
761     }
762
763     ContinueAnimation();
764
765     // wobble paddle
766     mWobbleAnimation = Animation::New(0.5f);
767     mWobbleAnimation.AnimateTo(Property(mPaddle, mWobbleProperty), 1.0f);
768     mWobbleAnimation.Play();
769     mPaddle.SetProperty(mWobbleProperty, 0.0f);
770   }
771
772   /**
773    * Notification: Ball hit brick
774    * @param source The notification
775    */
776   void OnHitBrick(PropertyNotification& source)
777   {
778     Actor   brick           = Actor::DownCast(source.GetTarget());
779     Vector3 collisionVector = brick.GetCurrentProperty<Vector3>(source.GetTargetProperty());
780
781     const float normalVelocity = fabsf(mBallVelocity.Dot(collisionVector));
782     mBallVelocity += collisionVector * normalVelocity * 2.0f;
783     const float currentSpeed = mBallVelocity.Length();
784     const float limitedSpeed = std::min(currentSpeed, MAX_VELOCITY);
785     mBallVelocity            = mBallVelocity * limitedSpeed / currentSpeed;
786
787     ContinueAnimation();
788
789     // remove collision-constraint and notification.
790     brick.RemovePropertyNotification(source);
791     brick.RemoveConstraints();
792
793     // fade brick (destroy)
794     Animation destroyAnimation = Animation::New(0.5f);
795     destroyAnimation.AnimateTo(Property(brick, Actor::Property::COLOR_ALPHA), 0.0f, AlphaFunction::EASE_IN);
796     destroyAnimation.Play();
797     destroyAnimation.FinishedSignal().Connect(this, &ExampleController::OnBrickDestroyed);
798     mDestroyAnimationMap[destroyAnimation] = brick;
799   }
800
801   /**
802    * Brick Destruction Animation complete.
803    * @param[in] source The animation responsible for destroying the brick
804    */
805   void OnBrickDestroyed(Animation& source)
806   {
807     // Remove brick from window, it's constraint and property notification should also remove themselves.
808     Actor brick = mDestroyAnimationMap[source];
809     mDestroyAnimationMap.erase(source);
810     brick.GetParent().Remove(brick);
811     mBrickCount--;
812
813     if(!mBrickCount)
814     {
815       mLevel++;
816       LoadLevel(mLevel);
817     }
818   }
819
820   /**
821    * Main key event handler
822    */
823   void OnKeyEvent(const KeyEvent& event)
824   {
825     if(event.GetState() == KeyEvent::DOWN)
826     {
827       if(IsKey(event, Dali::DALI_KEY_ESCAPE) || IsKey(event, Dali::DALI_KEY_BACK))
828       {
829         mApplication.Quit();
830       }
831     }
832   }
833
834 private:
835   Application&     mApplication;       ///< Application instance
836   Toolkit::Control mView;              ///< The View instance.
837   Layer            mContentLayer;      ///< The content layer (contains game actors)
838   ImageView        mBall;              ///< The Moving ball image.
839   Vector3          mBallStartPosition; ///< Ball Start position
840   Vector3          mBallVelocity;      ///< Ball's current direction.
841   Animation        mBallAnimation;     ///< Ball's animation
842   Actor            mPaddle;            ///< The paddle including hit area.
843   ImageView        mPaddleImage;       ///< The paddle's image.
844   ImageView        mPaddleHandle;      ///< The paddle's handle (where the user touches)
845   Vector2          mPaddleHitMargin;   ///< The paddle hit margin.
846   Animation        mWobbleAnimation;   ///< Paddle's animation when hit (wobbles)
847   Property::Index  mWobbleProperty;    ///< The wobble property (generated from animation)
848   Actor            mLevelContainer;    ///< The level container (contains bricks)
849   Property::Map    mBrickImageMap;     ///< The property map used to load the brick
850
851   // actor - dragging functionality
852
853   Animation                  mDragAnimation;       ///< Animation for dragging. (grows - affects ACTOR::SCALE)
854   Actor                      mDragActor;           ///< The actor which is being dragged (if any)
855   Vector3                    mRelativeDragPoint;   ///< The point the user touched, relative to the actor.
856   std::map<Animation, Actor> mDestroyAnimationMap; ///< Keep track of which actors are to be destroyed.
857   Vector2                    mPaddleFullSize;      ///< Initial 100% size of the paddle.
858   int                        mLevel;               ///< Current level
859   int                        mLives;               ///< Total lives.
860   int                        mBrickCount;          ///< Total bricks on screen.
861 };
862
863 int DALI_EXPORT_API main(int argc, char** argv)
864 {
865   Application       app = Application::New(&argc, &argv, DEMO_THEME_PATH);
866   ExampleController test(app);
867   app.MainLoop();
868   return 0;
869 }