59
Game Development with Qt3D Release 0.1 (default) Nokia, Qt Learning May 29, 2012

Game Development Qt 3 d

Embed Size (px)

DESCRIPTION

Game Development with Qt3D

Citation preview

Page 1: Game Development Qt 3 d

Game Development with Qt3DRelease 0.1 (default)

Nokia, Qt Learning

May 29, 2012

Page 2: Game Development Qt 3 d
Page 3: Game Development Qt 3 d

CONTENTS

1 About this Guide 31.1 Why should you read this guide? . . . . . . . . . . . . . . . . . . . . . . . . 31.2 Overview . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31.3 License . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4

2 When should you use Qt3D’s QML bindings? 5

3 Getting started 73.1 Overview of the “SpaceBurger” game . . . . . . . . . . . . . . . . . . . . . . 73.2 Qt3D the basics . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 83.3 A simple QtQuick3D application . . . . . . . . . . . . . . . . . . . . . . . . 93.4 Using a Camera . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11

4 Skybox 13

5 Player movement 155.1 Update-timer . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 155.2 Keyinput . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 155.3 Basic motion equations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 165.4 Transformations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18

6 Moving Targets 216.1 Onion rings . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 216.2 Collision-detection . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 236.3 Dynamic object creation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25

7 States 27

8 User interface 318.1 Head-up display . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 318.2 Game menu . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 32

9 Boss enemy 419.1 Camera movement . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 419.2 Movement . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 429.3 Weaponfire . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 44

i

Page 4: Game Development Qt 3 d

10 Shaders 4910.1 Bulletshader . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4910.2 Explosion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 52

11 Finalizing the game 55

ii

Page 5: Game Development Qt 3 d

Game Development with Qt3D, Release 0.1 (default)

Section author: bumberge

The Qt3D module is a set of APIs that use OpenGL and aim on making 3D developementeasier and more platform independent. It includes features like asset loading, shapes, texturemanagement and shaders.

Qt3D has both, a C++ and a QML API. The main aim of the C++ API is, to make 3D program-ming platform independent. You dont have to worry any more what 3D architecture is to beused. Whether it is destktop or embedded OpenGL and whether a fixed or dynamic pipeline isused. The QtQuick module for Qt3D further increase this abstraction level and makes it possi-ble to write complete 3D applications, while just using QtQuick. You can then very easily mix2D and 3D elements, for creating head up displays in your 3D application, or embedding 3Delements in your 2D QML user interface.

Qt3D was an addon project in Qt4 and finally became a part of Qt with Qt5.

The intention of this guide is to give a brief overview over the QtQuick elements in Qt3D andto show how to use them in a real application. We will also show how the 2D and 3D worldcan be combined and talk about basic lighting, material and texture topics. Requirements forunderstanding this guide are a solid understanding of QML and QtQuick. Also some basicknowledge of computer graphics and for the shadering chapter OpenGL and GLSL are a pre-requesit. Whereever possible, there will be references to other recources.

CONTENTS 1

Page 6: Game Development Qt 3 d

Game Development with Qt3D, Release 0.1 (default)

2 CONTENTS

Page 7: Game Development Qt 3 d

CHAPTER

ONE

ABOUT THIS GUIDE

1.1 Why should you read this guide?

This guide will walk you through the development of a Qt Quick application that uses the Qt3DAPI1. It provides an overview of som of the elements2 that can be used in the Qt3D QML APIand tell you how to use them.

The focus is explicitly on QML without C++ code, however for shader-programming, we willuse GLSL so a basic understanding of OpenGL3 and GLSL will be required as a prerequisitefor completing that chapter. If you do not have any experience in OpenGL you might want tolook into the http://ed.europe.nokia.com/qmlbook/OpenGL guide first.

Todo

Fix the link

Deployment of the game will no be covered in this particular guide. If you are interested inthis, you should read the Qt Quick desktop and mobile guides4.

After completion of this guide, you should have a good understanding of how the Qt3D QMLAPI works as well as how to develop basic desktop and mobile applications with this technol-ogy.

1.2 Overview

In the following chapters, a video game will be developed step by step.

1http://doc-snapshot.qt-project.org/5.0/qt3d-reference.html2http://doc-snapshot.qt-project.org/5.0/qt3d-qml3d.html3http://www.opengl.org4http://qt.nokia.com/learning/guides

3

Page 8: Game Development Qt 3 d

Game Development with Qt3D, Release 0.1 (default)

Firstly we will cover the basics of the Qt3D QML API, then learn how to load models andtextures. Afterwards we will cover dynamic model creation, Head up Displays, states, thegame menu and the usage of GLSL shaders.

1.3 License

Copyright (c) 2008-2012, Nokia Corporation and/or its subsidiary(-ies). All rights reserved.

This work, unless otherwise expressly stated, is licensed under a Creative CommonsAttribution-ShareAlike 2.5.

The full license document is available from http://creativecommons.org/licenses/by-sa/2.5/legalcode .

The Nokia, Qt and their respective logos are trademarks of Nokia Corporation in Finland and/orother countries worldwide. All other products named are trademarks of their respective owners.

4 Chapter 1. About this Guide

Page 9: Game Development Qt 3 d

CHAPTER

TWO

WHEN SHOULD YOU USE QT3D’SQML BINDINGS?

Qt3D’s QML API has its restrictions. You don’t have as much control over the elements andit is not as feature rich as the Qt3D’s C++ API, however for basic applications and games,Qt3D/Qml is the right choice because it can greatly shorten the development time. If youever come to a point at which the available features are no longer sufficient, you still havethe possibility of defining your own modules on the C++ side and exposing them to QML.Furthermore, the state concept in QML is a powerful tool for elegantly structuring your code.

5

Page 10: Game Development Qt 3 d

Game Development with Qt3D, Release 0.1 (default)

6 Chapter 2. When should you use Qt3D’s QML bindings?

Page 11: Game Development Qt 3 d

CHAPTER

THREE

GETTING STARTED

In this chapter, we aim to provide a brief overview of the game as well as an introductionto Qt3D. If you want a more detailed description please use the documentation of the Qt3D1

module.

This chapter includes the following steps:

3.1 Overview of the “SpaceBurger” game

“SpaceBurger” is a simple game consisting of several levels in which the player has to steer ahamburger through targets (onion rings). Every time the player flies through an onion ring, hegets score points. Small power ups appear and can be collected to help increase maneuverabilityand firepower. At the end of every level, there is a boss enemy the player has to fight. Once theboss has been defeated, the next level begins.

3.1.1 Controls

While having to hit several targets along the way, the burger is shown from behind. It will becontrolled using the A (left), S (down), D (right) and W (up) keys. In order to achieve realisticflight behavior, we will model it using basic movement equations.

The fight against the boss enemy at the end of every level is shown from the top view. Theburger can then only be moved left and right. Weapons can be fired using the space key.

3.1.2 Game menu

When starting the application, a game menu will be displayed from which a new game can bestarted and the highscore table can be shown.

1http://doc-snapshot.qt-project.org/5.0/qt3d-reference.html

7

Page 12: Game Development Qt 3 d

Game Development with Qt3D, Release 0.1 (default)

3.2 Qt3D the basics

Qt3D gives you some basic items that can be used in your Qt Quick application after importingthe Qt3D module into the QML document:

import Qt3D 1.0

Viewport

The Viewport2 element specifies a viewport for the whole scene. It is the root element and de-fines the camera and (scene-)lights as well as rendering parameters. It is usually the outermostelement in a 3D scene.

Camera

The Camera3 element is assigned to the camera property of the viewport. It defines a viewingposition and direction as well as the projection. Furthermore stereo projections are supported.

Item3D

The Item3D4 is used for creating visible 3D objects in a scene. It defines basic parameters andmethods for manipulating an object. To create a visible object, a mesh has to be specified forthe Item3D. Futhermore tree structures can be built out of Item3Ds which allows the creationof logical groups. Children of an Item3D are placed relatively to their parent object in the 3Dscene. I.e. if the parent object is moved or rotated, all the children will also be rotated.

Mesh

The Mesh5 is used to load geometry in such a way that it can be used in Qt3D. File loadingis done automatically after a filename is specified. The mesh element chooses the appropriatemodel loader from the file ending. The supported model formats are e.g. 3ds, dae, bez and obj.

Effect

An Effect6 defines a very basic and simple way on how an item is rendered on the screen. Withit simple lighting, material and texture effects can be achieved.

