How to create mobile games with multiple scenes and multiple levels

Introduction

Multiple scenes, like menu, credits or preferences, and multiple levels are essential to nearly any good game. We will take a look at how easy it is to cover those requirements and set up a well-structured project with V-Play.

The code, that we will build step by step, is also available as a template for new V-Play 2 projects. Click File/New Project in Qt Creator and then select Template - Multi Scene Multi Levels to create a new project based on this template.

This tutorial assumes you already had a look at Getting Started with V-Play and Qt Creator. They provide you with the very basics which won't be fully explained in this tutorial. Furthermore our project will support different screen sizes and resolutions. You can learn more about that here: How to create mobile games for different screen sizes and resolutions

Resources

Resources for this project are available for you to download right here: Download Resources

New Project

Create a new Empty V-Play 2 Project with a name of your choice. Then extract the resource files that you just downloaded into the assets folder of your project.

Let's quickly summarize what we are about to create:

  • menu scene as our home scene
  • credits scene
  • a scene where we can select a level
  • and the game scene to play the selected level

First, we cut down the Code in the main.qml to the minimum, like this:

import VPlay 2.0
import QtQuick 2.0

GameWindow {
  id: gameWindow

  // create and remove entities at runtime
  EntityManager {
    id: entityManager
  }

}

We won't need the functionality of the EntityManager in this tutorial, but you will most likely need it in a real game, so adding the EntityManager won't hurt nobody.

Now the GameWindow is ready to carry our scenes, so let's create some.

SceneBase and Scenes

There could be a couple of characteristics that are exactly the same in each of the scenes, so smart people (like you and me are!) plan ahead and create a SceneBase as a base component for all our scenes.

Create a folder with the name common inside the qml folder of your project. Then add a SceneBase.qml file to your common folder

import VPlay 2.0
import QtQuick 2.0

Scene {
  id: sceneBase
}

We will add more code later, as we encounter things that will fit in this base component.

Now we use the SceneBase to create our scenes and add different background rectangles to each of them plus a "logo" for the menu scene. Create a folder with the name scenes inside the qml folder of your project. Add the following files, each containing the code below it, to your scenes folder:

MenuScene.qml

import VPlay 2.0
import QtQuick 2.0
import "../common"

SceneBase {
  id:menuScene

  // background
  Rectangle {
    anchors.fill: parent.gameWindowAnchorItem
    color: "#47688e"
  }

  // the "logo"
  Text {
    anchors.horizontalCenter: parent.horizontalCenter
    y: 30
    font.pixelSize: 30
    color: "#e9e9e9"
    text: "MultiSceneMultiLevel"
  }
}

Note that we have to import the common folder at the top of the code, because the SceneBase.qml file is located in that directory, while our MenuScene is in the scenes directory.

CreditsScene.qml

import VPlay 2.0
import QtQuick 2.0
import "../common"

SceneBase {
  id:creditsScene

  // background
  Rectangle {
    anchors.fill: parent.gameWindowAnchorItem
    color: "#49a349"
  }
}

SelectLevelScene.qml

import VPlay 2.0
import QtQuick 2.0
import "../common"

SceneBase {
  id:selectLevelScene

  // background
  Rectangle {
    anchors.fill: parent.gameWindowAnchorItem
    color: "#ece468"
  }
}

GameScene.qml

import VPlay 2.0
import QtQuick 2.0
import "../common"

SceneBase {
  id:gameScene

  // background
  Rectangle {
    anchors.fill: parent.gameWindowAnchorItem
    color: "#dd94da"
  }
}

The next step is to add all those scenes to our main.qml, which should then look like this

import VPlay 2.0
import QtQuick 2.0
import "../scenes"

GameWindow {
  id: gameWindow

  // menu scene
  MenuScene {
    id: menuScene
  }

  // scene for selecting levels
  SelectLevelScene {
    id: selectLevelScene
  }

  // credits scene
  CreditsScene {
    id: creditsScene
  }

  // game scene to play a level
  GameScene {
    id: gameScene
  }
}

Awesome, if we run the project we got 4 scenes, overlaying each other, so effectively we only see the last one added, which is the GameScene. Although that's a very nice pink, it needs some improvement!

Controlling the visibility

The easiest way to only show one scene is to hide all scenes by default, and only display the scene we currently need. Wait what do I read there, hide ALL scenes? Now that should be no problem, thanks to our SceneBase.

Add one line to SceneBase.qml and all our scenes are hidden by default

Scene {
  id: sceneBase

  // by default, set the opacity to 0 - this will be changed from the main.qml with PropertyChanges
  opacity: 0
  // we set the visible property to false if opacity is 0 because the renderer skips invisible items, this is an performance improvement
  visible: opacity > 0
  // if the scene is invisible, we disable it. In Qt 5, components are also enabled if they are invisible. This means any MouseArea in the Scene would still be active even we hide the Scene, since we do not want this to happen, we disable the Scene (and therefore also its children) if it is hidden
  enabled: visible
}

But how do I set the current scene visible? And what is that first comment in the code talking about? PropertyChanges?

Add this at the end of your main.qml

//...

