Evgenii Legotckoi
Evgenii Legotckoi14 липня 2016 р. 05:57

Гра на QML - Урок 1. Ігрова арена та динамічні об'єкти

Після першого досвіду написання гри на Qt під Android, хочу поділитися цим досвідом і пропоную разом написати простеньку гру в стилі "Убий крота". Це гра, в якій потрібно встигати потрапляти по кротах, які вилазять з нір, але враховуючи, що це буде спрощена гра, замість кротів використовуємо круглі мішені, які будуть з'являтися на ігровому полі. Як ігрове поле використовуватиме сітку 6 на 6 осередків. Але для створення поля не буде використовуватися якийсь спеціальний об'єкт типу GridLayout . В ігровому полі сітка формуватиметься з кількості рядків, колонок та довжини сторони квадратного осередку. Дані про заповненість осередку будуть зберігатися в двовимірному масиві, який буде сформований в javascript складовій (забуваємо, що QML - це декларативна JSON-подібна мова з підтримкою javascript).

Структура проекту

Проект складається з наступних файлів:

  • TargetGame.pro - профайл проекту;
  • deployment.pri - файл складання та деплою проекту;
  • main.cpp - файл з main функцією проекту;
  • main.qml - основний файл QML-шару з об'єктом головного вікна програми;
  • Target.qml - файл QML з описом об'єкта мішені;
  • Target.png - зовнішній вигляд мішені є png малюнком;
  • GameArea.qml - ігрова арена з сіткою для розміщення мішеней;
  • logic.js - файл із javascript логікою гри.

Вивчимо зміст лише тих файлів, які від змісту за умовчанням під час створення проекту.


main.qml

У головному вікні програми розміщується об'єкт типу GameArea, який описаний в GameArea.qml. Оскільки ширина та висота ігрової арени розраховується автоматично залежно від розмірів сітки та осередків сітки, то ми просто розташуємо ігрову арену в середині вікна програми.

Відмінним моментом цього файлу є підключення файлу logic.js як тип Logic , який відповідатиме з логіку гри і в даному файлі, після створення об'єкта головного вікна програми з ігровою ареною, ми передамо вказівник на ігрову арену в ядро логіки гри, щоб запам'ятати поточний стан ігрового поля та провести первинну ініціалізацію ігрового поля, а також масиву ігрового поля, що відповідатиме вміст осередків сітки. Робиться це викликом функції newGameState(), і передачею до неї як аргумент id ігрової арени. У даному уроці ми не відстежуємо стан гри, тобто чи запущено гру або зупинено, тому ігровий таймер арени запуститися відразу ж після запуску програми.

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
    }

    // По окончании создания окна инициализируем состояние игрового поля
    Component.onCompleted: {
        Logic.newGameState(gameArea)
    }
}

GameArea.qml

Оскільки логічне ядро на JavaScript фігурує у нас у всіх аспектах гри, то підключаємо цей файл і тут.

Ігрова арена є прямокутником з властивостями попередньо заданих рядків і колонок, а також розмірів однієї сторони осередку. Залежно від цих властивостей і розраховуються ширина та висота ігрової арени. Також в ігровій арені присутній таймер, зі спрацьовування якого створюватимуться мішені.

Створення мішеней супроводжується перевіркою доступності випадково вибраної комірки. Вибір осередку здійснюється за допомогою вибору випадкових рядки та колонки, за допомогою отримання випадкового цілого числа у вибраному діапазоні від мінімального до максимального включно за допомогою функції getRandomRound(min, max) , яка також описана в logic.js.

Функція checkEmptyField(row, column) перевіряє, чи існує якийсь об'єкт у заданій комірці, і якщо комірка порожня, то створюється ціль за допомогою функції createTarget(parent, row, column).

import QtQuick 2.7
import "logic.js" as Logic