ShaderProgram

The ShaderProgram7 element is derived from Effect and gives the user the means for creatingcustom shader programs in GLSL. You can specify a fragment and a vertex shader. The textureproperty inherited from the Effect element will be maped to the qt_Texture0 in the shaderprogram code. The ShaderProgram automatically binds custom properties to your fragmentand vertex shaders if they exist under the same name. If you want e.g. to specify more then onetexture you may do so by adding a string property with the path to your texture.

Material2http://qt-project.org/doc/qt-5.0/qml-viewport.html3http://qt-project.org/doc/qt-5.0/qml-camera.html4http://qt-project.org/doc/qt-5.0/qml-item3d.html5http://qt-project.org/doc/qt-5.0/qml-mesh.html6http://qt-project.org/doc/qt-5.0/qml-effect.html7http://qt-project.org/doc/qt-5.0/qml-shaderprogram.html

8 Chapter 3. Getting started

Page 13: Game Development Qt 3 d

Game Development with Qt3D, Release 0.1 (default)

Materials8 provide some information for an effect, like lighting properties and colors.

Transformations

There are currently four transformations available in Qt3D: Rotation3D9, Translation3D10,Scale3D11 and LookAtTransform12. All of these can be applied to an Item3D and rotate, trans-late, scale or change the orientation of an item. The order in which the transformations arespecified are very important for the result. It makes a difference if an item is rotated first andthen translated or the other way round.

3.2.1 Building and running an example

Qt3D is shipped as a module with Qt5. There is a port to Qt4.8, however, that works fine. Wewill only focus on Qt5 in this guide, but the game should be easily adoptable to Qt4.8.

Because we are only working with elements already available in QML, there is no need to buildanything. We can simply execute the main QML file in a QDeclarativeView. For running thefinal program, we will use the program qmlscene from the qtbase/bin directory and specify thefilename (game.qml) as it’s first parameter. If Qt5 has been built without any problems andOpenGL support, running a Qt3D application should work fine.

What’s Next?

Next we will use the elements explained here in a very simple example.

3.3 A simple QtQuick3D application

Every scene is rendered into a Viewport element which can be used as any normal Qt Quickelement. It can be anchored, have a size and contain any other QML items:

// game.qmlimport QtQuick 2.0import Qt3D 1.0

// A Viewport element with defined geometriesViewport {

id: rootwidth: 300height: 200

}

When running this example, a black rectangle is displayed on the screen.

8http://qt-project.org/doc/qt-5.0/qml-material.html9http://qt-project.org/doc/qt-5.0/qml-rotation3d.html

10http://qt-project.org/doc/qt-5.0/qml-translation3d.html11http://qt-project.org/doc/qt-5.0/qml-scale3d.html12http://qt-project.org/doc/qt-5.0/qml-lookattransform.html

3.3. A simple QtQuick3D application 9

Page 14: Game Development Qt 3 d

Game Development with Qt3D, Release 0.1 (default)

3.3.1 Loading a model

In order to fill this empty scene with some exciting stuff, a model will be loaded and placed atthe origin (where the x,y and z coordinates are set to 0) of the scene.

Qt3D has support for some of the most common model formats. For displaying them in a scene,we simply create a mesh and apply it to an Item3D’s mesh property:

// game.qmlimport QtQuick 2.0import Qt3D 1.0

// A Viewport element with defined geometriesViewport {

id: rootwidth: 300height: 200

Item3D{id:hamburgerscale:0.1mesh: Mesh {

source: "hamburger/models/hamburger.dae"}

}}

In this example, we have loaded the geometry from hamburger.dae and scaled it to 10% of itsoriginal size. After we apply the source property, the model-loading process is started. As wecan see, the geometry that is loaded is not only restricted to vertices and indices, it can alsoinclude materials and textures.

The hamburger should now be displayed in front of the camera. By dragging and scrolling themouse, the model can be viewed from a different perspective:

10 Chapter 3. Getting started

Page 15: Game Development Qt 3 d

Game Development with Qt3D, Release 0.1 (default)

3.3.2 Where to get 3D Models?

There are plenty of sources on the Internet where one can get 3D models. One of the majorproblems however can be the license. Even if you find a free model on the Internet, you cannever be sure if the one who claims to be the author is the real author.

Here are some good sources where you might find free good-quality 3D models:

• http://archive3d.net/ : Lots of free and high-quality 3D models, but sometimes the modelshave too many polygons for a real time application.

• http://sketchup.google.com/3dwarehouse/ : The Google 3D warehouse is probably thebest source to get free 3D models on the Internet. Users can publish their self-mademodels that are usually made using Google Sketchup13. Most models are therefore of-fered in the Google Sketchup format (.skp). If you want to use them in your application,you first have to convert them to Collada (.dae). The best way to do this is by download-ing Google Sketchup and exporting the files from there. There is also an import pluginfor 3D Max 2010 that should work fine. The models in the 3D warehouse are of variablequality, but the license is free for commercial use.

• http://thefree3dmodels.com/ : Lots of free high-quality 3D models under the Attribution-Noncommercial-Share Alike 3.0 license.

What’s Next?

In the next chapter, we will see how to use a Camera element.

3.4 Using a Camera

In the previous chapter we, used a Viewport element to render a scene with a model placedat the origin. In order for OpenGL to be able to map the geometry of a scene onto the2D area on your computer monitor (called the viewport), a transformation matrix is created.The matrix consists of the camera position, the type of projection, the viewing vector and iscalled modelview-projection matrix. For further reading on this topic, please have a look at thehttp://ed.europe.nokia.com/qmlbook/OpenGL Tutorial.

Qt3D offers a very convenient way to deal with these projections, the Camera14 element. Ina Camera element e.g. the position, projection type, near and far plane are set. From theseparameters, a modelview-projection matrix is calculated and used for the projection.

3.4.1 Animating the Camera’s position

In the next example, we want to use the camera to rotate around our model, which has beenplaced at the origin.

13http://sketchup.google.com/14http://qt-project.org/doc/qt-5.0/qml-camera.html

3.4. Using a Camera 11

Page 16: Game Development Qt 3 d

Game Development with Qt3D, Release 0.1 (default)

The only thing we have to do for this is creating a new Camera element and assign it to thecamera property of the Viewport15. The eye property is the position of the camera and thecenter is basically the point in the 3D space at which the camera is looking. The default valueis the origin so the property would be dispensable in this example:

//The game cameracamera: Camera {

property real angle:0center: Qt.vector3d(0,0,0)eye: Qt.vector3d(20*Math.sin(angle), 10, 20*Math.cos(angle))NumberAnimation on angle{

to: 100duration: 1000000

}}

The rotation around the origin has been created in this example by using the sine and cosinetogether with a NumberAnimation on a custom property called angle. The angle is increasedfrom 0 to 100 in a big enough timespan.

What’s Next?

Next we will see how to use a Skybox in order to visualize stars in space.

15http://qt-project.org/doc/qt-5.0/qml-viewport.html

12 Chapter 3. Getting started

Page 17: Game Development Qt 3 d

CHAPTER

FOUR

SKYBOX

We currently have a hamburger and a camera which moves around it. To get the feeling ofspace, we should at least add some stars to our game.

There are several ways of accomplishing this. One way is to use particles, specifically one foreach star. We can use spheres for planets and suns. All these however, are very involved topicsthat can push us into the realm of topics beyond the scope of this guide. In order to model starsand suns, we will simply use a technique called skybox.

Skybox is a cube around the camera that creates the illusion of a distant 3D surrounding. Atexture is projected on every side of the cube. The skybox usually does not move with theviewer in the 3D scene so it creates the illusion of being very far away. It can be used to rendervery far away mountains, clouds or, in our case, stars when flying through space. Because askybox is a 6-sided cube, 6 textures are required - one for every side of the cube.

There are several sources of skybox textures on the internet. The problem is however, thata texture is projected onto a huge area on the screen and therefore has to be in a very highresolution (e.g. 1024x1024) to be of good quality. Because of this, we recommend usingapplications which are specialized for creating skybox textures:

• Terragen1: A terrain generator for photo realistic terrains. It is very easy to use and isavailable as a feature limited freeware application.

• Spacecape2: An open source project for creating space skyboxes containing several lay-ers of nebulas, suns and stars.

Qt3D has a built in element called Skybox that does exactly what we are looking for. It takes asource folder which should contain 6 textures. The textures can have a random name but mustcontain the orientation in the skybox (north, south, east, west, up and down). A texture couldhave the name space_west.png.

