1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
|
// Copyright (C) 2020 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
/*!
\example quickball
\ingroup quick3d-examples
\title Qt Quick 3D - Quick Ball Example
\examplecategory {3D}
\brief Demonstrates how to create a simple game using Quick3D.
\image quickball-example.jpg
This example demonstrates how to combine Qt Quick and Qt Quick 3D to create
a simple 3D game. The goal of the game is to hit target boxes by throwing
a ball. Points are given based on how fast and with how few balls all targets
are down. Aim well but be quick!
The source code is in a single QML file to emphasize how compact this example
is, considering it being a fully playable game. Let's start by looking at the
main properties. These are quite self-explanatory, and you can easily adjust
them to see how they affect the game.
\snippet quickball/main.qml main properties
The game logic is implemented with JavaScript. View3D contains a function to
start the game, which (re)initializes all required variables and creates the
level targets. It also contains a function to calculate the final points when
the game ends.
\snippet quickball/main.qml view functions
The view also contains a PointLight node to light up the scene. It is
positioned above the objects and set to cast shadows. Note how brightness is
used to darken the playing area when then game has ended. The \l {Light::ambientColor}
{ambientColor} property is used to soften the light contrast, as without it the
bottom parts of the objects would be very dark.
\snippet quickball/main.qml lights
Throwing the ball uses Qt Quick \l [QML QtQuick] MouseArea item, which is only
enabled when game is on, and the ball isn't already moving.
\snippet quickball/main.qml ball handling
Then we get into actual 3D models. Ball model is the biggest one, as it contains
the logic how the ball behaves, its animations, and hit detection. Let's look into
the ball properties first. Ball uses a built-in sphere model, scaled based on
\c ballSize. We use PrincipledMaterial with a \l {PrincipledMaterial::baseColorMap}
{baseColorMap} and a \l {PrincipledMaterial::normalMap} {normalMap} to create tennis
ball appearance.
\image quickball-ball.png
\snippet quickball/main.qml ball model
When mouse is moved or touch-screen is swiped, last \c maxMoves positions
before releasing the ball are stored into \c moves array. When user releases
the ball, \c throwBall() gets called, which calculates the ball direction from
these latest positions, and starts animating it.
\snippet quickball/main.qml ball functions
The ball position is animated separately among different axis. These animations
use previously assigned \c directionX and \c directionY to define where the ball
moves to, as well as \c speed for the ball flying time. Vertical position has two
sequential animations, so we can use easing for ball bounce. When position
animations finish, we'll check if there are still balls left or should the game
end. Finally we animate also rotation of the ball, so user can throw curve balls.
\snippet quickball/main.qml ball animations
Important part of the game playing is detecting when the ball hits the targets.
Whenever ball z position changes, we loop through \c targets array and detect
if ball is touching any of them using \c fuzzyEquals(). Whenever we detect
a hit, we'll call target \c hit() function and check if all targets are down.
\snippet quickball/main.qml ball collisions
Then we can switch to targets. Those are dynamically generated into a grouping
node which contains helper functions and allows e.g. animating all targets as
a group. Note that \c currentTargets property is needed because in QML arrays
changes are not triggering bindings, so we will update the amount of targets
manually.
\snippet quickball/main.qml targets node
Targets are nodes with a cube model and a text element for showing points.
Similarly to the ball model, we use \l {PrincipledMaterial::baseColorMap} {baseColorMap}
and \l {PrincipledMaterial::normalMap} {normalMap} textures to create cubes with
a Qt logo. When the hit is detected, we sequentially animate the cube away and
show the points gained from this target. Once the animation is finished, we will
dynamically remove the target node.
\snippet quickball/main.qml target component
We also need some models for the playing area. Ground model is a rectangle
with grass textures scaled to fill a larger area.
\snippet quickball/main.qml ground model
Sky model is further back, and we don't want shadows cast into the sky, so we set
\l {Model::receivesShadows} {receivesShadows} to false. For the sky we also add
some stars using \l {Qt Quick Particles QML Types} {Qt Quick Particles} module.
Similarly to other 2D Qt Quick elements, also particles can be directly added
inside 3D nodes.
\snippet quickball/main.qml sky model
When we combine the above ground and sky models we get a 3D world like this:
\image quickball-world.png
Finally we add some sparkles for the targets, this time using the
\l {Qt Quick 3D Particles3D QML Types}{QtQuick3D.Particles3D}
module. \l ParticleSystem3D contains a \l SpriteParticle3D and we allocate \c 200
or them, which is enough for two simultaneous \c 100 particle bursts.
\l ParticleEmitter3D defines the emitting properties for the particles like scale,
rotation, velocity and lifeSpan. We also add \l Gravity3D affector to drag the
particles down with a suitable magnitude.
\snippet quickball/main.qml hit particles
This ends the 3D parts of our game. There are still some 2D Qt Quick
elements to show the time, scores, start button etc. which are important
for the game, but not relevant for this Quick 3D documentation.
Now the ball is on your side (pun intended). Feel free to extend the game
in different ways and generate new wacky levels!
*/
|