GameWindow {

  //...

  // default state is menu -> default scene is menuScene
  state: "menu"

  // state machine, takes care reversing the PropertyChanges when changing the state like changing the opacity back to 0
  states: [
    State {
      name: "menu"
      PropertyChanges {target: menuScene; opacity: 1}
      PropertyChanges {target: gameWindow; activeScene: menuScene}
    },
    State {
      name: "selectLevel"
      PropertyChanges {target: selectLevelScene; opacity: 1}
      PropertyChanges {target: gameWindow; activeScene: selectLevelScene}
    },
    State {
      name: "credits"
      PropertyChanges {target: creditsScene; opacity: 1}
      PropertyChanges {target: gameWindow; activeScene: creditsScene}
    },
    State {
      name: "game"
      PropertyChanges {target: gameScene; opacity: 1}
      PropertyChanges {target: gameWindow; activeScene: gameScene}
    }
  ]
}

There is our answer. We use a state machine. The GameWindow has a property state which we use to define our current scene.

Our default scene is the MenuScene, so we write state: "menu". We just changed the value of the state property and that triggers our state machine. It will browse through the states we defined, and execute the corresponding PropertyChanges. In our case, we set the opacity of the MenuScene to 1, making it visible while the other scenes stay hidden.

If we change the state again, the state machine will take care that the PropertyChanges of the old state will be reversed. So e.g. if we would now change the state to "credits", the opacity of the MenuScene will be automatically changed to 0 again, and the CreditsScene will have its opacity changed to 1.

We are also changing the activeScene property of the GameWindow to set the focus on the current scene, you will learn why we do this later in this tutorial.

If we run the project now, we see the right home scene, but that's about it. Time to add some navigation.

Buttons please

We will create a small reusable button for all our menu items.

Add a MenuButton.qml file to the common folder, with this content:

import QtQuick 2.0

Rectangle {
  id: button
  // this will be the default size, it is same size as the contained text + some padding
  width: buttonText.width+ paddingHorizontal*2
  height: buttonText.height+ paddingVertical*2

  color: "#e9e9e9"
  // round edges
  radius: 10

  // the horizontal margin from the Text element to the Rectangle at both the left and the right side.
  property int paddingHorizontal: 10
  // the vertical margin from the Text element to the Rectangle at both the top and the bottom side.
  property int paddingVertical: 5

  // access the text of the Text component
  property alias text: buttonText.text

  // this handler is called when the button is clicked.
  signal clicked

  Text {
    id: buttonText
    anchors.centerIn: parent
    font.pixelSize: 18
    color: "black"
  }

  MouseArea {
    id: mouseArea
    anchors.fill: parent
    hoverEnabled: true
    onClicked: button.clicked()
    onPressed: button.opacity = 0.5
    onReleased: button.opacity = 1
  }
}

It's Time for Change! (changing the scenes)

Our MenuScene will have 2 buttons. One to go to the selectLevelScene, and the other one to go to the CreditsScene.

Add this to the MenuScene.qml

//...

SceneBase {
  id: menuScene
  // signal indicating that the selectLevelScene should be displayed
  signal selectLevelPressed
  // signal indicating that the creditsScene should be displayed
  signal creditsPressed

  //...

  // menu
  Column {
    anchors.centerIn: parent
    MenuButton {
      text: "Levels"
      onClicked: selectLevelPressed()
    }
    MenuButton {
      text: "Credits"
      onClicked: creditsPressed()
    }
  }
}

To make our code cleaner and easier to maintain, we will put all the logic for changing the scenes in our GameWindow in the main.qml. The scenes will only emit a signal which button has been pressed, and the GameWindow will listen to those signals and change the states (and scenes).

What we are missing now is the listener for these signals.

Add this to your main.qml, more precisely the MenuScene

  //...

  MenuScene {
    id: menuScene
    // listen to the button signals of the scene and change the state according to it
    onSelectLevelPressed: gameWindow.state = "selectLevel"
    onCreditsPressed: gameWindow.state = "credits"
  }

  //...

If we run the project, we can switch from the MenuScene to other scenes, but we can't go back, that's not very handy. Every scene will have some kind of back functionality, even the MenuScene which we will take care of at the very end of this tutorial. Luckily, the Scene already support back button support with Scene::backButtonPressed.

Thanks to that, every scene can send a request to go back. All we need is a simple back button and listen to that signal.

Add this to CreditsScene.qml, the SelectLevelScene.qml and the GameScene.qml

  // back button to leave scene
  MenuButton {
    text: "Back"
    anchors.right: selectLevelScene.gameWindowAnchorItem.right
    anchors.rightMargin: 10
    anchors.top: selectLevelScene.gameWindowAnchorItem.top
    anchors.topMargin: 10
    onClicked: backButtonPressed()
  }

And then add listeners to main.qml like this

  //...

  SelectLevelScene {
    id: selectLevelScene
    onBackButtonPressed: gameWindow.state = "menu"
  }

  //...
  //...

  CreditsScene {
    id: creditsScene
    onBackButtonPressed: gameWindow.state = "menu"
  }

  //...
  //...

  GameScene{
    id: gameScene
    onBackButtonPressed: gameWindow.state = "selectLevel"
  }

  //...

