Guides & Tutorials API Reference Examples & Demos
Documentation

How to make a simple BalloonPop game with V-Play

Introduction

This game idea and its image and audio resources are from the excellent Corona tutorial on Ray Wenderlich's site http://www.raywenderlich.com/22064/how-to-make-a-simple-game-with-corona.

This tutorial assumes you already had a look at Getting Started with QML & Qt Creator and Getting Started with V-Play. They provide you with the very basics which may not be fully explained in this tutorial. Nevertheless, this tutorial aims at beginners, so let's give it a shot.

Resources

Images and sounds for this game are available for you to download right here: resources

New Project

Create a new Empty V-Play Project with the name BalloonPop. Now extract the resource files that you just downloaded into the qml directory of your project. Since you are already here, also create a new folder entities (which we will need later) inside the qml directory.

On to a little brainstorming. What major components do we need to create this game?

  • We already know, our root element is the GameWindow in the main.qml which will then hold our Scene for the game.
  • Obviously there will be balloons.
  • We want to keep the balloons inside the scene so we need walls to surround it.
  • A little heads-up display to give the player information.

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

 import VPlay 1.0
 import QtQuick 1.1

 GameWindow {

   Scene {}
 }

Game scene

Keep in mind that larger games will definitely have more than just the game scene (e.g. menu, settings,...). So it's common to define the different scenes in separate files, making the code more readable. Go ahead and add a BalloonScene.qml to your qml directory.

Now we will put the Scene element here, and our GameWindow will hold our custom scene.

BalloonScene.qml

 import VPlay 1.0
 import QtQuick 1.1

 Scene {

 }

main.qml

 import VPlay 1.0
 import QtQuick 1.1

 GameWindow {

   // our scene that contains the game
   BalloonScene {}
 }

Our main.qml is finished now since this is our only scene.

Let's take a look at our BalloonScene.qml. We add some basic features that we definitely need.

 //...
 Scene {
   // number of balloons currently on the scene
   property int balloons: 0
   // maximum number of balloons
   property int balloonsMax : 100
   // duration of the game
   property int time : 20
   // flag if game is running
   property bool gameRunning: false

   // position the scene on the top edge
   sceneAlignmentY: "top"

   // used to create balloons at runtime
   EntityManager {
     id: entityManager
     entityContainer: balloonScene
   }

   // make the balloons float up
   PhysicsWorld {gravity.y:1}

   // add a background image
   Image {source:"img/clouds.png"; anchors.fill:gameWindowAnchorItem}

   // the pop sound used by balloon entities
   Sound {source:"snd/balloonPop.wav"}

   // add background music
   BackgroundMusic {source:"snd/music.mp3"}
 }

Now we can set the maximum number of balloons that we want to have on the scene, as well as the time we give the player to pop them. Furthermore we added a counter balloons that will hold the current number of balloons alive and a flag to indicate if the game is running or not.

The EntityManager is used to create balloons at runtime. The PhysicsWorld with a very low gravity of 1 in y direction makes the balloons float up gently.

The background Image and BackgroundMusic improve the gaming experience, I have no doubt about that! Have a look at anchors.fill:gameWindowAnchorItem in the background Image. This is needed to avoid black borders on different mobile devices with different screen sizes. Also the sceneAlignmentY: "top" is there for a similar reason, we ensure that the ceiling of our scene is at the top of the screen.

The Sound object loads the pop sound for the balloons. This one will have to be available to the balloons themselves so we need to add a little fix here.

 //...
 Scene {
   id: balloonScene

   // provide the pop sound public for the balloons
   property alias popSound: popSound
   //...
   // the pop sound used by balloon entities
   Sound {id:popSound; source:"snd/balloonPop.wav"}
   //...
 }

Adding an id to the Scene gives us the possibility to interact with it from outside. The pop sound can now be accessed via balloonScene.popSound thanks to the property alias that points at our Sound object.

