Changes after TouchedSignal changes
[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 <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 <dali-toolkit/devel-api/visuals/visual-properties-devel.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   /**
187    * @param[in,out] current The current rotation property
188    * @param[in] inputs Contains the wobble property (value from 0.0f to 1.0f)
189    * @return The rotation (quaternion) is generated.
190    */
191   void operator()( Quaternion& current, const PropertyInputContainer& inputs )
192   {
193     const float& wobble = inputs[0]->GetFloat();
194
195     float f = sinf(wobble * 10.0f) * (1.0f-wobble);
196
197     current = Quaternion(mDeviation * f, Vector3::ZAXIS);
198   }
199
200   Radian mDeviation;           ///< Deviation factor in radians.
201 };
202
203 } // unnamed namespace
204
205 /**
206  * This example shows how to use PropertyNotifications
207  */
208 class ExampleController : public ConnectionTracker
209 {
210 public:
211
212   /**
213    * Constructor
214    * @param application Application class, stored as reference
215    */
216   ExampleController( Application& application )
217   : mApplication( application ),
218     mView(),
219     mContentLayer(),
220     mBall(),
221     mBallStartPosition(),
222     mBallVelocity(),
223     mBallAnimation(),
224     mPaddle(),
225     mPaddleImage(),
226     mPaddleHandle(),
227     mPaddleHitMargin(),
228     mWobbleAnimation(),
229     mWobbleProperty( Property::INVALID_INDEX ),
230     mLevelContainer(),
231     mBrickImageMap(),
232     mDragAnimation(),
233     mDragActor(),
234     mRelativeDragPoint(),
235     mDestroyAnimationMap(),
236     mPaddleFullSize(),
237     mLevel( 0 ),
238     mLives( TOTAL_LIVES ),
239     mBrickCount( 0 )
240
241   {
242     // Connect to the Application's Init and orientation changed signal
243     mApplication.InitSignal().Connect(this, &ExampleController::Create);
244   }
245
246   /**
247    * This method gets called once the main loop of application is up and running
248    * @param[in] application Reference to the application instance
249    */
250   void Create(Application& application)
251   {
252     application.GetWindow().KeyEventSignal().Connect(this, &ExampleController::OnKeyEvent);
253
254     // Creates a default view with a default tool bar.
255     // The view is added to the window.
256     Toolkit::ToolBar toolBar;
257     mContentLayer = DemoHelper::CreateView( application,
258                                             mView,
259                                             toolBar,
260                                             BACKGROUND_IMAGE,
261                                             TOOLBAR_IMAGE,
262                                             APPLICATION_TITLE );
263
264     // Add an extra space on the right to center the title text.
265     toolBar.AddControl( Actor::New(), DemoHelper::DEFAULT_VIEW_STYLE.mToolBarButtonPercentage, Toolkit::Alignment::HORIZONTAL_RIGHT );
266
267     // Create the content layer, which is where game actors appear.
268     AddContentLayer();
269   }
270
271 private:
272
273   /**
274    * Adds a new layer to the window, containing game actors.
275    */
276   void AddContentLayer()
277   {
278     Window window = mApplication.GetWindow();
279     const Vector3 windowSize(window.GetSize());
280
281     // Ball setup
282     mBallStartPosition = windowSize * Vector3( BALL_START_POSITION );
283     mBall = CreateImage(BALL_IMAGE);
284     mBall.SetProperty( Actor::Property::POSITION, mBallStartPosition );
285     mBall.SetProperty( Actor::Property::SIZE, BALL_SIZE * windowSize.width );
286     mContentLayer.Add(mBall);
287     mBallVelocity = Vector3::ZERO;
288
289     // Paddle setup
290     mPaddleHitMargin = Vector2(windowSize) * PADDLE_HIT_MARGIN;
291     mPaddle = Actor::New();
292     mPaddleHandle = CreateImage(PADDLE_HANDLE_IMAGE);
293     mPaddleImage = CreateImage(PADDLE_IMAGE);
294     mPaddle.Add( mPaddleHandle );
295     mPaddle.Add( mPaddleImage );
296     mPaddleHandle.SetProperty( Actor::Property::PARENT_ORIGIN, ParentOrigin::TOP_CENTER );
297     mPaddleHandle.SetProperty( Actor::Property::ANCHOR_POINT, AnchorPoint::TOP_CENTER );
298     mPaddleHandle.SetProperty( Actor::Property::POSITION, Vector2( 0.0f, windowSize.width * 0.0125f ));
299     mPaddleImage.SetProperty( Actor::Property::PARENT_ORIGIN, ParentOrigin::TOP_CENTER );
300     mPaddleImage.SetProperty( Actor::Property::ANCHOR_POINT, AnchorPoint::TOP_CENTER );
301     mPaddle.SetProperty( Actor::Property::PARENT_ORIGIN, ParentOrigin::TOP_LEFT );
302     mPaddle.SetProperty( Actor::Property::ANCHOR_POINT, AnchorPoint::CENTER );
303     mPaddleFullSize = PADDLE_SIZE * windowSize.width;
304     mPaddle.SetProperty( Actor::Property::SIZE, mPaddleFullSize + mPaddleHitMargin );
305     mPaddleHandle.SetProperty( Actor::Property::SIZE, PADDLE_HANDLE_SIZE * windowSize.width );
306     mPaddleImage.SetProperty( Actor::Property::SIZE, mPaddleFullSize );
307
308     mWobbleProperty = mPaddle.RegisterProperty(WOBBLE_PROPERTY_NAME, 0.0f);
309     Constraint wobbleConstraint = Constraint::New<Quaternion>( mPaddle, Actor::Property::ORIENTATION, WobbleConstraint(Degree( 10.0f )));
310     wobbleConstraint.AddSource( LocalSource(mWobbleProperty) );
311     wobbleConstraint.Apply();
312
313     mPaddle.SetProperty( Actor::Property::POSITION, windowSize * Vector3( PADDLE_START_POSITION ) );
314     mContentLayer.Add(mPaddle);
315     mPaddle.TouchedSignal().Connect(this, &ExampleController::OnTouchPaddle);
316     mContentLayer.TouchedSignal().Connect(this, &ExampleController::OnTouchLayer);
317
318     const float margin(BALL_SIZE.width * windowSize.width * 0.5f);
319
320     // Set up notifications for ball's collisions against walls.
321     PropertyNotification leftNotification = mBall.AddPropertyNotification( Actor::Property::POSITION_X, LessThanCondition(margin) );
322     leftNotification.NotifySignal().Connect( this, &ExampleController::OnHitLeftWall );
323
324     PropertyNotification rightNotification = mBall.AddPropertyNotification( Actor::Property::POSITION_X, GreaterThanCondition(windowSize.width - margin) );
325     rightNotification.NotifySignal().Connect( this, &ExampleController::OnHitRightWall );
326
327     PropertyNotification topNotification = mBall.AddPropertyNotification( Actor::Property::POSITION_Y, LessThanCondition(margin) );
328     topNotification.NotifySignal().Connect( this, &ExampleController::OnHitTopWall );
329
330     PropertyNotification bottomNotification = mBall.AddPropertyNotification( Actor::Property::POSITION_Y, GreaterThanCondition(windowSize.height + margin) );
331     bottomNotification.NotifySignal().Connect( this, &ExampleController::OnHitBottomWall );
332
333     // Set up notification for ball colliding against paddle.
334     Actor delegate = Actor::New();
335     window.Add(delegate);
336     Property::Index property = delegate.RegisterProperty(COLLISION_PROPERTY_NAME, Vector3::ZERO);
337     Constraint constraint = Constraint::New<Vector3>( delegate, property, CollisionCircleRectangleConstraint( -Vector3(0.0f, mPaddleHitMargin.height * 0.575f, 0.0f),-Vector3(mPaddleHitMargin) ) );
338     constraint.AddSource( Source(mBall, Actor::Property::POSITION) );
339     constraint.AddSource( Source(mPaddle, Actor::Property::POSITION) );
340     constraint.AddSource( Source(mBall, Actor::Property::SIZE) );
341     constraint.AddSource( Source(mPaddle, Actor::Property::SIZE) );
342     constraint.Apply();
343
344     PropertyNotification paddleNotification = delegate.AddPropertyNotification( property, GreaterThanCondition(0.0f) );
345     paddleNotification.NotifySignal().Connect( this, &ExampleController::OnHitPaddle );
346
347     RestartGame();
348   }
349
350   /**
351    * Restarts Game
352    * Resets Lives count and other stats, and loads level
353    */
354   void RestartGame()
355   {
356     mLives = TOTAL_LIVES;
357     mLevel = 0;
358     mBall.SetProperty( Actor::Property::POSITION, mBallStartPosition );
359     mBallVelocity = Vector3::ZERO;
360     mPaddle.SetProperty( Actor::Property::SIZE, mPaddleFullSize + mPaddleHitMargin );
361     mPaddleImage.SetProperty( Actor::Property::SIZE, mPaddleFullSize );
362
363     LoadLevel(mLevel);
364   }
365
366   /**
367    * Loads level
368    * All existing level content is removed, and new bricks
369    * are added.
370    * @param[in] level Level index to load.
371    */
372   void LoadLevel(int level)
373   {
374     if(mLevelContainer && mLevelContainer.GetParent() == mContentLayer)
375     {
376       mContentLayer.Remove( mLevelContainer );
377     }
378
379     mLevelContainer = Actor::New();
380     mLevelContainer.SetProperty( Actor::Property::ANCHOR_POINT, AnchorPoint::CENTER );
381     mLevelContainer.SetProperty( Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER );
382     mLevelContainer.SetResizePolicy( ResizePolicy::FILL_TO_PARENT, Dimension::ALL_DIMENSIONS );
383
384     mContentLayer.Add( mLevelContainer );
385
386     mBrickCount = 0;
387
388     if( mBrickImageMap.Empty() )
389     {
390       Vector2 windowSize(mApplication.GetWindow().GetSize());
391       const Vector2 brickSize(BRICK_SIZE * Vector2(windowSize.x, windowSize.x));
392
393       mBrickImageMap["desiredWidth"] = static_cast<int>( brickSize.width );
394       mBrickImageMap["desiredHeight"] = static_cast<int>( brickSize.height );
395       mBrickImageMap["fittingMode"] = "SCALE_TO_FILL";
396       mBrickImageMap["samplingMode"] = "BOX_THEN_LINEAR";
397     }
398
399     switch(level%TOTAL_LEVELS)
400     {
401       case 0:
402       {
403         GenerateLevel0();
404         break;
405       }
406       case 1:
407       {
408         GenerateLevel1();
409         break;
410       }
411       case 2:
412       {
413         GenerateLevel2();
414         break;
415       }
416       default:
417       {
418         break;
419       }
420     } // end switch
421   }
422
423   /**
424    * Generates level 0
425    */
426   void GenerateLevel0()
427   {
428     Vector2 windowSize(mApplication.GetWindow().GetSize());
429     const Vector2 brickSize(BRICK_SIZE * windowSize.width);
430
431     const int columns = (0.85f * windowSize.width) / brickSize.width; // 85 percent of the width of the screen covered with bricks.
432     const int rows = (0.3f * windowSize.height) / brickSize.height;   // 30 percent of the height of the screen covered with bricks.
433     const Vector2 offset( (windowSize.x - (columns * brickSize.width)) * 0.5f,
434                            windowSize.y * 0.125f );
435
436     for(int j = 0; j < rows; j++)
437     {
438       for(int i = 0; i < columns; i++)
439       {
440         Actor brick = CreateBrick(Vector2(i * brickSize.width + offset.x, j * brickSize.height + offset.y) + (brickSize * 0.5f), j % TOTAL_BRICKS );
441         mLevelContainer.Add(brick);
442         mBrickCount++;
443       }
444     }
445   }
446
447   /**
448    * Generates level 1
449    */
450   void GenerateLevel1()
451   {
452     Vector2 windowSize(mApplication.GetWindow().GetSize());
453     const Vector2 brickSize(BRICK_SIZE * windowSize.width);
454
455     const int columns = (0.85f * windowSize.width) / brickSize.width; // 85 percent of the width of the screen covered with bricks.
456     const int rows = (0.3f * windowSize.height) / brickSize.height;   // 30 percent of the height of the screen covered with bricks.
457     const Vector2 offset( (windowSize.x - (columns * brickSize.width)) * 0.5f,
458                            windowSize.y * 0.125f );
459
460     for(int j = 0; j < rows; j++)
461     {
462       for(int i = 0; i < columns; i++)
463       {
464         int i2 = columns - i - 1;
465         int j2 = rows - j - 1;
466         int brickIndex = std::min( std::min(i, j), std::min(i2, j2) ) % TOTAL_BRICKS;
467
468         Actor brick = CreateBrick(Vector2(i * brickSize.width + offset.x, j * brickSize.height + offset.y) + (brickSize * 0.5f), brickIndex );
469
470         mLevelContainer.Add(brick);
471         mBrickCount++;
472       }
473     }
474   }
475
476   /**
477    * Generates level 2
478    */
479   void GenerateLevel2()
480   {
481     Vector2 windowSize(mApplication.GetWindow().GetSize());
482     const Vector2 brickSize(BRICK_SIZE * windowSize.width);
483
484     const int columns = (0.85f * windowSize.width) / brickSize.width; // 85 percent of the width of the screen covered with bricks.
485     const int rows = (0.3f * windowSize.height) / brickSize.height;   // 30 percent of the height of the screen covered with bricks.
486     const Vector2 offset( (windowSize.x - (columns * brickSize.width)) * 0.5f,
487                            windowSize.y * 0.125f );
488
489     // lays down bricks in a spiral formation starting at i,j = (0,0) top left corner
490     // travelling right di,dj = (1,0) initially
491     int i = 0;
492     int j = 0;
493     int di = 1;
494     int dj = 0;
495
496     // contracting boundaries
497     int left = 0;
498     int right = columns - 1;
499     int top = 2;
500     int bottom = rows - 1;
501
502     // length of current line. we stop laying down bricks when the length is 1 brick or less.
503     int length = 0;
504     while(true)
505     {
506       Actor brick = CreateBrick(Vector2(i * brickSize.width + offset.x, j * brickSize.height + offset.y) + (brickSize * 0.5f), 0 );
507       mLevelContainer.Add(brick);
508       i+=di;
509       j+=dj;
510       bool turn(false);
511       if((i==right) && (di==1))
512       {
513         right -= 2;
514         turn = true;
515       }
516       if((j==bottom) && (dj==1))
517       {
518         bottom -= 2;
519         turn = true;
520       }
521       if((i==left) && (di==-1))
522       {
523         left += 2;
524         turn = true;
525       }
526       if((j==top) && (dj==-1))
527       {
528         top += 2;
529         turn = true;
530       }
531       if(turn)
532       {
533         // turn 90 degrees clockwise.
534         std::swap(di, dj);
535         di = -di;
536         if (length<=1)
537         {
538           break;
539         }
540         length = 0;
541       }
542       length++;
543       mBrickCount++;
544     }
545   }
546
547
548   /**
549    * Creates a brick at a specified position on the window
550    * @param[in] position the position for the brick
551    * @param[in] type the type of brick
552    * @return The Brick Actor is returned.
553    */
554   Actor CreateBrick( const Vector2& position, int type )
555   {
556     mBrickImageMap["url"] = BRICK_IMAGE_PATH[type];
557     ImageView brick = ImageView::New();
558     brick.SetProperty( ImageView::Property::IMAGE, mBrickImageMap );
559     brick.SetProperty( Actor::Property::PARENT_ORIGIN,ParentOrigin::TOP_LEFT);
560     brick.SetProperty( Actor::Property::ANCHOR_POINT,AnchorPoint::CENTER);
561     brick.SetProperty( Actor::Property::POSITION, position );
562
563     // Add a constraint on the brick between it and the ball generating a collision-property
564     Property::Index property = brick.RegisterProperty(COLLISION_PROPERTY_NAME, Vector3::ZERO);
565     Constraint constraint = Constraint::New<Vector3>( brick, property, CollisionCircleRectangleConstraint(BRICK_COLLISION_MARGIN) );
566     constraint.AddSource( Source(mBall, Actor::Property::POSITION) );
567     constraint.AddSource( Source(brick, Actor::Property::POSITION) );
568     constraint.AddSource( Source(mBall, Actor::Property::SIZE) );
569     constraint.AddSource( Source(brick, Actor::Property::SIZE) );
570     constraint.Apply();
571
572     // Now add a notification on this collision-property
573
574     PropertyNotification brickNotification = brick.AddPropertyNotification( property, GreaterThanCondition(0.0f) );
575     brickNotification.NotifySignal().Connect( this, &ExampleController::OnHitBrick );
576
577     return brick;
578   }
579
580   /**
581    * Creates an Image (Helper)
582    *
583    * @param[in] filename the path of the image.
584    */
585   ImageView CreateImage(const std::string& filename)
586   {
587     Property::Map propertyMap;
588     propertyMap.Insert(Visual::Property::TYPE, Visual::IMAGE);
589     propertyMap.Insert(ImageVisual::Property::URL, filename);
590     propertyMap.Insert(DevelVisual::Property::VISUAL_FITTING_MODE, DevelVisual::FILL);
591     ImageView actor = ImageView::New();
592     actor.SetProperty(Toolkit::ImageView::Property::IMAGE, propertyMap);
593     actor.SetProperty( Actor::Property::PARENT_ORIGIN,ParentOrigin::TOP_LEFT);
594     actor.SetProperty( Actor::Property::ANCHOR_POINT,AnchorPoint::CENTER);
595     return actor;
596   }
597
598   /**
599    * Continue animation (based on current velocity)
600    */
601   void ContinueAnimation()
602   {
603     if(mBallAnimation)
604     {
605       mBallAnimation.Clear();
606     }
607
608     mBallAnimation = Animation::New(MAX_ANIMATION_DURATION);
609     mBallAnimation.AnimateBy( Property( mBall, Actor::Property::POSITION ), mBallVelocity * MAX_ANIMATION_DURATION);
610     mBallAnimation.Play();
611   }
612
613   /**
614    * Signal invoked whenever user touches the Paddle.
615    * @param[in] actor The actor touched
616    * @param[in] event The touch event
617    */
618   bool OnTouchPaddle(Actor actor, const TouchEvent& event)
619   {
620     if(event.GetPointCount()>0)
621     {
622       if( event.GetState( 0 ) == PointState::DOWN ) // Commence dragging
623       {
624         // Get point where user touched paddle (relative to paddle's center)
625         Vector2 screenPoint = event.GetScreenPosition( 0 );
626         mRelativeDragPoint = screenPoint;
627         mRelativeDragPoint -= actor.GetCurrentProperty< Vector3 >( Actor::Property::POSITION );
628
629         mDragActor = actor;
630         mDragAnimation = Animation::New(0.25f);
631         mDragAnimation.AnimateTo( Property(mDragActor, Actor::Property::SCALE), Vector3(1.1f, 1.1f, 1.0f), AlphaFunction::EASE_OUT);
632         mDragAnimation.AnimateTo( Property(mPaddleHandle, Actor::Property::COLOR), Vector4(1.0f, 1.0f, 1.0f, 0.0f), AlphaFunction::EASE_OUT);
633         mDragAnimation.Play();
634       }
635     }
636     return false;
637   }
638
639   /**
640    * Signal invoked whenever user touches anywhere on the screen.
641    * @param[in] actor The actor touched
642    * @param[in] event The touch event
643    */
644   bool OnTouchLayer(Actor actor, const TouchEvent& event)
645   {
646     if(event.GetPointCount()>0)
647     {
648       if(mDragActor)
649       {
650         Vector3 position( event.GetScreenPosition( 0 ) );
651         mPaddle.SetProperty( Actor::Property::POSITION, position - mRelativeDragPoint );
652
653         if( event.GetState( 0 ) == PointState::UP ) // Stop dragging
654         {
655           mDragAnimation = Animation::New(0.25f);
656           mDragAnimation.AnimateTo( Property(mDragActor, Actor::Property::SCALE), Vector3(1.0f, 1.0f, 1.0f), AlphaFunction::EASE_IN);
657           mDragAnimation.AnimateTo( Property(mPaddleHandle, Actor::Property::COLOR), Vector4(1.0f, 1.0f, 1.0f, 1.0f), AlphaFunction::EASE_OUT);
658           mDragAnimation.Play();
659           mDragActor.Reset();
660         }
661       }
662     }
663     return false;
664   }
665
666   /**
667    * Notification: Ball hit left wall
668    * @param source The notification
669    */
670   void OnHitLeftWall(PropertyNotification& source)
671   {
672     mBallVelocity.x = fabsf(mBallVelocity.x);
673     ContinueAnimation();
674   }
675
676   /**
677    * Notification: Ball hit right wall
678    * @param source The notification
679    */
680   void OnHitRightWall(PropertyNotification& source)
681   {
682     mBallVelocity.x = -fabsf(mBallVelocity.x);
683     ContinueAnimation();
684   }
685
686   /**
687    * Notification: Ball hit top wall
688    * @param source The notification
689    */
690   void OnHitTopWall(PropertyNotification& source)
691   {
692     mBallVelocity.y = fabsf(mBallVelocity.y);
693     ContinueAnimation();
694   }
695
696   /**
697    * Notification: Ball hit bottom wall
698    * @param source The notification
699    */
700   void OnHitBottomWall(PropertyNotification& source)
701   {
702     if(mBallAnimation)
703     {
704       mBallAnimation.Clear();
705     }
706
707     if(mLives>0)
708     {
709       mLives--;
710       const float f(static_cast<float>(mLives) / TOTAL_LIVES);
711       mBallVelocity = Vector3::ZERO;
712
713       Animation shrink = Animation::New(0.5f);
714       shrink.AnimateTo( Property(mPaddle, Actor::Property::SIZE_WIDTH), mPaddleFullSize.x * f + mPaddleHitMargin.x);
715       shrink.AnimateTo( Property(mPaddleImage, Actor::Property::SIZE_WIDTH), mPaddleFullSize.x * f );
716
717       shrink.FinishedSignal().Connect( this, &ExampleController::OnPaddleShrunk );
718       shrink.Play();
719     }
720   }
721
722   /**
723    * Paddle Shrink Animation complete.
724    * @param[in] source The animation responsible for shrinking the paddle.
725    */
726   void OnPaddleShrunk( Animation &source )
727   {
728     // Reposition Ball in start position, and make ball appear.
729     mBall.SetProperty( Actor::Property::POSITION, mBallStartPosition );
730     mBall.SetProperty( Actor::Property::COLOR, Vector4(1.0f, 1.0f, 1.0f, 0.1f) );
731     Animation appear = Animation::New(0.5f);
732     appear.AnimateTo( Property(mBall, Actor::Property::COLOR), Vector4(1.0f, 1.0f, 1.0f, 1.0f) );
733     appear.Play();
734
735     if(!mLives)
736     {
737       RestartGame();
738     }
739   }
740
741   /**
742    * Notification: Ball hit paddle
743    * @param source The notification
744    */
745   void OnHitPaddle(PropertyNotification& source)
746   {
747     Actor delegate = Actor::DownCast(source.GetTarget());
748     Vector3 collisionVector = delegate.GetCurrentProperty< Vector3 >( source.GetTargetProperty() );
749     Vector3 ballRelativePosition(mBall.GetCurrentProperty< Vector3 >( Actor::Property::POSITION ) - mPaddle.GetCurrentProperty< Vector3 >( Actor::Property::POSITION ));
750     ballRelativePosition.Normalize();
751
752     collisionVector.x += ballRelativePosition.x * 0.5f;
753
754     if(mBallVelocity.LengthSquared() < Math::MACHINE_EPSILON_1)
755     {
756       mBallVelocity += collisionVector * BALL_VELOCITY;
757     }
758     else
759     {
760       const float normalVelocity = fabsf(mBallVelocity.Dot(collisionVector));
761       mBallVelocity += collisionVector * normalVelocity * 2.0f;
762       const float currentSpeed = mBallVelocity.Length();
763       const float limitedSpeed = std::min( currentSpeed, MAX_VELOCITY );
764       mBallVelocity = mBallVelocity * limitedSpeed / currentSpeed;
765     }
766
767     ContinueAnimation();
768
769     // wobble paddle
770     mWobbleAnimation = Animation::New(0.5f);
771     mWobbleAnimation.AnimateTo( Property( mPaddle, mWobbleProperty ), 1.0f );
772     mWobbleAnimation.Play();
773     mPaddle.SetProperty(mWobbleProperty, 0.0f);
774   }
775
776   /**
777    * Notification: Ball hit brick
778    * @param source The notification
779    */
780   void OnHitBrick(PropertyNotification& source)
781   {
782     Actor brick = Actor::DownCast(source.GetTarget());
783     Vector3 collisionVector = brick.GetCurrentProperty< Vector3 >( source.GetTargetProperty() );
784
785     const float normalVelocity = fabsf(mBallVelocity.Dot(collisionVector));
786     mBallVelocity += collisionVector * normalVelocity * 2.0f;
787     const float currentSpeed = mBallVelocity.Length();
788     const float limitedSpeed = std::min( currentSpeed, MAX_VELOCITY );
789     mBallVelocity = mBallVelocity * limitedSpeed / currentSpeed;
790
791     ContinueAnimation();
792
793     // remove collision-constraint and notification.
794     brick.RemovePropertyNotification(source);
795     brick.RemoveConstraints();
796
797     // fade brick (destroy)
798     Animation destroyAnimation = Animation::New(0.5f);
799     destroyAnimation.AnimateTo( Property( brick, Actor::Property::COLOR_ALPHA ), 0.0f, AlphaFunction::EASE_IN );
800     destroyAnimation.Play();
801     destroyAnimation.FinishedSignal().Connect( this, &ExampleController::OnBrickDestroyed );
802     mDestroyAnimationMap[destroyAnimation] = brick;
803   }
804
805   /**
806    * Brick Destruction Animation complete.
807    * @param[in] source The animation responsible for destroying the brick
808    */
809   void OnBrickDestroyed( Animation& source )
810   {
811     // Remove brick from window, it's constraint and property notification should also remove themselves.
812     Actor brick = mDestroyAnimationMap[source];
813     mDestroyAnimationMap.erase(source);
814     brick.GetParent().Remove(brick);
815     mBrickCount--;
816
817     if(!mBrickCount)
818     {
819       mLevel++;
820       LoadLevel(mLevel);
821     }
822   }
823
824   /**
825    * Main key event handler
826    */
827   void OnKeyEvent(const KeyEvent& event)
828   {
829     if(event.GetState() == KeyEvent::DOWN)
830     {
831       if( IsKey( event, Dali::DALI_KEY_ESCAPE) || IsKey( event, Dali::DALI_KEY_BACK) )
832       {
833         mApplication.Quit();
834       }
835     }
836   }
837
838 private:
839
840   Application& mApplication;                            ///< Application instance
841   Toolkit::Control mView;                               ///< The View instance.
842   Layer mContentLayer;                                  ///< The content layer (contains game actors)
843   ImageView mBall;                                      ///< The Moving ball image.
844   Vector3 mBallStartPosition;                           ///< Ball Start position
845   Vector3 mBallVelocity;                                ///< Ball's current direction.
846   Animation mBallAnimation;                             ///< Ball's animation
847   Actor mPaddle;                                        ///< The paddle including hit area.
848   ImageView mPaddleImage;                               ///< The paddle's image.
849   ImageView mPaddleHandle;                              ///< The paddle's handle (where the user touches)
850   Vector2 mPaddleHitMargin;                             ///< The paddle hit margin.
851   Animation mWobbleAnimation;                           ///< Paddle's animation when hit (wobbles)
852   Property::Index mWobbleProperty;                      ///< The wobble property (generated from animation)
853   Actor mLevelContainer;                                ///< The level container (contains bricks)
854   Property::Map mBrickImageMap;                       ///< The property map used to load the brick
855
856   // actor - dragging functionality
857
858   Animation mDragAnimation;                             ///< Animation for dragging. (grows - affects ACTOR::SCALE)
859   Actor mDragActor;                                     ///< The actor which is being dragged (if any)
860   Vector3 mRelativeDragPoint;                           ///< The point the user touched, relative to the actor.
861   std::map<Animation, Actor> mDestroyAnimationMap;      ///< Keep track of which actors are to be destroyed.
862   Vector2 mPaddleFullSize;                              ///< Initial 100% size of the paddle.
863   int mLevel;                                           ///< Current level
864   int mLives;                                           ///< Total lives.
865   int mBrickCount;                                      ///< Total bricks on screen.
866 };
867
868 int DALI_EXPORT_API main(int argc, char **argv)
869 {
870   Application app = Application::New(&argc, &argv, DEMO_THEME_PATH);
871   ExampleController test(app);
872   app.MainLoop();
873   return 0;
874 }