Privacy policyContactsAbout siteOpinionsGitHubDonate
© EVILEG 2015-2018
Recommend hosting
TIMEWEB

Game on QML - Lesson 1. Games arena and dynamic objects

JavaScript, QML, Qt, динамические объекты, игровая арена

After the first experience of writing games on Qt for Android, I want to share this experience together and offer to write a simple game in the style of "kill the mole." This is a game in which you need to have time to hit the moles, which climbs out of their holes, but considering that this is a simplified game, instead of moles using the circular target, which will appear on the playing field. As the playing field will use the net 6 6 cells. But to create a field will not be used to some special object of type GridLayout . In the game board will be formed of a grid of rows, columns and the side length of a square cell. Data on occupancy cells to be stored in a two-dimensional array, which will be formed in javascript component (to forget that QML - a declarative JSON-like language with javascript enabled).

Project structure

The project consists of the following files:

  • TargetGame.pro - the profile of the project;
  • deployment.pri - build file and project deploya;
  • main.cpp - the file with the main function of the project;
  • main.qml - basic QML-layer file with the object of the main application window;
  • Target.qml - QML file with the description of the target object;
  • Target.png - the appearance of the target pattern is png;
  • GameArea.qml - gaming arena with the grid to accommodate the targets;
  • logic.js - file with javascript logic games.

We will study the contents of only those files that are different from the default content when creating the project.

main.qml

The main application window is an object of type GameArea , which is described in GameArea.qml . Since the width and height of the playing area is calculated automatically depending on the mesh size and mesh, we simply arrange the gaming arena in the middle of the application window.

A distinctive aspect of this file is to connect logic.js file as Logic type that will meet with the logic of the game and in the file, after the creation of the object of the main application window with the gaming arena, we will give a pointer to the gaming arena in the core logic of the game to remember the current state of the playing field and carry the primary initialization of the playing field, as well as the playing field of the array, which will be responsible the content of grid cells. This is done by calling function newGameState() , and transfer to it as an argument id gaming arena. In this tutorial, we do not track the state of the game, that is, if the game is started or stopped, so the gaming arena timer start immediately after the application starts.

import QtQuick 2.7
import QtQuick.Controls 1.5
import "logic.js" as Logic

ApplicationWindow {
    visible: true
    width: 420
    height: 480
    title: qsTr("Target")

    GameArea {
        id: gameArea
        anchors.centerIn: parent
    }

    // After creating a window initialize the state of the playing field
    Component.onCompleted: {
        Logic.newGameState(gameArea)
    }
}

GameArea.qml

Since the core logic in javascript figures we have in all aspects of the game, then connect the file here.

Games Arena is a rectangle with properties from are preset rows and columns, as well as the size of one side of the cell. Depending on these properties and the calculated width and height of the game arena. Also in the gaming arena, there is a timer which is triggered by the target will be created.

Creating targets accompanied by checking the availability of the randomly selected cell. The choice of the cell is carried out by selecting random rows and columns, by obtaining a random integer in the selected range from minimum to maximum, inclusive using getRandomRound(min, max) function, which is also described in logic.js.

checkEmptyField(row, column) function checks whether there is any object in a given cell, and if the cell is empty, then the target is created using createTarget(parent, row, column) function.

import QtQuick 2.7
import "logic.js" as Logic

Rectangle {
    id: grid
    property int squareSize: 64 // The size of the game grid cell
    property int rows: 6        // The number of grid lines
    property int cols: 6        // The number of grid columns

    width: cols * squareSize    // The width and height of the playing area
    height: rows * squareSize   // It will depend on the number of columns, rows, and the size of one cell

    // Timer to create a target
    Timer {
        id: createTargetsTimer
        interval: 350; running: true; repeat: true;
        // Every second, choose a random cell,
        // check the presence of a target in it, and if the cell is empty,
        // then create a target
        onTriggered: {
            var targetRow = Logic.getRandomRound(0, rows - 1);
            var targetColumns = Logic.getRandomRound(0, cols - 1);
            if (Logic.checkEmptyField(targetRow, targetColumns)) {
                Logic.createTarget(grid ,targetRow, targetColumns)
            }
        }
    }
}

Target.qml

Before studying the contents of the logical core javascript game, read the description of the target object.

The logic of the behavior of the target Is following. Once created, the target should disappear after a random period of time. And not just disappear, that is, changing the level of transparency, and self-destruct. For this is the destroy() method, which will be applied to the root of the object, its id: root. You can also see destroyTarget(row, column) function in javascript core games. This function is used to clear the cell array of two-dimensional information grid.

Since cell size is 64 pixels, and the target size of 56 pixels, then using some simple calculations, we determine the position of the target on the playing field, depending on the columns and rows that it should occupy.

import QtQuick 2.7
import "logic.js" as Logic

Image {
    id: root
    property int row: 0
    property int col: 0

    width: 56
    height: 56
    x: (col * (width + 8)) + 4
    y: (row * (height + 8)) + 4

    source: "Target.png"  // Load the target image

    Timer {
        interval: Logic.getRandomRound(350, 1500); running: true; repeat: false
        onTriggered: root.opacity = 0.0
    }

    // When the image is clear. we will destroy the object itself
    onOpacityChanged: {
        if (opacity == 0.0) {
            Logic.destroyTarget(row, col)
            root.destroy()
        }
    }

    // Set the behavior of the animation properties of the object transparency
    Behavior on opacity { PropertyAnimation { duration: 250 } }
}

logic.js

This file has define .pragma library .

.pragma library