Go ahead, run the project and enjoy jumping around between the scenes, what a pleasure!

Creating the levels

Before we can talk about the levels, we gotta figure out what our game actually is about. We will keep it as simple as it gets, since this tutorial is not primarily about creating a game. But I will use the simple game mechanics to show you some stuff that could be useful to you as well.

The game will be about having a simple rectangle, that moves or jumps around, and you score a point for each successful tap on it. Again, we will try to separate the components as good as possible, to make the code easier to understand and maintain. The Level will only handle how the rectangle behaves and moves, and will emit a signal if the rectangle is tapped successfully. The GameScene will listen to this signal and increase the score.

Since all of our levels will need the signal that is emitted when the rectangle is pressed, we will create a LevelBase and use it as the base component for our levels, just like we did it with the scenes.

Furthermore, we will load the levels dynamically, having only the one we currently play consume memory.

Create a Levelbase.qml file in the common folder and add this content:

import QtQuick 2.0

Item {
  // this will be displayed in the GameScene
  property string levelName
  // this is emitted whenever the rectangle has been tapped successfully, the GameScene will listen to this signal and increase the score
  signal rectanglePressed
}

Now we will create 3 levels and I will add a bit of explanation to each one of them.

Create a levels folder in your qml folder and add a Level1.qml file to it. Then add this to your Level1.qml file:

import QtQuick 2.0
import VPlay 2.0
import "../common" as Common

Common.LevelBase {
  levelName: "Level1"

  Rectangle {
    color: "orange"
    width: 100
    height: 100
    radius: 10
    anchors.centerIn: parent
    MouseArea {
      anchors.fill: parent
      // since the level is loaded in the gameScene, and is therefore a child of the gameScene, you could also access gameScene.score here and modify it. But we want to keep the logic in the gameScene rather than spreading it all over the place
      onPressed: rectanglePressed()
    }
  }
}

Important thing to note, V-Play already has a LevelBase component, for use with the LevelLoader and the LevelEditor. Since we created our own one, and want to use this one, we need to add a qualifier (pretty much like a namespace) to it. We are doing this by adding as Common to the import of the common folder. And then use Common.LevelBase to make sure the right one is used.

The rest is pretty straightforward. Just a rectangle with a MouseArea, and if it is pressed, we call our signal to indicate that the rectangle is pressed successfully.

Now create a Level2.qml file to the levels folder and add this code:

import QtQuick 2.0
import VPlay 2.0
import "../common" as Common

Common.LevelBase {
  levelName: "Level2"

  Rectangle {
    id: rectangle
    color: "cyan"
    width: 100
    height: 100
    radius: 10
    property bool togglePosition: false
    anchors.horizontalCenter: parent.horizontalCenter
    // this property binding changes the horizontal offset from the center each time togglePosition changes
    anchors.horizontalCenterOffset: togglePosition ? -100 : 100
    anchors.verticalCenter: parent.verticalCenter
    MouseArea {
      anchors.fill: parent
      onPressed: {
        // every time the rectangle is pressed, we toggle its position by changing the horizontal offset from the center
        rectangle.togglePosition = !rectangle.togglePosition
        rectanglePressed()
      }
    }
  }
}

That one is a bit more interesting. We want the rectangle to jump from left to right and vice versa each time we tap it. We are using a custom property togglePosition which is either true or false to indicate if the rectangle is left or right. First of all we center the rectangle in the level, and then we add a property binding on the horizontalCenterOffset property to move it 100 pixel to the left or right of the center.

Next create a Level3.qml file to the levels folder and add this code:

import QtQuick 2.0
import VPlay 2.0
import "../common" as Common

Common.LevelBase {
  levelName: "Level3"

  Rectangle {
    id: rectangle
    color: "blue"
    width: 100
    height: 100
    radius: 10
    anchors.horizontalCenter: parent.horizontalCenter
    anchors.horizontalCenterOffset: 120
    anchors.verticalCenter: parent.verticalCenter
    MouseArea {
      anchors.fill: parent
      onPressed: rectanglePressed()
    }
  }

  // SequentialAnimation plays all its child animations one after the other
  // we are moving the rectangle by changing its horizontal offset from the center
  SequentialAnimation {
    running: true
    // let it run forever
    loops: Animation.Infinite
    // move the rectangle left by changing the offset from current (120) to -120
    NumberAnimation {
      target: rectangle
      duration: 1500
      property: "anchors.horizontalCenterOffset"
      easing.type: Easing.InOutQuad
      to: -120
    }
    // after moving left has finished, we move the rectangle right by changing the offset from current (-120) to 120
    NumberAnimation {
      target: rectangle
      duration: 1500
      property: "anchors.horizontalCenterOffset"
      easing.type: Easing.InOutQuad
      to: 120
    }
  }
}

Animation time! We are using the SequentialAnimation component, that will play all the child animations one after the other. Like in the 2nd level, we are using the horizontalCenterOffset property to change the position of the rectangle relative to the center of the level. The start offset is set to 120 pixel. The first animation will change this to -120 pixel, the second one back to 120 pixel. Then the first animation is played again since we have set the SequentialAnimation to loops: Animation.Infinite.

Time to select and play a level.