Skybox{//The folder containing the skybox texturessource: "space"

}

The following skybox was made using Spacescape:

1http://www.planetside.co.uk/2http://sourceforge.net/projects/spacescape/

13

Page 18: Game Development Qt 3 d

Game Development with Qt3D, Release 0.1 (default)

What’s Next?

Next we will see how to create the player object and move it in the 3D world.

14 Chapter 4. Skybox

Page 19: Game Development Qt 3 d

CHAPTER

FIVE

PLAYER MOVEMENT

In the final game, the hamburger is controlled by the player using the keyboard. It shouldbe possible to steer it from left to right and up and down. Furthermore, it should change itsorientation depending on the current acceleration.

The movement is controlled by some basic movement equations in order to achieve realisticflight behavior.

For the purpose of organizing our code well, we are creating a new file called Gamelogic.qml.

5.1 Update-timer

For our movement equations to work, we need an update-timer which, after a certain periodof time, updates the position and acceleration of the hamburger. Choosing an interval of 50msshould be sufficient for achieving fluent movement.

On every triggered signal, the movement equations will be processed and the position will beupdated.

5.2 Keyinput

Key inputs will be handled in Gamelogic.qml. We first have to set the focus of the elementto true in order to be certain that we will get the key events. We are then listening for theonPressed and onReleased events. This whole construct is necessary because we want to allowthe user to press and hold more then one key at a time. We also need four new variables (onefor each key), which are either set to true or false depending on the press state of the keys.Movement processing is then performed in the update-timer’s onTriggered signal:

//Gamelogic.qmlimport QtQuick 2.0

Item {property bool upPressed: falseproperty bool downPressed: falseproperty bool leftPressed: falseproperty bool rightPressed: false

15

Page 20: Game Development Qt 3 d

Game Development with Qt3D, Release 0.1 (default)

focus: true//Handling of basic key eventsKeys.onPressed: {

if(event.key == Qt.Key_A)leftPressed = true

if(event.key == Qt.Key_D)rightPressed = true

if(event.key == Qt.Key_W)upPressed = true

if(event.key == Qt.Key_S)downPressed = true

if(event.key == Qt.Key_Space)fireLaser();

}Keys.onReleased: {

if(event.key == Qt.Key_A)leftPressed = false

if(event.key == Qt.Key_D)rightPressed = false

if(event.key == Qt.Key_W)upPressed = false

if(event.key == Qt.Key_S)downPressed = false

}}

We also have to instantiate the Gamelogic component in game.qml:

//game.qml...Gamelogic {id: gameLogic}...

5.3 Basic motion equations

In the final game, the hamburger will be seen by the player from the back (if there is any for ahamburger) so we set the camera’s eye position to 0, 0,-30. The player can then move it on they and x axes. We just have to be sure that the hamburger does not leave the screen. Thereforewe are defining y and x bounds that will restrict the movement. Our x and y bounds could becalculated from the camera parameters, but a simple trial and error method is usually faster. Asvalues, we get 4.5 for the x-bound and 5 for the y-bound.

Note: The y and x bound parameters will change with the aspect ratio of the viewport you areusing and in general with the camera parameters!

We are using the two basic motion equations for constant acceleration1 to move the hamburgerobject. For that we are taking the acceleration the current speed and the position into account.

1http://en.wikipedia.org/wiki/Motion_equation#Constant_linear_acceleration

16 Chapter 5. Player movement

Page 21: Game Development Qt 3 d

Game Development with Qt3D, Release 0.1 (default)

//Velocity is acceleration multiplied with time plus the initial speedv = a*t + v0//Distance is velocity multiplied with time plus the initial distances = v*t + s0

Both speed and acceleration have to be distinctly computed for the y and x axes and are saved inthe vx, vy, ax and ay properties of the Hamburger element that we will source out to Player.qml:

//Player.qmlimport QtQuick 2.0import Qt3D 1.0

Item3D {property real vx: 0property real vy: 0

property real ax: 0property real ay: 0

mesh: Mesh { source: "hamburger/models/hamburger.dae" }

scale: 0.1}

Because we can build a tree structure with Item3Ds, we will define a root Item3D for the wholelevel which contains all the visible 3D items of the scene. The player object will then be a childof this element. Furthermore, we set the camera to a position behind the hamburger:

//game.qml...Item3D {

id: level

Player {id: player

}}

camera: Camera {eye: Qt.vector3d(0, 0,-30)

}...

We will also define a variable called maneuverability in order to have better control over theflight parameters. A good value for the maneuverability is 0.3:

//Gamelogic.qml....property real maneuverability: 0.3//The game timer is our event loop. It processes the key events and updates the position of the hamburgerTimer {

id: gameTimerrunning: trueinterval: 50repeat: trueonTriggered: {

//Velocity is updated

5.3. Basic motion equations 17

Page 22: Game Development Qt 3 d

Game Development with Qt3D, Release 0.1 (default)

player.vx+=player.ax*0.05player.vy+=player.ay*0.05//Acceleration is updatedplayer.ax=(player.ax+maneuverability*leftPressed - maneuverability*rightPressed)/1.1player.ay=(player.ay+maneuverability*downPressed - maneuverability*upPressed)/1.1//Position is updatedplayer.position.x += player.vx*0.05player.position.y += player.vy*0.05//If the player exceeds a boundary, the movement is stoppedif (player.position.x>x_bound) {

player.position.x = x_boundplayer.vx = 0;if (player.ax>0)

player.ax = 0}else if (player.position.x<-x_bound) {

player.position.x = -x_boundplayer.vx = 0if (player.ax<0)

player.ax = 0}else if (player.position.y<-y_bound) {

player.position.y = -y_boundplayer.vy = 0if (player.ay<0)

player.ay = 0}else if (player.position.y>y_bound) {

player.position.y = y_boundplayer.vy = 0if (player.ay>0)

player.ay = 0}

}}...

We should be able to move the hamburger smoothly over the screen now and the movementshould stop on the viewport boundaries. But one thing is still missing for a realistic flightbehavior and that is the hamburger turning into the flight direction.

5.4 Transformations

There are currently four transformation types available in the QML API of Qt3D: Rotation3D,Scale3D, Translation3D and LookAtTransform. The names should be fairly self-explanatory.

One or more transformations can be applied to an Item3D’s transform or pretransform prop-erties. The pretransform property however is intended to transform the model before all othertransformations because it may be in an unconventional scale, rotation or translation after load-ing.

As explained above, we want the hamburger to rotate in the flight direction, so we want toachieve three things. When moving along the:

18 Chapter 5. Player movement

Page 23: Game Development Qt 3 d

Game Development with Qt3D, Release 0.1 (default)

• x axis, the hamburger should roll a bit into flight direction. (the rotation axis is the z axis)

• x axis, it should move the nose in flight direction. (the rotation axis is the y axis)

• y axis, the hamburger should move its front up or down. (the rotation axis is the x axis)

Now we can add the different transformations to the transform property and specify their axis.We are connecting the angle of each rotation directly to the acceleration, which will have afairly good-looking result. The scalar factors have been obtained by trial and error:

transform: [Rotation3D {

angle: -10*ayaxis: "1, 0, 0"

},Rotation3D {

angle: 5*axaxis: "0, 1, 0"

},Rotation3D {

angle: -20*axaxis: "0, 0, 1"

}]

When moving the hamburger, you might notice that the rolling behavior is a bit strange. Thatis because the balance point of the object is not at the origin. We can however correct this veryeasily by applying a Translation3D to the pretransform property. In addition to this, the scalingwas moved into pretransform as well. Furthermore a rotation 45° on the y axis was added,which however has only aesthetic reasons.

pretransform: [Scale3D {

scale: 0.1},//Moving the objects origin into the balance pointTranslation3D {

translate: "0,-1,0"},Rotation3D {

angle: 45axis: "0, 1, 0"

}]

The hamburger object should now be completely movable:

5.4. Transformations 19

Page 24: Game Development Qt 3 d

Game Development with Qt3D, Release 0.1 (default)

What’s Next?

Next we want to implement the onion rings, which have to be hit by the player. For that we willlearn about dynamic object creation, collision detection and how to use textures and predefinedshapes.

20 Chapter 5. Player movement

Page 25: Game Development Qt 3 d

CHAPTER

SIX

MOVING TARGETS

