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