Selecting a level

We start with adding some code to SelectLevelScene.qml

// ...

SceneBase {
  id: selectLevelScene

  // ...

  // signal indicating that a level has been selected
  signal levelPressed(string selectedLevel)

  // levels to be selected
  Grid {
    anchors.centerIn: parent
    spacing: 10
    columns: 5
    MenuButton {
      text: "1"
      width: 50
      height: 50
      onClicked: {
        levelPressed("Level1.qml")
      }
    }
    MenuButton {
      text: "2"
      width: 50
      height: 50
      onClicked: {
        levelPressed("Level2.qml")
      }
    }
    MenuButton {
      text: "3"
      width: 50
      height: 50
      onClicked: {
        levelPressed("Level3.qml")
      }
    }
    Repeater {
      model: 10
      MenuButton {
        text: " "
        width: 50
        height: 50
      }
    }
  }
}

We added a signal that indicates that a level has been selected, and pass the filename of the selected level as a parameter. The level selection is a Grid where we added 3 MenuButtons for our levels and some empty ones with a Repeater, just to have some content.

Passing the selected Level to the GameScene

Go to the main.qml file and change the code of the LevelSelectionScene component like this:

  // scene for selecting levels
  SelectLevelScene {
    id: selectLevelScene
    onLevelPressed: {
      // selectedLevel is the parameter of the levelPressed signal
      gameScene.setLevel(selectedLevel)
      gameWindow.state = "game"

    }
    onBackButtonPressed: gameWindow.state = "menu"
  }

Now if a level is selected, we pass the selected level to the GameScene and switch the state to "game", which will cause the scenes to change. Before we can run this code without any errors, we need to add the setLevel function to our GameScene.

The GameScene

I explain the code part by part as we add it, at the end of this section I will also post the full source code of the GameScene for you to copy and paste.

Go to the GameScene.qml file and change the content to this:

import VPlay 2.0
import QtQuick 2.0
import "../common"

SceneBase {
  id:gameScene
  // the filename of the current level gets stored here, it is used for loading the
  property string activeLevelFileName
  // the currently loaded level gets stored here
  property variant activeLevel

  // set the name of the current level, this will cause the Loader to load the corresponding level
  function setLevel(fileName) {
    activeLevelFileName = fileName
  }

  // background
  Rectangle {
    anchors.fill: parent.gameWindowAnchorItem
    color: "#dd94da"
  }

  // back button to leave scene
  MenuButton {
    text: "Back to menu"
    // anchor the button to the gameWindowAnchorItem to be on the edge of the screen on any device
    anchors.right: gameScene.gameWindowAnchorItem.right
    anchors.rightMargin: 10
    anchors.top: gameScene.gameWindowAnchorItem.top
    anchors.topMargin: 10
    onClicked: {
      backButtonPressed()
      activeLevel = undefined
      activeLevelFileName = ""
    }
  }

  // load levels at runtime
  Loader {
    id: loader
    source: activeLevelFileName !== "" ? "../levels/" + activeLevelFileName : ""
    onLoaded: {
      // since we did not define a width and height in the level item itself, we are doing it here
      item.width = gameScene.width
      item.height = gameScene.height
      // store the loaded level as activeLevel for easier access
      activeLevel = item
    }
  }
}

There you go, we got the function to set the level filename. The real magic happens in the Loader element though. It loads the component that is defined in the source property. Setting the source to "" will cause the Loader element to unload the level. So if we are passing a level filename via the setLevel function, we are changing the activeLevelFileName to something different than "" and this will make our Loader element load the level. Once it is loaded, we can access it with item in the onLoaded handler. Note that we are reseting (and therefore unloading) the level when pressing the "Back" button.

Since we defined a levelName for each level, why not display it?! Add this:

  // name of the current level
  Text {
    anchors.left: gameScene.gameWindowAnchorItem.left
    anchors.leftMargin: 10
    anchors.top: gameScene.gameWindowAnchorItem.top
    anchors.topMargin: 10
    color: "white"
    font.pixelSize: 20
    text: activeLevel !== undefined ? activeLevel.levelName : ""
  }

What we are missing now is some game logic, so change your code like this:

// ...

SceneBase {
  id:gameScene
  // the filename of the current level gets stored here, it is used for loading the
  property string activeLevelFileName
  // the currently loaded level gets stored here
  property variant activeLevel
  // score
  property int score: 0

  // ...

  // load levels at runtime
  Loader {
    id: loader
    source: activeLevelFileName !== "" ? "../levels/" + activeLevelFileName : ""
    onLoaded: {
      // reset the score
      score = 0
      // since we did not define a width and height in the level item itself, we are doing it here
      item.width = gameScene.width
      item.height = gameScene.height
      // store the loaded level as activeLevel for easier access
      activeLevel = item
    }
  }

  // we connect the gameScene to the loaded level
  Connections {
    // only connect if a level is loaded, to prevent errors
    target: activeLevel !== undefined ? activeLevel : null
    // increase the score when the rectangle is clicked
    onRectanglePressed: {
      score++
    }
  }

  // display the current score
  Text {
    anchors.horizontalCenter: parent.horizontalCenter
    anchors.top: gameScene.gameWindowAnchorItem.top
    anchors.topMargin: 30
    color: "white"
    font.pixelSize: 40
    text: score
  }
}