The goal of the game is to fly into targets (onion rings) that move toward the player and whenhit raise the score of the player.

6.1 Onion rings

Every onion ring will be rendered using a quad that has a semi transparent texture on it. Tocreate the quad, we’ll be using the Qt3D Shapes module, which comes with some useful pre-defined shapes such as quads, cylinders, teapots etc.

import Qt3D.Shapes 1.0

We are moving the onion ring component into a new QML file called Target.qml to structureour code well. A quad is then created using the following:

//Target.qmlimport Qt3D.Shapes 1.0import Qt3D 1.0

Quad {id: root

}

Probably the first thing we notice here is that the quad is lying on the x,z plane. We want it,however, to face the camera so we are applying a pretransform:

//Target.qml...pretransform: [

Rotation3D { axis: "1,0,0"; angle: 90}]...

We also want to apply a semi transparent texture onto the quad where only the onion ring partof the texture is visible. That means, however, that we need an image format that supportstransparancy. PNG is probably the best choice here.

Furthermore, we want to have a bit transparency on the non transparent parts of the onion ring.That’s why we are adding a Material with a diffuseColor that has a alpha value of only 0.9 so

21

Page 26: Game Development Qt 3 d

Game Development with Qt3D, Release 0.1 (default)

the onion ring is slightly transparent. We also want to have the onion ring glowing a bit so weadd a red emittedLight:

//Target.qml...effect: Effect {

blending: truematerial: Material {

textureUrl: "onion.png"emittedLight: Qt.rgba(1,0.8,0.8,1)diffuseColor: Qt.rgba(1,1,1,0.9)

}}...

Because we are using blending for the transparent objects, we have to consider some things.First of all the blending property in the Effect has to be set. This will also override the viewportspecific setting for alpha blending. When using blending, items have to be painted from backto front. This means that items which are farther away from the viewer have to be painted first,which requires sorting of the items. Fortunately, Qt3D does this for us automatically if we setthe sortChildren property to BackToFront in the parent Item3D element.

//game.qml...Item3D {

sortChildren: Item3D.BackToFrontid: level

...

Warning: BackToFront sorting only works for one hierarchy level. This means only directchildren of an Item3D are sorted and not the children’s children.

When the component is created, it should immediately start moving toward the player. We canachieve this by a simple NumberAnimation on the z property of Item3D.

//Target.qml...NumberAnimation on z{

running: trueduration: durationTfrom: 200to: -30

}...

You can test the code by manually adding a Target component to the level.

22 Chapter 6. Moving Targets

Page 27: Game Development Qt 3 d

Game Development with Qt3D, Release 0.1 (default)

6.2 Collision-detection

Collision-detection is not yet supported by Qt3D. It is unfortunately not even possible to get abounding box of an Item3D, but that still does not mean that we can not build a simple collisiondetection on our own.

We will just use the advantage that the collision test is only performed between two objects (i.e.a collision only occurs between the onion ring and the hamburger and between the weapon fireand the enemy or player).

Because we will also be using collision detection for other items than for onion rings, we createBasicGameItem, which implements the collision detection. This component will be used as aparent item for all the components that will implement collision detection.

The detection will work as follows:

• A target is specified for which the collision test is performed

• The target element has to define a radius property that specifies the size of the object

• The BasicGameItem also defines a radius that specifies the size of the item

• Every time the positionChanged signal is emitted, a test for collision takes place

• If a collision is detected, a collisionDetected signal is emitted and BasicGameItem isdestroyed afterwards.

//BasicGameItem.qmlimport QtQuick 2.0import Qt3D 1.0

Item3D {id: gameItem

6.2. Collision-detection 23

Page 28: Game Development Qt 3 d

Game Development with Qt3D, Release 0.1 (default)

signal collisionDetected(variant object)

property variant collisionTarget: 0

property real radius: 0.5

//Test for a collision between the item and the targetfunction testCollision(){

if (Math.pow(x-collisionTarget.x,2)+Math.pow(y-collisionTarget.y,2)+Math.pow(z-collisionTarget.z,2)< Math.pow(radius+collisionTarget.radius,2)) {

return true;}return false;

}

onPositionChanged: {if (collisionTarget!=0) {

if (testCollision()) {collisionDetected(gameItem)gamenItem.destroy()

}}

}}

The Target.qml file looks like this:

BasicGameItem {id: rootQuad {

pretransform: [Rotation3D { axis: "1,0,0"; angle: 90}

]

effect: Effect {blending: truematerial: Material {

textureUrl: "onion.png"emittedLight: Qt.rgba(1,0.8,0.8,1)diffuseColor: Qt.rgba(1,1,1,0.9)

}}

}NumberAnimation on z{

running: trueduration: 10000from: 200to: -30onRunningChanged: {

if (running == false)root.destroy()

}}

}

Be careful to use the NumberAnimation on the BasicGameItem and not on the Quad. Otherwisethe detection will not work.

24 Chapter 6. Moving Targets

Page 29: Game Development Qt 3 d

Game Development with Qt3D, Release 0.1 (default)

The collision target of our Target component will be the Player object so we also have to definethe radius property for the player.

//Player.qml...property real radius: 1.5...

6.3 Dynamic object creation

We will create the onion ring targets dynamically triggered by a timer in Gamelogic.qml withan interval of 4 seconds.

This means a new onion ring will be created in the distance every 4 seconds and fly towards theplayer.

When creating a new QML object, a component has to first be loaded. Afterwards, an instanceof the component can be created using the createObject method. Because we want to reuse thecomponent several times, we will load it when starting the application in GameLogig.qml.

Note: If the component is loaded over the network, we first need to wait for the component tobe ready before calling createObject

We first define some properties in game.qml:

//game.qml...property int score: 0property int targetCount: 0...

Then we implement the target timer.

//GameLogic.qml...property variant targetComponent: Qt.createComponent("Target.qml");....//Timer creates targets in a certain intervalTimer {

id: targetTimerinterval: 4000repeat: truerunning: trueonTriggered: {

targetCount++var object = targetComponent.createObject(level, {"position.x": (Math.random()-0.5) *8,

"position.y": (Math.random()-0.5) *6,"scale": 3-0.2*targetCount, "collisionTarget": player})

object.collisionDetected.connect(targetCollision)}

}

6.3. Dynamic object creation 25

Page 30: Game Development Qt 3 d

Game Development with Qt3D, Release 0.1 (default)

After the object is created, we connect the collisionDetected signal to a function called target-Collision where the Score property that is defined in the game.qml is incremented by one.

function targetCollision(sender) {score++

}

What’s Next?

Next we will see how to use States in order to handle our game flow.

26 Chapter 6. Moving Targets

Page 31: Game Development Qt 3 d

CHAPTER

SEVEN

STATES

We have already implemented some parts of the game. Now it is time to think about howwe want to control the game. With Qt Quick it is very convenient to make use of the stateconcept. This means, states have to be defined for every major event, that requires e.g. cameraadjustment, or in general the change of important parameters.

Just to summarize the game flow once more: after starting the application, a game menu isshown. The player has the option to view the highscore table or start the game. When startingthe game the camera has to be moved to the back of the hamburger and the keyboard controlswill be enabled. Onion rings start to fly toward the player and the player has to try to hit them.After a certain number of onion rings, the boss enemy appears. The final fight will take placefrom the birds eye view so the camera has to be moved first. After the boss or player have beendestroyed, a dialog appears where the player can enter his name for the highscore table.

This makes following states:

• Menu is the initial state where only the 3 buttons of the menu are shown

• Highscore is an extention to the Menu state where the highscore table is also shown

• Enter Highscore is a state in which a textfield for entering the name for the highscoretable is shown. The whole game scene will be frozen

• The Game state moves the camera behind the hamburger and starts the game. The key-board controls are enabled and the game timer starts running

• Boss Rotation rotates the camera around the boss enemy and finally to a position abovethe scenery

• The Boss Fight state adjusts the x-bound because the camera has moved to a differentpoint and the fight against the boss enemy starts

//game.qml...state: "Menu"

