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
AD

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

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

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

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

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

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

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