If a level is loaded, we connect to it and listen to the rectanglePressed signal. If it is emitted, we increase our score. Additionally we display the score on the screen. That was way too easy, right? We will just add a little more to make the "game" feel better. A countdown at the start of the game, so the player can mentally prepare for the upcoming tap tap tap tap tap ...

Just a little more modification:

// ...

SceneBase {
  id:gameScene
  // the filename of the current level gets stored here, it is used for loading the
  property string activeLevelFileName
  // the currently loaded level gets stored here
  property variant activeLevel
  // score
  property int score: 0
  // countdown shown at level start
  property int countdown: 0
  // flag indicating if game is running
  property bool gameRunning: countdown === 0

  // ...

  // load levels at runtime
  Loader {
    id: loader
    source: activeLevelFileName !== "" ? "../levels/" + activeLevelFileName : ""
    onLoaded: {
      // reset the score
      score = 0
      // since we did not define a width and height in the level item itself, we are doing it here
      item.width = gameScene.width
      item.height = gameScene.height
      // store the loaded level as activeLevel for easier access
      activeLevel = item
      // restarts the countdown
      countdown = 3
    }
  }

  // we connect the gameScene to the loaded level
  Connections {
    // only connect if a level is loaded, to prevent errors
    target: activeLevel !== undefined ? activeLevel : null
    // increase the score when the rectangle is clicked
    onRectanglePressed: {
      // only increase score when game is running
      if(gameRunning) {
        score++
      }
    }
  }

  // ...

  // text displaying either the countdown or "tap!"
  Text {
    anchors.centerIn: parent
    color: "white"
    font.pixelSize: countdown > 0 ? 160 : 18
    text: countdown > 0 ? countdown : "tap!"
  }

  // if the countdown is greater than 0, this timer is triggered every second, decreasing the countdown (until it hits 0 again)
  Timer {
    repeat: true
    running: countdown > 0
    onTriggered: {
      countdown--
    }
  }
}

We make heavy use of property bindings here. First we have a gameRunning property that only becomes true if the countdown property is 0. Then we got a Timer that is triggered every second (1000ms is the default interval) but is only running if the countdown is greater than 0. And then we got a Text element that displays the countdown if it is bigger then 0, or shows "tap!" once the countdown becomes 0. And to start this whole countdown machine, all we have to do is set the countdown property to anything bigger than 0, in our case we set it to 3 when the level is loaded. Our score will only get increased if the game is running.

The whole source code of the GameScene.qml file should now look something like this:

import VPlay 2.0
import QtQuick 2.0
import "../common"

SceneBase {
  id:gameScene
  // the filename of the current level gets stored here, it is used for loading the
  property string activeLevelFileName
  // the currently loaded level gets stored here
  property variant activeLevel
  // score
  property int score: 0
  // countdown shown at level start
  property int countdown: 0
  // flag indicating if game is running
  property bool gameRunning: countdown === 0

  // set the name of the current level, this will cause the Loader to load the corresponding level
  function setLevel(fileName) {
    activeLevelFileName = fileName
  }

  // background
  Rectangle {
    anchors.fill: parent.gameWindowAnchorItem
    color: "#dd94da"
  }

  // back button to leave scene
  MenuButton {
    text: "Back to menu"
    // anchor the button to the gameWindowAnchorItem to be on the edge of the screen on any device
    anchors.right: gameScene.gameWindowAnchorItem.right
    anchors.rightMargin: 10
    anchors.top: gameScene.gameWindowAnchorItem.top
    anchors.topMargin: 10
    onClicked: {
      backButtonPressed()
      activeLevel = undefined
      activeLevelFileName = ""
    }
  }

  // name of the current level
  Text {
    anchors.left: gameScene.gameWindowAnchorItem.left
    anchors.leftMargin: 10
    anchors.top: gameScene.gameWindowAnchorItem.top
    anchors.topMargin: 10
    color: "white"
    font.pixelSize: 20
    text: activeLevel !== undefined ? activeLevel.levelName : ""
  }

  // load levels at runtime
  Loader {
    id: loader
    source: activeLevelFileName !== "" ? "../levels/" + activeLevelFileName : ""
    onLoaded: {
      // reset the score
      score = 0
      // since we did not define a width and height in the level item itself, we are doing it here
      item.width = gameScene.width
      item.height = gameScene.height
      // store the loaded level as activeLevel for easier access
      activeLevel = item
      // restarts the countdown
      countdown = 3
    }
  }

  // we connect the gameScene to the loaded level
  Connections {
    // only connect if a level is loaded, to prevent errors
    target: activeLevel !== undefined ? activeLevel : null
    // increase the score when the rectangle is clicked
    onRectanglePressed: {
      // only increase score when game is running
      if(gameRunning) {
        score++
      }
    }
  }

  // name of the current level
  Text {
    anchors.horizontalCenter: parent.horizontalCenter
    anchors.top: gameScene.gameWindowAnchorItem.top
    anchors.topMargin: 30
    color: "white"
    font.pixelSize: 40
    text: score
  }

  // text displaying either the countdown or "tap!"
  Text {
    anchors.centerIn: parent
    color: "white"
    font.pixelSize: countdown > 0 ? 160 : 18
    text: countdown > 0 ? countdown : "tap!"
  }

  // if the countdown is greater than 0, this timer is triggered every second, decreasing the countdown (until it hits 0 again)
  Timer {
    repeat: true
    running: countdown > 0
    onTriggered: {
      countdown--
    }
  }
}