states:[State{

name: "Menu"PropertyChanges {target: player; ax: 0; ay: 0; vx: 0; vy:0; position: Qt.vector3d(0, 0, 0); hitpoints: 2; energy:2000; restoreEntryValues: false}PropertyChanges {target: root; score: 0; targetCount:0; restoreEntryValues: false}PropertyChanges {target: cam; center: Qt.vector3d(0, 0, 0) }

27

Page 32: Game Development Qt 3 d

Game Development with Qt3D, Release 0.1 (default)

},State{

name: "Highscore"extend: "Menu"

},State{

name: "EnterHighscore"},State{

name: "Game"PropertyChanges {target: player; position: Qt.vector3d(0, 0, 0) }

},State{

name: "BossFight"PropertyChanges {target: player; ay: 0; vy:0; position: Qt.vector3d(0, 0, 0); restoreEntryValues: false}

},State{

name: "BossRotation"PropertyChanges {target: player; position: Qt.vector3d(0, 0, 0) }

}]...

Note: The implementation of the game menu and the highscore dialog will be done in a laterchapter

Also the target and game timer should not be started until either Game or BossFight are reached:

//Gamelogic.qml...id: targetTimerrunning: root.state=="Game"...id: gameTimer;running: root.state=="Game"||root.state=="BossFight"...

According to our state concept, we also have to adjust our controls. The player will not be ableto move on the y axis during the fight against the boss anemy and shooting lasers obviouslymust only be possible when there is a target to shoot.

//Gamelogic.qml...if(event.key == Qt.Key_W && root.state=="Game")

upPressed = trueif(event.key == Qt.Key_S && root.state=="Game")

downPressed = trueif(event.key == Qt.Key_Space && root.state=="BossFight")

fireLaser();...

28 Chapter 7. States

Page 33: Game Development Qt 3 d

Game Development with Qt3D, Release 0.1 (default)

What’s Next?

Next we will build a user interface in Qt Quick for our game.

29

Page 34: Game Development Qt 3 d

Game Development with Qt3D, Release 0.1 (default)

30 Chapter 7. States

Page 35: Game Development Qt 3 d

CHAPTER

EIGHT

USER INTERFACE

Qt3D together with Qt Quick makes it very easy to mix 2D and 3D elements, which makes theuser interface design very straight forward.

8.1 Head-up display

A Head-up display (HUD) usually shows information to the player about the current gamestate and the player’s condition. There are actually three things we want to display: the currentenergy for the laser, the hitpoints of the player and the score.

First we define the properties for the player:

//Player.qml...property int hitpointsproperty real maxHitPoints: 10

property int energyproperty int maxEnergy: 500...

Then two energy bars are painted on the screen. A red one in the center showing the players hitpoints and a blue one showing the current energy that is left for the laser. The current score isdisplayed in the upper left corner:

//Hud.qmlimport QtQuick 2.0

Item {anchors.fill: parentid: hudText {

anchors.left: parent.leftanchors.top: parent.topanchors.margins: 10text: "Score: " + score;style: Text.Raisedfont.pixelSize: 20color: "green"

}

31

Page 36: Game Development Qt 3 d

Game Development with Qt3D, Release 0.1 (default)

Rectangle {anchors.top: parent.topanchors.topMargin: 20anchors.horizontalCenter: parent.horizontalCenterwidth: parent.width/2height: 15color: "transparent"border.color: "red"Rectangle{

anchors.left: parent.leftanchors.top: parent.topanchors.bottom: parent.bottomwidth: parent.width*player.hitpoints/player.maxHitPoints;color: "red"

}}

Rectangle {anchors.right: parent.rightanchors.rightMargin: 20anchors.verticalCenter: parent.verticalCenterheight: parent.height/3width: 10color: "transparent"border.color: "blue"Rectangle{

anchors.right: parent.rightanchors.left: parent.leftanchors.bottom: parent.bottomheight: parent.height*player.energy/player.maxEnergy;color: "blue"

}}

}

//main.qml...//Head up displayHud {id: hud}...

8.2 Game menu

When the game is started, a menu should first be displayed. This menu consists of a buttongroup containing a “start”, “highscore” and “exit” buttons. When the menu is being shown,the hamburger will be displayed while rotating in the background. By clicking on the “start”button, the game begins and the camera is moved behind the hamburger. When clicking on the“highscore” button, a new rectangle will appear that displays the highscores in a ListView. Thefunction of the exit button should be obvious.

32 Chapter 8. User interface

Page 37: Game Development Qt 3 d

Game Development with Qt3D, Release 0.1 (default)

Before we start defining the menu, we first have to define the two camera movements that aremissing. One is the rotation of the hamburger while the game menu is being shown and theother one moves the camera behind the hamburger after starting the game:

//game.qml...//The game cameracamera: Camera {

property real angle:0;id: cam1eye: Qt.vector3d(20*Math.sin(angle), 10, 20*Math.cos(angle))NumberAnimation on angle{

id: hamburgerRotationto: 100running: falseduration: 1000000;

}PropertyAnimation on eye {

id: moveBehindHamburgerto: Qt.vector3d(0, 0,-30)duration: 2000running: false

}}...

Now we define our own button component in Button.qml:

//Button.qmlimport QtQuick 2.0

//Creates a simple button that has an attribute buttonText

8.2. Game menu 33

Page 38: Game Development Qt 3 d

Game Development with Qt3D, Release 0.1 (default)

Rectangle {id:rootIwidth: 200;height: 50;signal buttonClicked();property variant buttonText;radius: 5border.color: "black"border.width: 2color: "darkblue"opacity: 1MouseArea {

hoverEnabled: true;anchors.fill: parent;onClicked: buttonClicked();onEntered: border.color="white"onExited: border.color="black"

}Text {

anchors.centerIn: parent;text: buttonText;color: "white"

}}

We then assemble three buttons to a button group in Menu.qml. When pressing a button, theappropriate state will be set in the root element (the viewport):

//Menu.qmlimport QtQuick 2.0

Item {visible: falseanchors.fill: parent//The button groupColumn {

id: buttonGroupanchors.verticalCenter: parent.verticalCenter;anchors.left: parent.left;anchors.leftMargin: 20spacing: 10

Button {buttonText: "Start game"onButtonClicked: root.state="Game"

}Button {

buttonText: "Highscore"onButtonClicked: root.state="Highscore"

}Button {

buttonText: "Exit"onButtonClicked: Qt.quit()

}}

}

34 Chapter 8. User interface

Page 39: Game Development Qt 3 d

Game Development with Qt3D, Release 0.1 (default)

The highscore will be saved in a SQLite database. The code for SQL integration is taken fromthe Qt Quick Desktop Guide1 and will not be discussed in detail here.

We create a new JavaScript library for that purpose. It is very important for this library to bestateless, which means that every time this library is included into a QML file, no new instanceswill be created. After that, we can access the same SQL connection from every file:

// gameDb.js

//making the nodeDB.js a stateless library.pragma library

.import QtQuick.LocalStorage 2.0 as Sql

// declaring a global variable for storing the database instancevar _db

//Opens the database connectionfunction openDB() {

print("noteDB.createDB()")_db = Sql.openDatabaseSync("SpaceburgerDB","1.0","The Spaceburger Database",1000000);createHighscoreTable();

}

//Creates the highscore tablefunction createHighscoreTable() {

print("gameDB.createTable()")_db.transaction( function(tx) {

tx.executeSql("CREATE TABLE IF NOT EXISTS highscore (score INTEGER, name TEXT)");});

}

//Reads the first 10 elements of the highscoretable and returns them as an arrayfunction readHighscore() {

print("noteDB.readHighscore()")var highscoreItems = {}_db.readTransaction( function(tx) {

var rs = tx.executeSql("SELECT name, score FROM highscore ORDER BY score DESC LIMIT 0,10");var itemfor (var i=0; i< rs.rows.length; i++) {

item = rs.rows.item(i)highscoreItems[i] = item;

}});

return highscoreItems;}

//Saves an element into the highscore tablefunction saveHighscore(score, name) {

print("gameDB.saveHighscore()")_db.transaction( function(tx){

tx.executeSql("INSERT INTO highscore (score, name) VALUES(?,?)",[score, name]);});

}

1http://qt.nokia.com/learning/guides

8.2. Game menu 35

Page 40: Game Development Qt 3 d

Game Development with Qt3D, Release 0.1 (default)

Next we have to create the highscore table in the menu:

//Menu.qmlItem {