Run the project and enjoy the beautiful sky, prettified by the delightful music, isn't that already enough? Not for us!

We'll proceed with some walls, everyone likes walls.

Walls

Following the lessons we learned in Getting Started with V-Play, we will create a reusable wall entity. Add an entities folder as subfolder of qml if you haven't done that already. Now add a new file Wall.qml to the entities folder.

Insert the following code to Wall.qml

 import QtQuick 1.1
 import VPlay 1.0
 import Box2D 1.0 // for accessing the Body.Static type

 EntityBase {
   entityType: "wall"
   // default width and height
   width: 1
   height: 1

   // only collider since we want the wall to be invisible
   BoxCollider {
     anchors.fill: parent
     bodyType: Body.Static // the body shouldn't move
   }
 }

Not much new things here. We want the walls to be invisible, so we don't add anything like a Rectangle or similar. We just want them to block our balloons, so we add a BoxCollider that fills the whole wall. Also, the wall shouldn't be affected by gravity, that's why we tell it to be Body.Static.

Now add some walls to the BalloonScene.qml, just before the last closing }. Additionally we need to import our entities folder to be able to use our Wall.qml

 //...
 import "entities"

 Scene {
   //...
   // left wall
   Wall {height:parent.height+50; anchors.right:parent.left}
   // right wall
   Wall {height:parent.height+50; anchors.left:parent.right}
   // ceiling
   Wall {width:parent.width; anchors.bottom:parent.top}
 }

We want the walls to be outside of the scene, so we use the anchors. E.g. we anchor the furthest right side of the wall to the furthest left side of the scene, meaning the body of the wall is left outside of the screen. The reason I put +50 to the height, which causes the wall to go 50pixel below the scene, is that we don't lose any balloons to the sides while creating them. You will understand this security measure in a few moments.

Ok looks fine, not that there is anything new to see if we run the project, but anyway, time for balloons!

Balloons

Add a Balloon.qml file to the entities folder.

Insert the following code to Balloon.qml

 import QtQuick 1.1
 import VPlay 1.0

 EntityBase {
   entityType: "balloon"

   CircleCollider {
     radius: sprite.width/2
     anchors.centerIn: parent
     // restitution is bounciness, balloons are quite bouncy
     fixture.restitution: 0.5
   }

   MultiResolutionImage {
     id: sprite
     source: "../img/balloon.png"
     anchors.centerIn: parent
   }

   MultiTouchArea {
     anchors.fill: sprite
   }

   // gives the balloon a random position when created
   Component.onCompleted: {
     x = utils.generateRandomValueBetween(15,balloonScene.width-15)
     y = balloonScene.height
   }
 }

There are some more things to say about this piece of code. In our case, we want the size of the balloon to result from the size of the MultiResolutionImage we load, so it's super easy to replace that one. The MultiResolutionImage uses the correct image depending on the display. To learn more about multi-resolution support check out How to create mobile games for different screen sizes and resolutions.

A CircleCollider matches the shape of a balloon best and way better than a BoxCollider. Add some fixture.restitution to make the balloon bouncy.

The MultiTouchArea fills the whole balloon image and is used to make them pop when the balloon is touched.

And the last little bit of code, the Component.onCompleted section will define the position of the balloon when it is created. We want the balloons to spawn at the very bottom of your scene, and thanks to the id:balloonScene that we gave the Scene, we can get its width and height for this calculation.

At the end of BalloonScene.qml, just before the last closing }, we add this piece of code

   //...
   // create balloons with short intervals in between
   Timer {
     interval: 20 // milliseconds
     running: true // start running from the beginning, when the scene is loaded
     repeat: true
     onTriggered: {
       // after every 20ms we create a new balloon
       entityManager.createEntityFromUrl(Qt.resolvedUrl("entities/Balloon.qml"));
       balloons++
       // if the maximum number of balloons is reached, we stop the timer and therefore the balloon creation and start the game
       if(balloons===balloonsMax) {
         running = false
         gameRunning = true
       }
     }
   }
   //...