Sweet, the core functionality is finished. Time to get some rest? NOPE, we ain't no lazy people. We add just a little more features.

We want it smooth! (fading effect)

We are using the opacity property to set the scenes visible and invisible. Since the opacity is a value of the QML type real (which is also known as float or double in other languages) within the range between 0 and 1, we can create a fade effect when switching scenes just by animating it.

All you have to do is add this to the SceneBase.qml file:

// ...

Scene {
  id: sceneBase

  // ...

  // every change in opacity will be done with an animation
  Behavior on opacity {
    NumberAnimation {property: "opacity"; easing.type: Easing.InOutQuad}
  }
}

Oh boy, thats so smooooooth!

Android hardware back button

For Android users, the hardware back button is the common way to navigate backwards in applications. Luckily, the Scene already supports this with Scene::backButtonPressed.

An important thing to know is that any hardware button press only gets handled in the scene that is focused. Being displayed doesn't automatically mean that something is focused, so we got to take care of that.

This is the main reason why we set the activeScene property of the GameWindow in our state machine in the main.qml because it will automatically call forceActiveFocus() on that scene.

If we change a scene, we set the new one as activeScene, that's all the magic.

Close the game

What if we press the hardware back button in our home scene (MenuScene)? Normally that should cause the game to be closed on Android. So let's add this, including a message box, asking the user if he really wants to quit the game.

Add this to the MenuScene in main.qml

  // menu scene
  MenuScene {
    id: menuScene
    // listen to the button signals of the scene and change the state according to it
    onSelectLevelPressed: gameWindow.state = "selectLevel"
    onCreditsPressed: gameWindow.state = "credits"
    // the menu scene is our start scene, so if back is pressed there we ask the user if he wants to quit the application
    onBackButtonPressed: {
      nativeUtils.displayMessageBox("Really quit the game?", "", 2);
    }
    // listen to the return value of the MessageBox
    Connections {
      target: nativeUtils
      onMessageBoxFinished: {
        // only quit, if the activeScene is menuScene - the messageBox might also get opened from other scenes in your code
        if(accepted && window.activeScene === menuScene)
          Qt.quit()
      }
    }
  }

If the signal backPressed gets called in the MenuScene, we display the message box. With the Connections element we check if the player pressed yes. In this case we close the game with Qt.quit().

That's it for now! If you have any questions regarding this tutorial, don't hesitate to visit the support forums.

Visit V-Play Engine Examples and Demos to gain more information about game creation with V-Play and to see the source code of existing apps in the app stores.

Further perspectives

  • Loading scenes at runtime is also possible, just like loading levels, but this can become a bit tricky. If you need this feature now, you can take a look at the Squaby Demo source code to get a thought-provoking impulse.
  • Also different animations between scenes are possible. Dig deeper into Animation and Transitions in Qt Quick to find out more about that.

Voted #1 for:

  • Easiest to learn
  • Most time saving
  • Best support

Develop Cross-Platform Apps and Games 50% Faster!

  • Voted the best supported, most time-saving and easiest to learn cross-platform development tool
  • Based on the Qt framework, with native performance and appearance on all platforms including iOS and Android
  • Offers a variety of plugins to monetize, analyze and engage users
FREE!
create apps
create games
cross platform
native performance
3rd party services
game network
multiplayer
level editor
easiest to learn
biggest time saving
best support
Sign up for Free and start developing right away!
I want to make Games
I want to make Apps
Game Development
Cross-Platform, Any Resolution

Use one IDE to deploy to all supported platforms, including iOS and Android, from a single code base.

Support all screen resolutions, aspect ratios and sizes with auto-adapting UI elements, smart scene scaling and memory efficient asset selection.

Learn More

V-Play ranked #1 at research2guidance cross-platform tool benchmarking 2014 by 2188 developers comparing 40 leading tools.

Multiplayer

V-Play Multiplayer supports both real-time and turn-based gameplay, so you can use it to make many different types of cross-platform multiplayer games. It's perfect for making player-vs-player games like 'Words with Friends' or games for a large amount of players, such as 'Clash of Clans'.

Learn More Video

Highscores & Achievements

The V-Play Game Network is a cross-platform gaming service that allows players around the world to compare game highscores and achievements. Players can also challenge each other across multiple platforms and share their progress on Facebook.

Learn More

QML & JavaScript

Qt Meta Language is a highly intuitive reactive language, which is super easy to learn, yet it's extremely powerful and flexible. Mix it with JavaScript to create awesome stuff, with just a few lines of code.

import QtQuick 2.0
import VPlay 2.0

GameWindow {
  Scene {
    
    SimpleButton {
      anchors.centerIn: parent
      text: "Press Me"
      onClicked: {
        console.debug("Wow you pressed me!")
      }
    }
  }
}

