×
Namespaces

Variants
Actions
Revision as of 13:51, 13 June 2012 by hamishwillee (Talk | contribs)

Collision Detection In QtQuick

From Nokia Developer Wiki
Jump to: navigation, search

This article explains how easy pixel collision detection can be implemented in QML by using QML features as object property binding, object transition and object signal-slots communication mechanism. Also the article shows number of limitations of that implementation approach and ways how to workaround them

Article Metadata
Code Example
Installation file: qbloks
Tested with
Devices(s): Symbian emulator, Desktop target
Compatibility
Platform(s): Qt 4.7.4 or later
Symbian
Device(s): All*
Dependencies: Qt Quick 1.0
Platform Security
Signing Required: Self-Signed
Capabilities: none
Article
Keywords: QML basic elements
Created: izinin (02 Mar 2012)
Last edited: hamishwillee (13 Jun 2012)

Contents

Introduction

Collision detection is a crucial part of any game implementation. There are number of physic engines available for developers and there are tries to apply some of them in QML projects as for example MotoTrialRacer game that uses Box2D engine. Physic engines implement sophisticated algorithms to emulate physic of object interaction in real world. The API requires C++ components implementation for QML projects. Sometimes that is too much for casual QML games. Can we find a cheaper solution ? QML offers great things as declarative programming approach , object transitions, signal-slot mechanism. Do we need extra API that will duplicate these features in some sense? That is what this article about

The project prototype

There is a game board a QML Rectangle element that serves as visual container for all the rest game components. Game component is an object that can fall and collide with other elements. On the elements collision falling object stops. Game component can consist of several QML Rectangle elements that can detect their position on screen in device coordinates then each element checks point ahead of that position in desired direction whether there is other game component. Thanks to QML the implementation code is very concise. There is no explicit loops in the code -- each element catches Y coordinate change signal broadcasted from game component. We unify the elements behavior just for simplicity sake and amazingly we do not get visible performance impact -- QML does the job nicely!

gameboard with falling figures

While figure is falling you can control its horizontal movement by mouse click from right or left side of the figure. If you click on the figure, it will rotate by 90 degree.

Implementation details

The actors

  • game board is implemented in blocks.qml file (please see project package attached to the article File:Blocks.zip).
It instantiates figure and makes it falling. When figure reached the bottom of the game board , game board creates new figure and makes it falling.
It captures mouse click outside of the falling figure and passes the signal to figure to allow it some action. As you see we try to follow good old object incapsulation implementation principle.
  • figure is an composition of smaller elements. It is implemented in figure.qml file
it handles mouse clicks inside the figure and rotates itself
it implements horizontal movement signalled from the game board
it is container for smaller elements
it has internal states : falling , stopped, reached-the-board-bottom. The latter is kind of axillary state needed to detect situation when no collision is expected but figure reached the board bottom and must stop
  • block is a smallest visible elements in the game. It is implemented in block.qml file
it implements collision detection
when collision happens it signals to its parent , figure to change state from 'falling' to 'stop'

Collision detection

This humble function below does this job --- sorry no math at all.

.....
function collisionDetection(){
var figure = parent.parent;
var board = figure.parent;
var stepahead = 1;
 
var obj = parent.mapToItem(board, x, y);
var pointx = obj.x;
var pointy = obj.y;
pointx += width / 2;
pointy += height + stepahead;
 
var foreign = board.childAt(pointx, pointy);
if(foreign !== null && foreign.objectName !== '' && foreign.objectName !== figure.objectName){
figure.state = "stop";
}
}
 
Connections{
target: figure
onCheckmove:{
collisionDetection();
}
}
......

The main point is : first you need to translate object's coordinate to game board coordinate and then by using game board coordinates query for board's children. When found children is the other figure then you have detected collision. There is small problem -- the game board has several children object that we do not want to count, as for example MouseArea and figure itself. How we can recognize other figure? --- Simple! Using time stamp as figure object name does the trick.

here is figure creation dynamically in the game board:

.....
var figure = component.createObject(board, {
objectName: Qt.formatTime(new Date(), "hhmmsszzz"),
x: (board.width / 2),
y:0,
boardWidth: board.width,
boardHeight: board.height,
control: control
});
figure.reachedBottom.connect(createFigure)
.....

Note the last line -- is another trick i like to demonstrate. It connects signal from dynamically created object to the parent object. I see that is the best pattern to do so because you do not need to keep reference to variable 'figure' to use it later when the variable is gone when you go out of the function scope.

...And finally the implementation problem

It looks pretty nice, but try to rotate falling object and see how it will collide. It calculates figure position incorrectly! What is wrong! -- Simple: that is QML transformation in action. When you rotate the figure, 'x' and 'y' remains unchanged but transformation is applied dynamically when the figure is drawn. Such behavior makes it easy to layout objects in standard UI but leaves developer hopeless. On this problem there is an open bug report https://bugreports.qt-project.org/browse/QTBUG-18161 At least it is accepted and being analyzed by Qt team.

I see two workarounds here:

  • not to use transformations. In that case you need to implement matrix rotation -- (don't confuse with rotating matrix) -- not big deal to google for ready solution
  • subclass Rectangle element with method that will allow you to convert element's coordinates to device coordinates and vice versa. check QPainter class. I consider this missing as QML implementation flaw. Please argue me

Downloads

QML UI project File:Blocks.zip

Commercial release

qblocks

117 page views in the last 30 days.
×