Evgenii Legotckoi
Evgenii LegotckoiШілде 14, 2016, 5 Т.Ж.

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 . Чтобы установить доступ извне к свойству text данного текстового поля, необходимо воспользоваться свойством 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 очков.

Для того, чтобы правильно вычислить попадание по этим областям, нам понадобиться применить теорему Пифагора. Помните? Из курса школьной тригонометрии. Квадрат гипотенузы равен сумме квадратов катетов.

[latex]a 2 + b 2 = c^2[/latex]

Так вот, эта самая гипотенуза будет дальностью отклонения попадания от центра мишени, ну а факт попадание по той или иной области будем определять по радиусу окружности, начиная от самого меньшего круга. Эти проверки проводятся в функции 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 хостинг.

Ол саған ұнайды ма? Әлеуметтік желілерде бөлісіңіз!

Пікірлер

Тек рұқсаты бар пайдаланушылар ғана пікір қалдыра алады.
Кіріңіз немесе Тіркеліңіз
AD

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

  • Нәтиже:50ұпай,
  • Бағалау ұпайлары-4
m
  • molni99
  • Қаз. 26, 2024, 1:37 Т.Ж.

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

  • Нәтиже:80ұпай,
  • Бағалау ұпайлары4
m
  • molni99
  • Қаз. 26, 2024, 1:29 Т.Ж.

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

  • Нәтиже:20ұпай,
  • Бағалау ұпайлары-10
Соңғы пікірлер
ИМ
Игорь МаксимовҚар. 22, 2024, 11:51 Т.Ж.
Django - Оқулық 017. Теңшелген Django кіру беті Добрый вечер Евгений! Я сделал себе авторизацию аналогичную вашей, все работает, кроме возврата к предидущей странице. Редеректит всегда на главную, хотя в логах сервера вижу запросы на правильн…
Evgenii Legotckoi
Evgenii LegotckoiҚаз. 31, 2024, 2:37 Т.Қ.
Django - Сабақ 064. Python Markdown кеңейтімін қалай жазуға болады Добрый день. Да, можно. Либо через такие же плагины, либо с постобработкой через python библиотеку Beautiful Soup
A
ALO1ZEҚаз. 19, 2024, 8:19 Т.Ж.
Qt Creator көмегімен fb3 файл оқу құралы Подскажите как это запустить? Я не шарю в программировании и кодинге. Скачал и установаил Qt, но куча ошибок выдается и не запустить. А очень надо fb3 переконвертировать в html
ИМ
Игорь МаксимовҚаз. 5, 2024, 7:51 Т.Ж.
Django - Сабақ 064. Python Markdown кеңейтімін қалай жазуға болады Приветствую Евгений! У меня вопрос. Можно ли вставлять свои классы в разметку редактора markdown? Допустим имея стандартную разметку: <ul> <li></li> <li></l…
d
dblas5Шілде 5, 2024, 11:02 Т.Ж.
QML - Сабақ 016. SQLite деректер қоры және онымен QML Qt-та жұмыс істеу Здравствуйте, возникает такая проблема (я новичок): ApplicationWindow неизвестный элемент. (М300) для TextField и Button аналогично. Могу предположить, что из-за более новой верси…
Енді форумда талқылаңыз
m
moogoҚар. 22, 2024, 7:17 Т.Ж.
Mosquito Spray System Effective Mosquito Systems for Backyard | Eco-Friendly Misting Control Device & Repellent Spray - Moogo ; Upgrade your backyard with our mosquito-repellent device! Our misters conce…
Evgenii Legotckoi
Evgenii LegotckoiМаусым 24, 2024, 3:11 Т.Қ.
добавить qlineseries в функции Я тут. Работы оень много. Отправил его в бан.
t
tonypeachey1Қар. 15, 2024, 6:04 Т.Ж.
google domain [url=https://google.com/]domain[/url] domain [http://www.example.com link title]
NSProject
NSProjectМаусым 4, 2022, 3:49 Т.Ж.
Всё ещё разбираюсь с кешем. В следствии прочтения данной статьи. Я принял для себя решение сделать кеширование свойств менеджера модели LikeDislike. И так как установка evileg_core для меня не была возможна, ибо он писался…

Бізді әлеуметтік желілерде бақылаңыз