Learn More

Monetize & Analyze

With V-Play, you can use many 3rd party services for ads, in-app purchases, analytics and more, with just a few lines of code. All of these plugins work cross-platform, which allows you to maintain a single code base.

Learn More

Level Editor

The LevelEditor can be used during development to create and modify levels for your game, which you can then bundle in your final publishing build. Additionally, you can also integrate the in-game level editor to your published game and let your gamers create new levels.

Learn More Video

Card Game like UNO, Hearthstone or Poker

We got a demo game for you!

The V-Play SDK includes an open-source demo for this game genre. You can use its source code and build your game in record time. After installing V-Play, you can simply open the .pro file with Qt Creator, the development environment used for V-Play.

<Path to V-PlaySDK>/Examples/V-Play/demos/OneCard/OneCard.pro

Match-3 like Candy Crush Saga

We got a demo game for you!

The V-Play SDK includes an open-source demo for this game genre. You can use its source code and build your game in record time. After installing V-Play, you can simply open the .pro file with Qt Creator, the development environment used for V-Play.

<Path to V-PlaySDK>/Examples/V-Play/demos/JuicySquash/JuicySquash.pro

<Path to V-PlaySDK>/Examples/V-Play/demos/JuicySquashAdvanced/JuicySquashAdvanced.pro

Puzzle like 2048 or Threes!

We got a demo game for you!

The V-Play SDK includes an open-source demo for this game genre. You can use its source code and build your game in record time. After installing V-Play, you can simply open the .pro file with Qt Creator, the development environment used for V-Play.

<Path to V-PlaySDK>/Examples/V-Play/demos/2048/2048.pro

Casino like Big Win Slots

We got a demo game for you!

The V-Play SDK includes an open-source demo for this game genre. You can use its source code and build your game in record time. After installing V-Play, you can simply open the .pro file with Qt Creator, the development environment used for V-Play.

<Path to V-PlaySDK>/Examples/V-Play/demos/FlaskOfRum/FlaskOfRum.pro

Side Scroller like Jetpack Joyride or Flappy Bird

We got a demo game for you!

The V-Play SDK includes an open-source demo for this game genre. You can use its source code and build your game in record time. After installing V-Play, you can simply open the .pro file with Qt Creator, the development environment used for V-Play.

<Path to V-PlaySDK>/Examples/V-Play/demos/FlappyBird/FlappyBird.pro

Tower Defense like Castle Defense or Bloons TD

We got a demo game for you!

The V-Play SDK includes an open-source demo for this game genre. You can use its source code and build your game in record time. After installing V-Play, you can simply open the .pro file with Qt Creator, the development environment used for V-Play.

<Path to V-PlaySDK>/Examples/V-Play/demos/Squaby/Squaby.pro

Falldown & Jump like Doodle Jump or Mega Jump

We got a demo game for you!

The V-Play SDK includes an open-source demo for this game genre. You can use its source code and build your game in record time. After installing V-Play, you can simply open the .pro file with Qt Creator, the development environment used for V-Play.

<Path to V-PlaySDK>/Examples/V-Play/demos/ChickenOutbreak/ChickenOutbreak.pro

<Path to V-PlaySDK>/Examples/V-Play/demos/ChickenOutbreak2/ChickenOutbreak2.pro

<Path to V-PlaySDK>/Examples/V-Play/demos/DoodleJump/DoodleJump.pro

Platformer like Super Mario or Lep's World

We got a demo game for you!

The V-Play SDK includes an open-source demo for this game genre. You can use its source code and build your game in record time. After installing V-Play, you can simply open the .pro file with Qt Creator, the development environment used for V-Play.

<Path to V-PlaySDK>/Examples/V-Play/demos/Platformer/Platformer.pro

<Path to V-PlaySDK>/Examples/V-Play/demos/PlatformerWithLevelEditor/PlatformerWithLevelEditor.pro

Action like Angry Birds, Fruit Ninja, Cut the Rope

We got a demo game for you!

The V-Play SDK includes an open-source demo for this game genre. You can use its source code and build your game in record time. After installing V-Play, you can simply open the .pro file with Qt Creator, the development environment used for V-Play.

<Path to V-PlaySDK>/Examples/V-Play/demos/StackTheBoxWithCommunityEditor/StackTheBoxWithCommunityEditor.pro

<Path to V-PlaySDK>/Examples/V-Play/demos/BalloonPop/BalloonPop.pro

<Path to V-PlaySDK>/Examples/V-Play/demos/CarChallenge/CarChallenge.pro

Arcade like Arkanoid or Space Invaders

We got a demo game for you!

The V-Play SDK includes an open-source demo for this game genre. You can use its source code and build your game in record time. After installing V-Play, you can simply open the .pro file with Qt Creator, the development environment used for V-Play.

<Path to V-PlaySDK>/Examples/V-Play/demos/ZombieBreak/ZombieBreak.pro

Community like Super Mario Maker or Minecraft

We got a demo game for you!

The V-Play SDK includes an open-source demo for this game genre. You can use its source code and build your game in record time. After installing V-Play, you can simply open the .pro file with Qt Creator, the development environment used for V-Play.