Rectangle {
    id: grid
    property int squareSize: 64 // Размер ячейки игровой сетки
    property int rows: 6        // Количество строк сетки
    property int cols: 6        // Количество колонок сетки

    width: cols * squareSize    // Ширина и высота игровой арены
    height: rows * squareSize   // Будет зависеть от количества колонок, строк и размера одной ячейки

    // Таймер для создания мишеней
    Timer {
        id: createTargetsTimer
        interval: 350; running: true; repeat: true;
        // Раз в секунду выбираем случайную ячейку,
        // проверяем наличие мишени в ней, и если ячейка пустая,
        // то создаём мишень
        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

Перш, ніж вивчати вміст логічного javascript ядра гри, вивчимо опис об'єкта мішені.

Логіка поведінки мішені така. Після створення, мета має зникнути через випадковий проміжок часу. Причому не просто зникнути, тобто змінивши рівень прозорості, а самознищення. Для цього є метод destroy(), який буде застосований на кореневому об'єкті, його id: root. Також Ви можете побачити функцію destroyTarget(row, column) у javascript ядрі гри. Ця функція служить для очищення осередку двовимірного інформаційного масиву сітки.

Оскільки розмір комірки становить 64 пікселі, а розмір мішені 56 пікселів, то використовуючи нехитрі обчислення, ми визначаємо положення мішені на ігровому полі залежно від колонки та рядка, які вона повинна займати.

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"  // Загружаем изображение мишени

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

    // Когда изображение станет прозрачным. мы уничтожим сам объект
    onOpacityChanged: {
        if (opacity == 0.0) {
            Logic.destroyTarget(row, col)
            root.destroy()
        }
    }

    // Задаём поведение анимации свойства прозрачности объекта
    Behavior on opacity { PropertyAnimation { duration: 250 } }
}

logic.js

Цей файл містить оголошення .pragma library *. *

.pragma library

Це зроблено у зв'язку з необхідністю, щоб можна було звертатися до стану гри ( var gameState ) з будь-якого місця, де цей файл підключений в QML шарі. Фактично змінні стають ресурсами для різних об'єктів у грі. І найголовнішим ресурсом є gameState (він же ігрова арена GameArea ). Справа в тому, що якщо ми можемо звертатися до ігрової арени безпосередньо по id з різних об'єктів, які статично оголошені в ігровій арені, або у файлі, де оголошена сама арена, то в динамічних ресурсах, як, наприклад, у Target .qml , у нас такий спосіб не спрацює. Для цього і використовується gameState з logic.js, який дозволяє перевіряти стан ігрової арени незалежно від того, чи оголошено об'єкт статично (під станом я розумію, чи запущена гра, чи поставлена на паузу, що буде розглянуто у наступних статтях).

Тепер звернемося до вмісту файлу logic.js. Тут є два найцікавіші моменти:

  1. Це створення двовимірного масиву на JavaScript, який служить інформаційною моделлю про об'єкти, що містяться в сітці, і перевірка на предмет існування об'єкта в осередку.
  2. Це створення динамічних мішеней.

Якщо з першим все досить зрозуміло з коду, про друге скажу кілька слів. Для динамічного створення мішені в даному випадку використовується компонент заготівля, яка створюється з файлу Target.qml. Причому така заготівля потрібна лише одна навіть для безлічі таких однотипних елементів у грі.

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

А створення безпосередньо самого об'єкта на ігровій арені здійснюється нижче наступної функції.

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

У нашому випадку достатньо створити об'єкт, застосувавши метод createObject на заготівлі компонента мішені із зазначенням батьківського об'єкта, тобто об'єкта в якому буде розташовуватися мішень (в даному випадку це буде ігрова арена) і перерахувавши параметри, які повинні бути встановлені при створенні об'єкта, в даному Якщо це рядок і колонка, в яких буде розміщуватися мета.

Повний лістинг файлу

// Объявляем, как библиотеку, чтобы иметь доступ
// к разделяемым ресурсам, например состоянию игры
.pragma library

var gameState       // Локальное объявление состояния игры
                    // в нашем случае это будет игровая область gameArea
function getGameState() { return gameState; }

var gameField;      // Игровое поле, игровая сетка
// Создаём заготовку для мишеней
var targetComponent = Qt.createComponent("Target.qml");

// Инициализируем новое состояние игры
function newGameState(gameArea)
{
    gameState = gameArea;
    // Игровой сеткой будет служить двумерный массив,
    // в котором будем сохранять информацию о наличии в ячейках объектов
    gameField = create2DArray(gameState.rows, gameState.cols);
    return gameState;
}
// Функция получения случайного целого числа
// в диапазоне чисел включительно
function getRandomRound(min, max)
{
    return Math.round(Math.random() * (max - min) + min);
}

// Создаём мишень из компонента заготовки
function createTarget(parent, row, column)
{
    var target = targetComponent.createObject(parent, {"row": row, "col": column})
    gameField[row][column] = target;
}

// Мишень удаляется из массива сетки
function destroyTarget(row, column)
{
    gameField[row][column] = null;
}

// Функция создания двумерного массива сетки
function create2DArray(rows, columns)
{
  var arr = [];

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

  return arr;
}

// Проверка на предмет наличия какого либо объекта в выбранной ячейке
function checkEmptyField(row, column)
{
    if (gameField[row][column] == null) {
        return true;
    } else {
        return false;
    }
}

Підсумок

В результаті ми отримаємо програму з ігровим полем 6 на 6, в якому при запуску будуть генеруватися мішені, що зникають.

Завантажити вихідні записи з Прикладом гри на QML з GitHub

Вся серія уроків

Відеоурок

Рекомендуємо хостинг TIMEWEB
Рекомендуємо хостинг TIMEWEB
Стабільний хостинг, на якому розміщується соціальна мережа EVILEG. Для проектів на Django радимо VDS хостинг.

Вам це подобається? Поділіться в соціальних мережах!

Коментарі

Only authorized users can post comments.
Please, Log in or Sign up
AD

C++ - Тест 004. Указатели, Массивы и Циклы

  • Результат:50бали,
  • Рейтинг балів-4
m
  • molni99
  • 26 жовтня 2024 р. 11:37

C++ - Тест 004. Указатели, Массивы и Циклы

  • Результат:80бали,
  • Рейтинг балів4
m
  • molni99
  • 26 жовтня 2024 р. 11:29

C++ - Тест 004. Указатели, Массивы и Циклы

  • Результат:20бали,
  • Рейтинг балів-10
Останні коментарі
ИМ
Игорь Максимов22 листопада 2024 р. 22:51
Django - Підручник 017. Налаштуйте сторінку входу до Django Добрый вечер Евгений! Я сделал себе авторизацию аналогичную вашей, все работает, кроме возврата к предидущей странице. Редеректит всегда на главную, хотя в логах сервера вижу запросы на правильн…
Evgenii Legotckoi
Evgenii Legotckoi01 листопада 2024 р. 00:37
Django - Урок 064. Як написати розширення для Python Markdown Добрый день. Да, можно. Либо через такие же плагины, либо с постобработкой через python библиотеку Beautiful Soup
A
ALO1ZE19 жовтня 2024 р. 18:19
Читалка файлів fb3 на Qt Creator Подскажите как это запустить? Я не шарю в программировании и кодинге. Скачал и установаил Qt, но куча ошибок выдается и не запустить. А очень надо fb3 переконвертировать в html
ИМ
Игорь Максимов05 жовтня 2024 р. 17:51
Django - Урок 064. Як написати розширення для Python Markdown Приветствую Евгений! У меня вопрос. Можно ли вставлять свои классы в разметку редактора markdown? Допустим имея стандартную разметку: <ul> <li></li> <li></l…
d
dblas505 липня 2024 р. 21:02
QML - Урок 016. База даних SQLite та робота з нею в QML Qt Здравствуйте, возникает такая проблема (я новичок): ApplicationWindow неизвестный элемент. (М300) для TextField и Button аналогично. Могу предположить, что из-за более новой верси…
Тепер обговоріть на форумі
Evgenii Legotckoi
Evgenii Legotckoi25 червня 2024 р. 01:11
добавить qlineseries в функции Я тут. Работы оень много. Отправил его в бан.
t
tonypeachey115 листопада 2024 р. 17:04
google domain [url=https://google.com/]domain[/url] domain [http://www.example.com link title]
NSProject
NSProject04 червня 2022 р. 13:49
Всё ещё разбираюсь с кешем. В следствии прочтения данной статьи. Я принял для себя решение сделать кеширование свойств менеджера модели LikeDislike. И так как установка evileg_core для меня не была возможна, ибо он писался…
9
9Anonim25 жовтня 2024 р. 19:10
Машина тьюринга // Начальное состояние 0 0, ,<,1 // Переход в состояние 1 при пустом символе 0,0,>,0 // Остаемся в состоянии 0, двигаясь вправо при встрече 0 0,1,>…

Слідкуйте за нами в соціальних мережах