Learn what Felgo offers to help your business succeed. Start your free evaluation today! Felgo for Your Business

How to make 2048 with Felgo - Colors and Animations

\contentspageHow to make 2048 with Felgo - Colors and Animations

Final Touches

For a complete picture just a few small details are missing. I’m talking about animations and colors. In order to add those we are going to work with our old buddy Tile.qml.

Tile.qml

 import QtQuick 2.2
 import Felgo 4.0

 EntityBase{
   id: tile
   entityType: "Tile"

   width: gridWidth / gridSizeGame
   height: width // square so height=width

   property int tileIndex // is responsible for a position of the tile
   property int tileValue // tileValue is what gets incremented every time 2 tiles get merged
   property color tileColor
   property color tileTextColor: "white"
   property string tileText

   // tileFontSize is deduced from the tile width, therefore it will always fit into the tile(up to 10^5)
   property int tileFontSize: width/3

   // animationDuration is responsible for how long an animation will go
   // 500ms for desktop and 250ms for mobiles
   property int animationDuration: system.desktopPlatform ? 500 : 250

   // tileColor corresponds to the tileValue
   property var bgColors: ["#000000", "#88B605", "#587603", "#293601", "#48765F", "#1A9C70", "#14C3B6", "#1591B6", "#1668C3", "#1C15B6", "#821DB6", "#C31555"]

   // tile rectangle
   Rectangle {
     id: innerRect
     anchors.centerIn: parent // center this object in the invisible "EntityBase"
     width: parent.width-2 // -2 is the width offset, set it to 0 is no offset is needed
     height: width // square so height=width
     radius: 4 // radius of tile corners
     color: bgColors[tileValue]

     // tile text
     Text {
       id: innerRectText
       anchors.centerIn: parent // center this object in the "innerRect"
       color: tileTextColor
       font.pixelSize: tileFontSize
       text: Math.pow(2, tileValue) // tileValue gets squared according to the 2048 rules (1,2,3) ->(2,4,6)
     }
   }

   // startup position calculation
   Component.onCompleted: {
     x = (width) * (tileIndex % gridSizeGame) // we get the current row and multiply with the width to get the current position
     y = (height) * Math.floor(tileIndex/gridSizeGame) // we get the current column and multiply with the width to get the current position
     tileValue = Math.random() < 0.9 ? 1 : 2 // a new tile has 10% = 4 and 90% = 2
     showTileAnim.start() // new tile animation trigger
   }

   // this methods gets called every time a tile moves
   // it has exactly the same kind of behavior as our previous method
   // however, the values get transfered to a targetPoint to simplify our movement animations
   function moveTile(newTileIndex) {
     tileIndex = newTileIndex
     moveTileAnim.targetPoint.x = ((width) * (tileIndex % gridSizeGame))
     moveTileAnim.targetPoint.y = ((height) * Math.floor(tileIndex/gridSizeGame))
     moveTileAnim.start()
   }

   function destroyTile() { // trigger tile death animation
     deathAnimation.start()
   }

   // in a parallel animation, animations which are inside will run at the same time
   ParallelAnimation {
     id: showTileAnim

     // number animation works with any real number
     NumberAnimation {
       target: innerRect // specify the target of the animation
       property: "opacity" // specify the property that will be animated
       from: 0.0
       to: 1.0
       duration: animationDuration
     }

     // ScaleAnimator used for scaling
     ScaleAnimator {
       target: innerRect
       from: 0
       to: 1
       duration: animationDuration
       easing.type: Easing.OutQuad // Easing used to put some live action in your animation
     }
   }

   // movement animation
   ParallelAnimation {
     id: moveTileAnim
     property point targetPoint: Qt.point(0,0)
     NumberAnimation {
       target: tile
       property: "x"
       duration: animationDuration/2
       to: moveTileAnim.targetPoint.x
     }
     NumberAnimation {
       target: tile
       property: "y"
       duration: animationDuration/2
       to: moveTileAnim.targetPoint.y
     }
   }

   // a ScriptAction is treated like an animation, so the SequentialAnimation will call this script after the previous animation has finished
   // in other words, when the tile is completely faded out, it will be removed
   SequentialAnimation {
     id: deathAnimation
     NumberAnimation {
       target: innerRect
       property: "opacity"
       from: 1
       to: 0
       duration: animationDuration/2
     }
     ScriptAction {
       script: removeEntity() // removesEntity from the game
     }
   }
 }

It definitely looks longer now, but the truth is only a few things got changed.

We added colors, so when a tile gets incremented it changes its color.

In addition our moveTile() function has been edited a bit. Instead of setting its position by itself, it sends the next position coordinates to the animation method.

Speaking of animation, at the moment we have several NumberAnimation elements and one ScaleAnimator. They all behave in a very similar pattern. You specify an id of a target you want to affect, a property you want to affect, from-to values, and how long the animation should take to complete.

ParallelAnimation plays all its children animation at the same time. While SequentialAnimation plays its children one after another.

As you can see, we make the entity removal a part of the death animation. We put it in a ScriptAction of the SequentialAnimation so it gets played after opacity reaches 0.

Just for information, you can also nest ParallelAnimation and SequentialAnimation , e.g. run a number of SequentialAnimation parallel.

Go run your game and enjoy!

You can also try different grid sizes. Just change gridSize number in the Main.qml

Final Word

Congratulations, you just completed this 2048 game tutorial!

As in any development process there is still plenty of space for modifications and improvement. For example the game still lacking gameover condition and there is no highscore counter. You can try to make it on your own with the help of existing Felgo Tutorials, components and plugins, or you could take a break and wait for the second part of this tutorial.

Got a question? - don't hesitate to visit our support forums!

Qt_Technology_Partner_RGB_475 Qt_Service_Partner_RGB_475_padded