<Path to V-PlaySDK>/examples/Squaby/Squaby.pro

<Path to V-PlaySDK>/Examples/V-Play/demos/StackTheBoxWithCommunityEditor/StackTheBoxWithCommunityEditor.pro

<Path to V-PlaySDK>/Examples/V-Play/demos/PlatformerWithLevelEditor/PlatformerWithLevelEditor.pro

Any other Idea? let us know how we can help you

You are looking for another demo?

The V-Play SDK includes many open-source demos for different game genres. You can use their source code and build your game in record time. After installing V-Play, you can simply open the .pro file with Qt Creator, the development environment used for V-Play.

If you do not find your game genre in this list and wonder if V-Play is the right choice, just contact us, we are happy to help!

Contact Us

App Development
Better Apps, Less Effort

Develop feature-rich, cross-platform mobile apps from a single code base.

V-Play apps look, feel and perform exactly like native iOS, Android and Desktop apps. With less code & much faster development time.

Learn More Highlights Video

V-Play ranked #1 at research2guidance cross-platform tool benchmarking 2014 by 2188 developers comparing 40 leading tools.

Save Time, Code & Money

Save up to 90% source code with V-Play, compared to frameworks like Xamarin, Titanium, React Native and others.

Learn More

QML & JavaScript

Qt Meta Language is a highly intuitive reactive language, which is super easy to learn, yet it's extremely powerful and flexible. Mix it with JavaScript to create awesome stuff, with just a few lines of code. QML also allows you to easily create smooth and complex animations.

import QtQuick 2.0
import VPlayApps 2.0

App {
  Page {
    
    AppButton {
      anchors.centerIn: parent
      text: "Press Me"
      onClicked: {
        console.debug("Wow you pressed me!")
      }
    }
  }
}

Learn More

Monetize & Analyze

With V-Play, you can use many 3rd party services for ads, in-app purchases, analytics and more, with just a few lines of code. All of these plugins work cross-platform, which allows you to maintain a single code base.

Learn More

Native Sensors & More

V-Play Apps harness the power of Qt, the leading cross-platform development framework used by over 1,000,000 developers.

This gives your access to native device features like sensors, camera, file system as well as multimedia, networking, localization and much more.

Learn More

Responsive Design

V-Play has built in responsive design capabilities, you can target phones and tablets with the same source code.

Learn More

Highly Extensible

You have existing JavaScript, C++ or native code? You want to add any 3rd party SDK that we do not offer already?

No worries, you can add any JS, C++, Java or Objective-C code to your project.

Learn More

Component Showcase App shows the most important V-Play features and components

We got a demo app for you!

The V-Play SDK includes an open-source demo for this app type. You can use its source code and build your app in record time. After installing V-Play, you can simply open the .pro file with Qt Creator, the development environment used for V-Play.

<Path to V-PlaySDK>/Examples/V-Play/appdemos/showcase/Showcase.pro

Qt World Summit Conference App a full-featured conference management app made by V-Play

We got a demo app for you!

The V-Play SDK includes an open-source demo for this app type. You can use its source code and build your app in record time. After installing V-Play, you can simply open the .pro file with Qt Creator, the development environment used for V-Play.

<Path to V-PlaySDK>/Examples/V-Play/appdemos/qtws2016/QtWS2016.pro

Twitter App how to build layouts like in the official Twitter app for iOS and Android

We got a demo app for you!

The V-Play SDK includes an open-source demo for this app type. You can use its source code and build your app in record time. After installing V-Play, you can simply open the .pro file with Qt Creator, the development environment used for V-Play.

<Path to V-PlaySDK>/Examples/V-Play/appdemos/twitter/Twitter.pro

Maps App displays free bikes or boxes at bike stations for Vienna's bike sharing service Citybike Wien

We got a demo app for you!

The V-Play SDK includes an open-source demo for this app type. You can use its source code and build your app in record time. After installing V-Play, you can simply open the .pro file with Qt Creator, the development environment used for V-Play.

<Path to V-PlaySDK>/Examples/V-Play/appdemos/maps/Maps.pro

Messaging App demonstrates how to create the UI of a Facebook Messenger like app

We got a demo app for you!

The V-Play SDK includes an open-source demo for this app type. You can use its source code and build your app in record time. After installing V-Play, you can simply open the .pro file with Qt Creator, the development environment used for V-Play.

<Path to V-PlaySDK>/Examples/V-Play/appdemos/messaging/Messaging.pro

Weather App how to include powerful animations into your user interface

We got a demo app for you!

The V-Play SDK includes an open-source demo for this app type. You can use its source code and build your app in record time. After installing V-Play, you can simply open the .pro file with Qt Creator, the development environment used for V-Play.

<Path to V-PlaySDK>/Examples/V-Play/appdemos/waether/Weather.pro

Any other Idea? let us know how we can help you

You are looking for another app demo?

The V-Play SDK includes many open-source demos for different app types. You can use their source code and build your app in record time. After installing V-Play, you can simply open the .pro file with Qt Creator, the development environment used for V-Play..

If you do not find your app type in this list and wonder if V-Play is the right choice, just contact us, we are happy to help!

Contact Us