Finally we got some action in here. Watch the balloons getting a little crazy when you run the project. So crazy that we made the side walls a little longer than maybe needed, just in case...

What did we do? We used a Timer with an interval of 20ms. So every 20ms it will trigger. And on each of these triggers we tell our EntityManager to create a new entity from our Balloon.qml file, in other words a new balloon. If our maximum number of balloons is reached, we stop the timer and start the game.

You don't want to hurt no balloon, do you? Me neither, but the players may want to, so let's make them pop-able.

Popping the balloons

This is done by adding a simple piece of code to our Balloon.qml, more precisely the MultiTouchArea

   MultiTouchArea {
     anchors.fill: sprite
     onPressed: {
       // if you touch a balloon and the game is running, it will pop
       if(balloonScene.gameRunning) {
         balloonScene.balloons--
         balloonScene.popSound.play()
         removeEntity()
       }
     }
   }

We listen on the MultiTouchArea::onPressed signal of the MultiTouchArea. If the player touches the balloon we count down the current number of balloons, play the pop sound and remove the entity. Of course we only allow this to happen if the game is running.

Not too challenging when you got infinite time. We take care of that now.

Ending the game

Add this at the end of BalloonScene.qml, just before the last closing }

   // game timer, default interval is 1 second
   Timer {
     id: gameTimer
     running: gameRunning // time only counts down if game is running
     repeat: true
     onTriggered: {
       time--
       // if time is over, or each balloon is popped, we stop the game and give the player some feedback about his success
       if(time === 0 || balloons === 0) {
         gameRunning = false
       }
     }
   }

The timer is only running if the game is running, meaning it starts after all balloons are loaded and the game started. After each second (we don't necessarily need to define that interval because 1 second is the default interval) the timer triggers. On every trigger we decrease the remaining time. Then we check if the time is already over or the player managed to pop all balloons, in both cases we stop the game.

The only thing missing now is a little heads-up display.

HUD

We want to keep it as simple as possible, one row at the bottom will do it. We want to display the remaining time, and a little info field.

Add this to BalloonScene.qml, e.g. above our timers

   //...
   // HUD
   Row {
     anchors.bottom: parent.bottom
     z: 1 // make sure the HUD is always on top
     // info text area
     Text {id:infoText; width:200; height:40; text:"Loading balloons..."}
     // display remaining time
     Text {id:timeText; height:40; text:"Time: "+balloonScene.time}
   }
   //...

Now we got a Row with 2 Texts. The 2nd one displays the text "Time: " followed by the remaining time of the game.

The 1st one should give the player some information. We start with the text "Loading balloons...". After all balloons are loaded we want to give the player feedback that he can start popping them now.

Add this to your BalloonScene.qml, more precisely the first Timer, used to create the balloons

       //...
       // if the maximum number of balloons is reached, we stop the timer and therefore the balloon creation and start the game
       if(balloons===balloonsMax) {
         running = false
         gameRunning = true
         infoText.text = "Hurry!"
       }
       //...

This changes the text property of our infoText Item to "Hurry!", telling the player that the game is running.

The last thing we want to add are some motivating words to the player at the end of the game.

Add this to your BalloonScene.qml, more precisely the second Timer, used for the game time

       //...
       // if time is over, or each balloon is popped, we stop the game and give the player some feedback about his success
       if(time === 0 || balloons === 0) {
         gameRunning = false
         if(balloons === 0) infoText.text = "Perfect, take a cookie!"
         else if(balloons < balloonsMax/2) infoText.text = "Well, that was decent..."
         else infoText.text = "Not your day huh..."
       }
       //...

Depending on the number of balloons the player popped, he gets a feedback about his success as soon as the time ran out or he popped all balloons.

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

Visit V-Play Components 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.