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

Гра на QML - Урок 2. Додавання слідів пострілів і системи очок

Після того, як була зроблена ігрова арена та додані мішені, настав час реалізувати попадання по мішеням, промахи та систему нарахування та списування очок.

Система нарахування та списування очок буде наступна:

  • У разі промахів, пропоную зробити сліди кульових отворів, декількох видів, а також списування по одному окуляру з ігрового рахунку.
  • У разі зникнення мішені без влучення по ній також зробити списування одного очка з ігрового рахунку.
  • У разі влучення по мішені нараховувати від одного до п'яти очок залежно від того, наскільки близько до центру довелося це влучення.

Рахунок буде відображатися у верхній частині ігрового поля.

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

Для реалізації цього задуму знадобиться, крім модифікації частини файлів, додати такі файли:

  • InfoBar.qml - який відображатиме інформацію про рахунок;
  • Bullet.qml - файл з описом типу кульового отвору;
  • bullet-1.png, bullet-2.png, bullet-3.png, bullet-4.png, bullet-5.png, bullet-6.png - файли із зображеннями кульових отворів;

Можливі варіанти кульових отворів розміщуються в папці graphics, туди було перенесено зображення мішені.


Зображення кульових отворів

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

GameArea.qml

Перш ніж переходити до розгляду вмісту нових файлів, звернімося до зміненого вмісту файлу GameArea.qml, а також логічного ядра на JavaScript у файлі logic.js.

Основними нововведеннями є поява якості scores для підрахунку очок, поява InfoBar і обробника кліків миші всередині ігрової арени.

Властивість scores містить поточний рахунок та його значення передається властивості scores у infoBar для візуалізації поточного рахунку гравцю.

У разі натискання на ігровій арені, що дорівнює промаху, створюється об'єкт кульового отвору і на одиницю зменшується поточний рахунок. Кульовий отвір створюється подібно до того, як реалізовано створення мішеней у минулому уроці, тобто за допомогою логічного ядра Logic, яке знаходиться у файлі logic.js.

import QtQuick 2.7
import "logic.js" as Logic

Rectangle {
    id: grid

    ...

    property int scores: 0      // Очки

    ...

    InfoBar {
        id: infoBar
        scores: grid.scores
    }

    // Создаём след от пули при клике по игровой арене
    MouseArea {
        anchors.fill: parent
        onClicked: {
            Logic.createBullet(grid, mouse.x, mouse.y)
            grid.scores--;
        }
    }

    ...
}

logic.js

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

... 

// Создаём заготовку для мишеней
var bulletComponent = Qt.createComponent("Bullet.qml");

...

// Создаём след от пули
function createBullet(parent, x, y)
{
    bulletComponent.createObject(parent, {"x": x, "y": y});
}

...

InfoBar.qml

InfoBar являє собою звичайний RowLayout, який буде прибитий до верхнього краю ігрової арени і розтягнутий по її ширині. Поточний рахунок відображатиметься у текстовому полі з id: scores . Щоб встановити доступ ззовні до властивості тексту цього текстового поля, необхідно скористатися властивістю property alias .

import QtQuick 2.7
import QtQuick.Layouts 1.1

RowLayout {
    height: 40
    anchors.left: parent.left
    anchors.right: parent.right
    anchors.top: parent.top
    anchors.leftMargin: 6
    anchors.rightMargin: 6
    spacing: 6
    z: 100

    // Данное свойство устанавливает доступ к текстовому полю,
    // в которое будет устанавливаться текущий счёт
    property alias scores: scores.text

    Text {
        text: qsTr("Scores")

        font.pixelSize: 20
        verticalAlignment: Text.AlignVCenter
        horizontalAlignment: Text.AlignHCenter
    }

    Text {
        // Текстовое поле с текущим счётом
        id: scores

        font.pixelSize: 20
        verticalAlignment: Text.AlignVCenter
        horizontalAlignment: Text.AlignHCenter
    }

    // Своеобразный Spacer, который в RowLayout сдвинет текстовые поля к левому краю InfoBar
    Item {
        Layout.fillHeight: true
        Layout.fillWidth: true
    }
}