...ListModel {

id: highscoreModel;}Rectangle {

visible: root.state=="Highscore"anchors.left: buttonGroup.rightanchors.right: parent.rightanchors.bottom: parent.bottomanchors.top: parent.topanchors.margins: 50radius: 5border.color: "black"border.width: 2color: "darkblue"opacity: 0.7Text {

id: titleanchors.top: parent.topanchors.horizontalCenter: parent.horizontalCenteranchors.topMargin: 20text: "Highscore"font.bold: truefont.pointSize: 15color: "white"

}//The highscore tableListView {id: highscore

anchors.top: title.bottomanchors.topMargin: 50anchors.verticalCenter: parent.verticalCenterwidth: parent.width-70height: parent.height-title.height-50model: highscoreModel;delegate: Item {

anchors.left: parent.left; anchors.right: parent.right; anchors.margins: 40height: 30Text{anchors.left: parent.left; text: name; font.bold: true; font.pointSize: 20; color: "white"}Text{anchors.right: parent.right; text: score; font.bold: true; font.pointSize: 20; color: "white"}

}}

}}

As you might have noticed, we have created an empty ListModel and use it in the ListView.Next we are going to populate this model with the data we get out of the SQL table through thereadHighscore() function. The first thing to do is to import the library:

//Menu.qmlimport "gameDB.js" as GameDB

Now we can read the data of the highscore table. Because we always want our highscore to

36 Chapter 8. User interface

Page 41: Game Development Qt 3 d

Game Development with Qt3D, Release 0.1 (default)

be up to date, we will have to refresh the information every time the highscore is shown to theuser. We will therefore put the code for reading the SQL table into the onVisibleChanged signalof the highscore item. From there, we call the GameDB’s readHighscore() function and parseit into the ListModel we have already defined:

//Menu.qml...

onVisibleChanged: {if (visible == true) {

var highscoreTable=GameDB.readHighscore();highscoreModel.clear();for (var i in highscoreTable) {

print(highscoreTable[i])highscoreModel.append(highscoreTable[i]);

}}

}

Entering of a new highscore into the SQL table will be possible after the game has finished. Adialog is displayed that asks the player to enter his name. The name and the score will then besaved:

//HighscoreDialog.qmlimport QtQuick 2.0import "gameDB.js" as GameDB

Rectangle{anchors.verticalCenter: root.verticalCenteranchors.horizontalCenter: root.horizontalCenterheight:170width:270radius: 5border.color: "black"border.width: 2color: "darkblue"opacity: 0.7visible: falseText{

id: titleanchors.horizontalCenter: parent.horizontalCenteranchors.top: parent.topanchors.topMargin: 15text: "Enter your name:"font.pointSize: 17color: "white"

}Rectangle{

id: inputanchors.horizontalCenter: parent.horizontalCenteranchors.top: title.bottomanchors.topMargin: 15height: 40width: 200radius: 2color: "lightgray"clip: trueTextInput{

8.2. Game menu 37

Page 42: Game Development Qt 3 d

Game Development with Qt3D, Release 0.1 (default)

id: inputFieldanchors.fill: parentcolor: "black"text: "Name..."font.pointSize: 17

}}Button {

anchors.bottom: parent.bottom;anchors.bottomMargin: 15anchors.right: parent.rightanchors.rightMargin: 15buttonText: "OK"onButtonClicked: {

GameDB.saveHighscore(score, inputField.text)root.state="Menu"

}}

}

//main.qml...HighscoreDialog {id: highscoreDialog}...

We now can update our states:

states:[State{

name: "Menu"PropertyChanges {target: player; ax: 0; ay: 0; vx: 0; vy:0; position: Qt.vector3d(0, 0, 0); hitpoints: 2; energy:2000; restoreEntryValues: false}PropertyChanges {target: root; score: 0; targetCount:0; restoreEntryValues: false}PropertyChanges {target: cam; center: Qt.vector3d(0, 0, 0) }PropertyChanges {target: gamemenu; visible: true;}PropertyChanges {target: hamburgerRotation; running: true;}PropertyChanges {target: hud; visible: false;}

},State{

name: "Highscore"extend: "Menu"

},State{

name: "EnterHighscore"PropertyChanges {target: hud; visible: true;}PropertyChanges {target: highscoreDialog; visible: true;}

},State{

name: "Game"PropertyChanges {target: moveBehindHamburger; running: true;}PropertyChanges {target: hud; visible: true;}

},State{

name: "BossFight"PropertyChanges {target: hud; visible: true;}PropertyChanges {target: player; ay: 0; vy:0; position: Qt.vector3d(0, 0, 0); restoreEntryValues: false}

},State{

38 Chapter 8. User interface

Page 43: Game Development Qt 3 d

Game Development with Qt3D, Release 0.1 (default)

name: "BossRotation"}

]

What’s Next?

Next we will create the boss enemy which appears at the end of the level.

8.2. Game menu 39

Page 44: Game Development Qt 3 d

Game Development with Qt3D, Release 0.1 (default)

40 Chapter 8. User interface

Page 45: Game Development Qt 3 d

CHAPTER

NINE

BOSS ENEMY

The boss enemy appears at the end of a level after a certain number of targets has been passed.

Other than in the first part of the level, the player observes the fight not from the back of thehamburger, but from the top. That also means the hamburger (and the enemy) can only bemoved on the x-axis. At the beginning of the fight, we have to set the y value to 0, which wealready did when we defined the states.

9.1 Camera movement

The camera will start moving when the enemy has arrived at its final position in front of theplayer. To accomplish this, a SequentialAnimation is started which moves the camera’s centerto the enemy’s position. After that, the camera pans around the enemy and at the end moves theeye to the top of the scene and adjusts the camera’s center to the middle of the fighting scene.

After the animation is finished, the start of the fight is triggered by setting a new state for theroot item:

//game.qml...SequentialAnimation {

id: rotateAroundBossrunning: falsePropertyAnimation{

target: cam1properties: "center"to: enemy.positionduration: 400

}PropertyAnimation{

target: cam1properties: "eye"duration: 2000to: Qt.vector3d(30,5,50);

}PropertyAnimation{

target: cam1properties: "eye"duration: 2000to: Qt.vector3d(-30,5,50);

41

Page 46: Game Development Qt 3 d

Game Development with Qt3D, Release 0.1 (default)

}PropertyAnimation{

target: cam1properties: "eye"duration: 1000to: Qt.vector3d(0,5,0);

}ParallelAnimation {

PropertyAnimation{target: cam1properties: "eye"duration: 2000to: Qt.vector3d(0, 140, -1);

}PropertyAnimation{

target: cam1properties: "center"running: falseduration: 1000;to: Qt.vector3d(0,0,20);

}}onRunningChanged: {

if (running==false) {root.state="BossFight"

}}

}...

We can also add the animation to the states:

//game.qml...State{

name: "BossRotation"PropertyChanges {target: rotateAroundBoss; running: true }

}...

9.2 Movement

For the boss enemy, we will create Enemy.qml.

It uses the Fruits.3ds model, which has to first be pretransformed in order for it to fit into ourscene:

//Enemy.qmlimport QtQuick 2.0import Qt3D 1.0

//Creates an enemyItem3D {

id: enemy

42 Chapter 9. Boss enemy

Page 47: Game Development Qt 3 d

Game Development with Qt3D, Release 0.1 (default)

//Size of the object for the collision detectionproperty real radius: 1.5

mesh: Mesh { source: "Fruits/Fruits.3ds"; options: "ForceSmooth";}

pretransform : [Rotation3D {

angle: -180axis: Qt.vector3d(0, 1, 0)

},Scale3D {

scale: 0.01}

]}

The enemy will be created after a certain amount of targets has passed so we have to extend thetargetTimer code and add the boss enemy component:

//Gamelogic.qml...property variant bossEnemyComponent: Qt.createComponent("Enemy.qml")...onTriggered: {

var component;//After a certain amount of targets were created the boss enemy appearsif (targetCount>10) {

targetTimer.stop()enemy = bossEnemyComponent.createObject(level)enemy.playerHit.connect(playerHit)

}//Targets are constantly created and fly towards the playerelse {

targetCount++var object = targetComponent.createObject(level, {"position.x": (Math.random()-0.5) * 8,

"position.y": (Math.random()-0.5) * 6,"scale": 3-0.2*targetCount, "collisionTarget": player})

object.collisionDetected.connect(targetCollision)}

}...

Furthermore, we are adding a property called enemy to main.qml to be able to easily access theobject:

//game.qml...

property variant enemy...

When the enemy is created, it will approach the player, stop at a distance of 40, and afterwards,set a new state for the root element which will trigger the camera movement.

//Enemy.qml...//Animation which moves the the enemy towards the playerNumberAnimation on z{

9.2. Movement 43

Page 48: Game Development Qt 3 d