This is due to the need to be able to access the games of ( var gameState ) from any location where the attached file in QML layer. In fact, the variables are shared resources for the various objects in the game. And the most important resource is gameState (aka play arena GameArea ). The fact is that if we can refer to the playing arena directly id of various objects which are declared statically in the game arena, or in the file where the arena declared itself, the dynamic resources, such as in Target.qml , have such a the method does not work. For this and used gameState of logic.js , which allows you to check the condition of the playing area regardless of whether the object is declared statically (under the condition I understand, whether the game or put on pause, as will be discussed in the following articles in the running).

Now let us turn to the content of logic.js file. It features two of the most interesting points:

  1. This is creating a two-dimensional array to javascript, which serves as an information model of the objects contained in the grid, and checking for the existence of the object in the cell.
  2. It is the creation of dynamic targets.

If the first all is clear enough from the code, then about the second to say a few words. To dynamically create a target, in this case, a component of the workpiece, which is created from Target.qml file. Moreover, such a workpiece needs only one set even for the same elements of the game.

var targetComponent = Qt.createComponent("Target.qml");

A creation of the object directly on the playing arena is carried out below the next function.

function createTarget(parent, row, column)
{
    var target = targetComponent.createObject(parent, {"row": row, "col": column})
    gameField[row][column] = target;
}

In this case it is sufficient to create an object, using createObject method for harvesting a target component indicating the parent object, i.e. the object which is placed a target (in this case it will be the game arena) and listing the parameters that must be set when an object, in this case is the row and column, which will be located a target.

Full file listing

// We declare as a library to have access to shared resources, such as state of game
.pragma library

var gameState       // Local state of the game
                    // in our case it will be a play area - gameArea
function getGameState() { return gameState; }

var gameField;      // The playing field, the game grid
// Create a template for the targets
var targetComponent = Qt.createComponent("Target.qml");

// Initialize a new state of the game
function newGameState(gameArea)
{
    gameState = gameArea;
    // The game grid will serve a two-dimensional array 
    // that will store information about the presence of objects in the cells
    gameField = create2DArray(gameState.rows, gameState.cols);
    return gameState;
}
// Function get a random integer in the range of numbers inclusive
function getRandomRound(min, max)
{
    return Math.round(Math.random() * (max - min) + min);
}

// Create the target component from the template
function createTarget(parent, row, column)
{
    var target = targetComponent.createObject(parent, {"row": row, "col": column})
    gameField[row][column] = target;
}

// The target is removed from the grid array
function destroyTarget(row, column)
{
    gameField[row][column] = null;
}

// The option to create a two-dimensional array of grid
function create2DArray(rows, columns)
{
  var arr = [];

  for (var i=0;i<rows;i++) {
     arr[i] = [];
  }

  return arr;
}

// Checking for the presence of any object in the selected cell
function checkEmptyField(row, column)
{
    if (gameField[row][column] == null) {
        return true;
    } else {
        return false;
    }
}

Conclusion

As a result, we get the program to the board 6 by 6, which will be generated when you start disappearing targets.

Download the source of Game sample on QML from GitHub

The entire series of lessons

Video

10% refund of hotel reservation amount on Booking
10% refund of hotel reservation amount on Booking
We offer a link with a 10% return on the amount of the order when booking a hotel through Booking

Comments

Only authorized users can post comments.
Please, Log in or Sign up
МБ
April 21, 2019, 9:40 a.m.
Моисей Бушуев

Qt - Test 001. Signals and slots

  • Result:0points,
  • Rating points-10
AA
April 17, 2019, 7:40 p.m.
Anton Ablin

Qt - Test 001. Signals and slots

  • Result:73points,
  • Rating points1
E
April 17, 2019, 6:16 p.m.
Evgeny

Qt - Test 001. Signals and slots

  • Result:100points,
  • Rating points10
Last comments
April 21, 2019, 4:22 p.m.
Евгений Легоцкой

Через метод setIcon table.horizontalHeaderItem(0).setIcon("qrc://path/to/icon.png")
April 21, 2019, 3:48 p.m.
Евгений Легоцкой

Добрый день! Спасибо за комментарий. Там действительно лучше будет сделать с инициализацией по умолчанию.
U
April 18, 2019, 3:37 p.m.
Unreal_man

А как иконку в хедер задать?
u
April 18, 2019, 2:15 a.m.
uaa

доброго времени,большое спасибо за пример для начинающего)при адаптации к своему проекту столкнулся с таким ньансом:в vepolyline.h в 47 строке нужна инициализация по умолчанию: int m_pointF...
E
April 11, 2019, 12:49 p.m.
Evgeny

Спасибо за ответ) У меня компоновщик на нее ругался просто. Оказалось, просто забыл Q_OBJECT в начале класса указать.
Now discuss on the forum
April 21, 2019, 4:16 p.m.
Евгений Легоцкой

Приветствую Нужно сохранять где-то выбранное значение, а потом восстанавливать его. Или использовать QSettings или добавить метод open(), в который передавать начальные значения для того...
R
April 19, 2019, 9:55 a.m.
RED_Spider

мені важко це зараз навіть перевірити, тому що знайшов коміт, це ще було в 2016 році, і цей код не буде працювати коректно зараз, єдине скажу що це були QThread
i
April 17, 2019, 3:03 p.m.
ilya.guzikov

BlinCT, на стороне ++ это делать необходимо так как в qml при использовании функции append происходит перерисовка всех точек лини(как я понимаю) и из-за этого при использовании больших массиво...
April 10, 2019, 11:20 a.m.
Алексей Внуков

может тоже кому надо будет - QML не принимает QVector<QVector<int>> , при попытке вывести полученый вектор QML показывает что это QVariant(QVector<QVector<int> ...
Join us in social networks

For registered users on the site there is a minimum amount of advertising