Bullet.qml

Поведінка кульового отвору аналогічна до поведінки мішені. Час життя кульового отвору обмежений і після зникнення цей об'єкт знищується. Відмінність полягає в тому, що робиться зсув координати (0, 0) в середину об'єкта, щоб центр об'єкта збігався точно з центром точки, куди було здійснено клік. А також відмінність полягає в тому, що в об'єкті є масив із зображеннями, з якого під час створення виконується випадковий вибір зовнішнього вигляду кульового отвору.

import QtQuick 2.7
import QtQuick.Window 2.2
import "logic.js" as Logic

Image {
    id: root
    width: 40
    height: 40

    // Устанавливаем смещение координаты (0, 0) в центр изображения
    transform: Translate {
        x: -width / 2
        y: -height / 2
    }

    // Задаём список массив возможных изображений выстрела пули
    property variant bullets: ["graphics/bullet-1.png", "graphics/bullet-2.png", "graphics/bullet-3.png",
                               "graphics/bullet-4.png", "graphics/bullet-5.png", "graphics/bullet-6.png"]

    // При создании объекта выбираем случайное изображение
    source: bullets[Logic.getRandomRound(0, 5)]

    // Таймер, который будет отсчитывать случайную величину от 3500 до 10000 мс
    // и по окончании запускать исчезновение следа от пули
    Timer {
        interval: Logic.getRandomRound(3500, 10000); running: true; repeat: false
        onTriggered: root.opacity = 0.0
    }

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

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

Target.qml

Ну і вся сіль – це влучення по мішені. Як бачите, тут є п'ять полів, яким ми надамо різну вартість від 1 до 5 очок.

Для того, щоб правильно вирахувати попадання по цих областях, нам доведеться застосувати теорему Піфагора. Пам'ятаєте? Із курсу шкільної тригонометрії. Квадрат гіпотенузи дорівнює сумі квадратів катетів.

[латекс]a 2 + b 2 = c^2[/латекс]

Так ось, ця сама гіпотенуза буде дальністю відхилення попадання від центру мішені, ну а факт влучення по тій чи іншій області будемо визначати по радіусу кола, починаючи від найменшого кола. Ці перевірки проводяться у функції isHit() , оголошеної в даному файлі.

Також нюансом тут є те, що зображення мішені міститься в квадрат, а значить, що і область кліка також квадратна, тому якщо клік не потрапляє в область найбільшого кола мішені, то ми повертаємо 0 і передаємо клік далі в ігрову арену. Для цього необхідно встановити propagateComposedEvents у true і заборонити accept події миші, тоді він пройде в об'єкти, що лежать нижче, по координаті z.

Також не забудемо зменшувати на одиницю рахунок гри, якщо мета зникла сама по собі без дії гравця. Це здійснюється через об'єкт, що розділяється, з логічного ядра гри, який зберігає стан ігрової арени. Тобто через Logic.gameState.scores.

import QtQuick 2.7
import "logic.js" as Logic

Image {
    id: root

    ...

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

    MouseArea {
        id: mouseArea
        anchors.fill: parent
        // Данное свойство определяет, будут ли передаваться события клика
        // в ниже лежащие элементы, то есть те, которые по координате z
        // находятся ниже мишени
        propagateComposedEvents: true
        onClicked: {
            var scores =  isHit(root.width/2, root.height/2, mouse.x, mouse.y);
            if (scores != 0) {
                // Если количество очков отлично от нуля, то фиксируем попадание
                // Увеличиваем счёт и уничтожаем мишень
                Logic.gameState.scores += scores;
                Logic.destroyTarget(row, col);
                root.destroy();
            } else {
                // В противном случае передаём событие клика в ниже лежащие элементы
                // по координате z
                mouse.accepted = false;
            }
        }
    }

    ...

    // Проверка попадания по мишени.
    // Проверяем длину линии от центра до места попадания по мишени
    // В зависимости от длины этой линии относительно радиуса 5-ти окружностей, из которых состоит мишень
    // определяем сколько очков получаем за попадание
    // a, b - координаты центра мишени
    // x, y - координаты точки попадания
    function isHit(a, b, x, y)
    {
        var length = Math.sqrt(Math.pow(Math.abs(a - x), 2) + Math.pow(Math.abs(b - y), 2));
        if (length <= 5.6) {
            return 5;
        } else if (length <= 11.2) {
            return 4;
        } else if (length <= 16.8) {
            return 3;
        } else if (length <= 22.4) {
            return 2;
        } else if (length <= 28) {
            return 1;
        } else {
            // В данном случае мы попали в зону MouseArea,
            // которая является прямоугольником, но промахнулись
            // по самой мишени, которая является кругом
            return 0;
        }
    }
}

Підсумок

Тепер у нас є рахунок і зовнішній вигляд програми виглядає так.

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

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

Відеоурок

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

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

Коментарі

Only authorized users can post comments.
Please, Log in or Sign up
Дмитрий

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

  • Результат:60бали,
  • Рейтинг балів-1
Дмитрий

C++ - Тест 003. Условия и циклы

  • Результат:92бали,
  • Рейтинг балів8
d
  • dsfs
  • 26 квітня 2024 р. 11:56

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

  • Результат:80бали,
  • Рейтинг балів4
Останні коментарі
k
kmssr09 лютого 2024 р. 02:43
Qt Linux - Урок 001. Автозапуск програми Qt під Linux как сделать автозапуск для флэтпака, который не даёт создавать файлы в ~/.config - вот это вопрос ))
АК
Анатолий Кононенко05 лютого 2024 р. 09:50
Qt WinAPI - Урок 007. Робота з ICMP Ping в Qt Без строки #include <QRegularExpressionValidator> в заголовочном файле не работает валидатор.
EVA
EVA25 грудня 2023 р. 18:30
Boost - статичне зв&#39;язування в проекті CMake під Windows Ошибка LNK1104 часто возникает, когда компоновщик не может найти или открыть файл библиотеки. В вашем случае, это файл libboost_locale-vc142-mt-gd-x64-1_74.lib из библиотеки Boost для C+…
J
JonnyJo25 грудня 2023 р. 16:38
Boost - статичне зв&#39;язування в проекті CMake під Windows Сделал всё по-как у вас, но выдаёт ошибку [build] LINK : fatal error LNK1104: не удается открыть файл "libboost_locale-vc142-mt-gd-x64-1_74.lib" Хоть убей, не могу понять в чём дел…
G
Gvozdik19 грудня 2023 р. 05:01
Qt/C++ - Урок 056. Підключення бібліотеки Boost в Qt для компіляторів MinGW і MSVC Для решения твой проблемы добавь в файл .pro строчку "LIBS += -lws2_32" она решит проблему , лично мне помогло.
Тепер обговоріть на форумі
G
George1307 травня 2024 р. 07:27
добавить qlineseries в функции в функции: "GPlotter::addSeries(QString title, QVector &arr)" я вызываю метод setChart(...), я в конструктор передал адрес на QChartView элемент
BlinCT
BlinCT05 травня 2024 р. 12:46
Написать свой GraphsView Всем привет. В Qt есть давольно старый обьект дял работы с графиками ChartsView и есть в 6.7 новый но очень сырой и со слабым функционалом GraphsView. По этой причине я хочу написать х…
PS
Peter Son04 травня 2024 р. 00:57
Best Indian Food Restaurant In Cincinnati OH Ready to embark on a gastronomic journey like no other? Join us at App india restaurant and discover why we're renowned as the Best Indian Food Restaurant In Cincinnati OH . Whether y…
Evgenii Legotckoi
Evgenii Legotckoi02 травня 2024 р. 21:07
Мобильное приложение на C++Qt и бэкенд к нему на Django Rest Framework Добрый день. По моему мнению - да, но то, что будет касаться вызовов к функционалу Андроида, может создать огромные трудности.
IscanderChe
IscanderChe30 квітня 2024 р. 11:22
Во Flask рендер шаблона не передаётся в браузер Доброе утро! Имеется вот такой шаблон: <!doctype html><html> <head> <title>{{ title }}</title> <link rel="stylesheet" href="{{ url_…

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