Game Development with Qt3D, Release 0.1 (default)

running: trueduration: 10000from: 200to: 40onRunningChanged: { if (running == false) root.state="BossRotation" }

}...

The enemy will simply move from left to right and fire in constant intervals. Both the Sequen-tialAnimation and the Timer will only run if the root item is in the BossFight state.

//Enemy.qml...//The enemy movementSequentialAnimation {

id: bossMovementrunning: root.state=="BossFight"loops: Animation.InfinitePropertyAnimation{

target: enemyproperties: "x"duration: 5000to: -16easing.type: Easing.InOutSine

}PropertyAnimation{

target: enemyproperties: "x"duration: 5000easing.type: Easing.InOutSineto: 16

}}

Timer {id: shootTimerinterval: 1000repeat: truerunning: root.state=="BossFight"onTriggered: {

shootLaser()}

}...

Because we use a SequentialAnimation here, more complex movements could be implemented(for example the enemy flying in circles or at altering speeds).

9.3 Weaponfire

We use a very popular technique called Billboarding for bullets that are fired from and at theenemy. It adjusts an item’s orientation so that it always faces the camera. Billboarding is veryoften used for particle effects, (distant) vegetation or just to cut down polygons on far away 3D

44 Chapter 9. Boss enemy

Page 49: Game Development Qt 3 d

Game Development with Qt3D, Release 0.1 (default)

Objects. Usually a billboard consists of a rectangle that is always facing the camera, but anyarbitrary 3D Object could be used for that.

With BillboardItem3D, which inherits Item3D, Qt3D offers a very convenient and fast wayof using this rendering technique. It automatically rotates the item so that it faces the view-ing plane, but not the camera, which gives more or less the same result in most cases, but issignificantly faster:

BillboardItem3D {//defines the shadereffect, that should be used for the itemeffect: lasereffectscale: 0.2mesh: Mesh { source: "quad.obj" }

}

Todo

Probably use the lookattransform here because billboarding is not working properly

For now, we will just create a simple Effect for each bullet, i.e. a semitransparent texture ismapped on top of the quad.

Effect {id: lasereffectblending: truematerial: Material {

textureUrl: "bullet.png"}

}

We will reuse the collision detection, which we already built in the previous section, for thebullets. The difference between the bullet and the onion rings is that a bullet has a direction anda velocity that can both depend on the entity that shoots the bullet or power ups that the playerhas collected. We therefore have to implement a new animation that handles the movement ofthe bullet. Again, it is very important to only animate the position of the BasicGameItem andnot the BillboardItem3D. Otherwise collision detection will not work.

//Bullet.qmlimport QtQuick 2.0import Qt3D 1.0

//This item represents a laser particleBasicGameItem{

id: bulletproperty variant dir: Qt.vector3d(0,0,1)property real speed: 100;BillboardItem3D {

//defines the shadereffect, that should be used for the itemeffect: lasereffectscale: 0.2mesh: Mesh { source: "quad.obj" }Effect {

id: lasereffectblending: truematerial: Material {

9.3. Weaponfire 45

Page 50: Game Development Qt 3 d

Game Development with Qt3D, Release 0.1 (default)

textureUrl: "laser2.png"}

}}//The movement of the bulletPropertyAnimation on position {

to: Qt.vector3d(x+speed*dir.x, y+speed*dir.y, z+speed*dir.z);duration: 10000onRunningChanged: {

//When the bulletanimation is finished and no target has been hitif (running==false) {

bullet.destroy();}

}}

}

We have now got bullets with a working collision detection that move in a direction that canbe specified. The feature that is still missing is the firing mechanism for the bullets. On onehand, it has to be possible for the player to shoot a bullet when pressing the space key and,on the other hand, the enemy has to shoot back somehow. Furthermore, the player and theenemy need a property which holds the hitpoints that are left and a function connected to thecollisionDetected signal of the bullets so that the hitpoints can be subtracted.

We first implement the latter for the enemy:

//Enemy.qmlproperty int hitpoints: 10property real maxHitPoints: 10....function hit() {

hitpoints--if (hitPoints <= 0) {

explode();}

}

function explode () {enemy.destroy()root.state="EnterHighscore"

}....

It is nearly the same for the player except that we do not delete the player after it explodes:

//Player.qmlproperty int hitpoints: 10....function hit() {

hitpoints--if (hitpoints <= 0) {

explode();}

}

function explode () {root.state="EnterHighscore"

46 Chapter 9. Boss enemy

Page 51: Game Development Qt 3 d

Game Development with Qt3D, Release 0.1 (default)

}....

The firing mechanism for the enemy is very simple. We just create a new Bullet object with theplayer as the target. Then we connect the collisionDetected signal to the player’s hit function:

//Enemy.qml...//Shoots a bulletfunction shootLaser() {

var component = Qt.createComponent("Bullet.qml")var object = component.createObject(level, {"position": enemy.position, "radius": 0.2,

"dir": Qt.vector3d(0,0,-1), "collisionTarget": player});object.collisionDetected.connect(player.hit)object.collisionDetected.connect(object.destroy)

}...

We implement the firing of the player’s bullet in Gamelogic.qml, where the fireLaser functionis executed after the space key has been pressed. Every time a bullet is fired, a certain amountof energy is subtracted from the player, which we refill in the gameTimer:

//Gamelogic.qml...id: gameTimeronTriggered: {

if(energy<maxenergy)energy++;

...function fireLaser() {

if (player.energy>=40) {print(player.y)player.energy -=40var component = Qt.createComponent("Bullet.qml")var laserObject = component.createObject(level, {"position": player.position,"collisionTarget": enemy})laserObject.collisionDetected.connect(enemy.hit)

}}...

The fight against the enemy should work fine now. One thing you have probably noticed isthat the area in which the hamburger can be moved is fairly small. This is because of the newperspective. That is why we have to expand the x_bound value during the fight against the bossenemy.

//game.qml...property real x_bound: state == "BossFight" ? 16: 4.5;...

9.3. Weaponfire 47

Page 52: Game Development Qt 3 d

Game Development with Qt3D, Release 0.1 (default)

What’s Next?

Next we will talk about shaders and see how to create particle effects with it.

48 Chapter 9. Boss enemy

Page 53: Game Development Qt 3 d

CHAPTER

TEN

SHADERS

In the previous chapters, we learned that we can define Effects with a texture and differentmaterial properties for our geometry in QML/3d. For most areas of applications, we do notneed more than that, but if we want to achieve a custom effect, this technique has its limits. Itis, however, possible in Qt Quick 2.0 and QML/3d to define custom effects as shader programsthat allow you to extend the functionality of the built in effects.

The basics of shader programming are not covered in this guide. There are several tutorials outthere, some of them even Qt specific. If you do not yet have any experience with shaders, werecommend that you first read the OpenGL-Tutorial1. In this section, we will only give youadvice on how to use shaders in QML/3d and show you how to use them.

In QML/3d, shader programming is possible using the ShaderProgram element, which is de-rived from the more general Effect element. The ShaderProgram element has been extended bytwo properties: fragmentShader and vertexShader. Both take a string that contains the GLSLshader code.

10.1 Bulletshader

The shader for the fired bullets will mix two textures, rotate them and adjust the interpolationlevel over time. The result should be the impression of a rotating and blinking object.

For rotation and interpolation, we are defining two new properties in the ShaderProgram ele-ment. A special feature of the ShaderProgram is the automatic property to uniform binding.This means that if we define a uniform variable in either of the shaders (fragment or vertex),the uniform is automatically bound to the ShaderProgram’s property when they have the samename. The following code serves as an example:

//Lasershader.qmlShaderProgram {

property real angle : 1.0property real interpolationFactor : 1.0property string texture2: "texture2.png"...fragmentShader: "uniform mediump float angle;

1http://qt.nokia.com/learning/guides

49

Page 54: Game Development Qt 3 d

Game Development with Qt3D, Release 0.1 (default)

uniform mediump float interpolationFactor;uniform sampler2D texture2;..."

}

What you should also notice is that you can not only bind simple integer and float variablesto uniforms, but also textures and matrices. Textures are therefore defined as string properties,however, the first texture can be defined using the Effect’s texture property and is bound to theqt_Texture0 uniform.

First we want to define the vertex shader, because it is a fairly simple task:

vertexShader: "attribute highp vec4 qt_Vertex;uniform mediump mat4 qt_ModelViewProjectionMatrix;

attribute highp vec4 qt_MultiTexCoord0;varying mediump vec4 texCoord;

void main(void){gl_Position = qt_ModelViewProjectionMatrix * qt_Vertex;texCoord = qt_MultiTexCoord0;}"

There are the predefined attributes qt_Vertex (vertex position), qt_MultiTexCoord0 (texture co-ordinates) of the currently processed vertex and the qt_ModelViewProjectionMatrix. We wantto pass the texture coordinates to the next stage so we define a varying texCoord that we assignthe texture coordinates to.

The fragment shader will be a bit more involved. There are two major tasks that we have toaccomplish. Firstly, we need to rotate the texture according to the angle value and then we haveto interpolate between the two Sampler2Ds that have been assigned to the shader. The rotationwill be accomplished by rotating the texture coordinates with a 2D rotation matrix. Afterwards,we will be using the built in mix function in order to mix two color values and hand over theinterpolation factor as a third parameter:

fragmentShader: "varying highp vec4 texCoord;uniform sampler2D qt_Texture0;uniform sampler2D texture2;uniform mediump float angle;uniform mediump float interpolationFactor;uniform mediump float hallo;void main(){//The rotation matrixmat2 RotationMatrix = mat2( cos( angle ), -sin( angle ),

sin( angle ), cos( angle ));

vec2 textureC = RotationMatrix*(texCoord.st-vec2(0.5))+vec2(0.5);

mediump vec4 texture1Color = texture2D(qt_Texture0, textureC);mediump vec4 texture2Color = texture2D(texture2, textureC);

50 Chapter 10. Shaders

Page 55: Game Development Qt 3 d

Game Development with Qt3D, Release 0.1 (default)

mediump vec4 textureColor = mix(texture1Color, texture2Color, interpolationFactor);gl_FragColor = textureColor;}"

Now we also want to animate the interpolationFactor and angle properties:

//Lasershader.qmlSequentialAnimation on interpolationFactor{

running: true; loops: Animation.InfiniteNumberAnimation {

from: 0.3; to: 0.7;duration: 800

}PauseAnimation { duration: 200 }NumberAnimation {

from: 0.7; to: 0.3;duration: 800

}PauseAnimation { duration: 500 }

}

NumberAnimation on angle{from:0to: Math.PIduration: 1000;running: true; loops: Animation.Infinite;

}

For enabling this Effect on our bullets, we have two options. The first option would be todirectly assign the Lasershader to the effect property of the bullet, which would mean thatwhenever a new bullet is created, a new ShaderProgram is also created:

//Bullet.qml...effect: Lasershader { }...

The second option would be to create it globally in game.qml and assign the id of the effect tothe bullet’s effect property. The latter method saves more resources, but as you might notice,the angle and interpolationFactor stay the same for all bullets that are shot, and therefore, donot look as good as in the first method:

//game.qml...Lasershader {id:bulleteffect}...

//Bullet.qml...effect: bulleteffect...

10.1. Bulletshader 51

Page 56: Game Development Qt 3 d

Game Development with Qt3D, Release 0.1 (default)

10.2 Explosion

There are many ways to create explosions. Most of them, however, are quite difficult to imple-ment. Our approach will be a very simple one, but quite aesthetic and realistic looking. We usethe Billboarding technique again and combine it with an animation. When an object explodes,one or more quads are created on which an explosion is shown that has been created beforewith a special program for example. In this context, Animated means that several pictures ofan explosion are shown after each other (the same concept, as when watching a movie).

For a good explosion animation, we need at least 10 to 16 pictures to shown one after theother. We can, however, not include them separately in the vertex shader because we onlyhave a certain amount of texture slots available on the graphic card. That is why we merge allexplosion frames together into one big texture. This texture will be uploaded to the GPU andthe fragment shader chooses which parts of the texture to use according to a time value, butfirst of all, we create a new file called Explosion.qml. This will contain one BillboardItem3Dthat uses a quad as a mesh:

//Explosion.qmlimport QtQuick 2.0import Qt3D 1.0

BillboardItem3D {id: explosionItem

scale:2mesh: Mesh { source: "quad.obj" }

}

As already mentioned, we need a lifetime property for our explosion that has to be available inthe fragement shader:

//Explosion.qml...NumberAnimation{

running:truetarget: programproperty: "lifetime"from: 0.0to: 1.0;duration: 1000onRunningChanged: {

if(running==false)explosionItem.enabled= false;

}}...

The ShaderProgram consists of the lifetime property used above, the explo.png texture, whichhas 16 explosion frames, a vertex and a fragment shader:

//Explosion.qml...

52 Chapter 10. Shaders

Page 57: Game Development Qt 3 d

Game Development with Qt3D, Release 0.1 (default)

effect: programShaderProgram {

id: programtexture: "explo.png"property real lifetime : 1.0blending: truevertexShader: "attribute highp vec4 qt_Vertex;uniform mediump mat4 qt_ModelViewProjectionMatrix;

attribute highp vec4 qt_MultiTexCoord0;uniform mediump float textureOffsetX;varying mediump vec4 texCoord;

void main(void){gl_Position = qt_ModelViewProjectionMatrix * qt_Vertex;texCoord.st = qt_MultiTexCoord0.st;}"...

}...

The vertex shader is not really exciting because it just computes the position of the vertex andpasses on the texture coordinates. The fragment shader, however, looks a bit more involved.We first multiply the lifetime by the number of frames we have in our texture and then try tofind out which row and column position is the closest to our current lifetime value:

//Explosion.qml...fragmentShader: "varying highp vec4 texCoord;uniform sampler2D qt_Texture0;uniform mediump float lifetime;

void main(void){mediump int life = int(lifetime*16.0);mediump int row = life % 4;mediump int column = life / 4;mediump vec4 textureColor = texture2D(qt_Texture0, vec2(texCoord.s/4.0+0.25*float(row) , 1.0-texCoord.t/4.0-0.25*float(column)));gl_FragColor = textureColor;}"...

Suitable animated explosions can be found everywhere on the internet. There is also softwarethat can produce these textures from scratch. This technique is not only limited to displayingexplosions. Thunderbolts and fire can also be animated.

The last thing needed for our explosion to work is the integration into our game. There areseveral ways of doing this. Either we define a global explosion which can be moved to theposition of the exploding object or we implement the explosion in the objects. We now createa completely new component that can handle all possible explosions. For the new component,ExplosionSystem.qml is created:

10.2. Explosion 53

Page 58: Game Development Qt 3 d

Game Development with Qt3D, Release 0.1 (default)

//ExplosionSystem.qmlimport QtQuick 2.0

Timer {id: explosionrunning: trueproperty int loops: 40property variant position: Qt.vector3d(0,0,0)property variant explosionComponent: Qt.createComponent("Explosion.qml")property real variation: 3

signal finished()

interval: 200repeat: trueonTriggered: {

loops--var object = explosionComponent.createObject(level, {"x": position.x+(Math.random()-0.5) * variation,

"y": position.y+(Math.random()-0.5) * variation,"z": position.z+(Math.random()-0.5) * variation})

if (loops==0) {finished()explosion.destroy()

}}

}

Note: Because we destroy the object after the loops property reaches 0, the ExplosionSystemcomponent may only be created dynamically with the createObject function.

What’s Next?

Next we will finalize the game and talk about how we can extend and improve it.

54 Chapter 10. Shaders

Page 59: Game Development Qt 3 d

CHAPTER

ELEVEN

FINALIZING THE GAME

Although the game is already playable, a few things are obviously still missing to bring it up toa round figure. We have to extend the current game for that - add new levels and enemies andmake small adjustments to enhance game play.

More levels can easily be added. We need, however, a level counter and to exend the GameL-ogic.qml. After the BossFight, the highscore dialog will be shown but the Game state will berevoked again. In further levels, the targets could for example change into something else andnot fly straight towards the player but maybe move on the way on the y- or x-axis. This makesit mor difficult for the player to hit them.

Furthermore, small power-ups could be collected that could improve the hitpoints, energy levelor enhance maneuverability. In future levels, there could also be targets that have to be avoidedbecause they might cause damage to the player or decrease abilities.

As mentioned before, the boss enemy could also be greatly improved by giving it a differentmoving pattern and other weapons. It should be able to move back and forth, avoid the playersweaponfire and perform some unexpected movements.

All of this can be accomplished by just extending the current structure. The final version ofthis game includes some of the mentioned enhancements. However these are not documentedin this guide any more.

55