Nach den ersten Erfahrungen beim Schreiben eines Spiels in Qt für Android möchte ich diese Erfahrung teilen und vorschlagen, gemeinsam ein einfaches Spiel im „Kill the Mole“-Stil zu schreiben. Dies ist ein Spiel, bei dem Sie Zeit haben müssen, um die Maulwürfe zu treffen, die aus ihren Löchern klettern, aber wenn man bedenkt, dass dies ein vereinfachtes Spiel sein wird, verwenden wir anstelle von Maulwürfen runde Ziele, die auf dem Spielfeld erscheinen. Als Spielfeld wird ein Raster von 6 mal 6 Zellen verwendet. Einige spezielle Objekte wie GridLayout werden jedoch nicht zum Erstellen des Felds verwendet. Auf dem Spielfeld wird das Raster aus der Anzahl der Reihen, Spalten und der Seitenlänge der quadratischen Zelle gebildet. Daten zur Zellfülle werden in einem zweidimensionalen Array gespeichert, das in der Javascript-Komponente gebildet wird (vergessen Sie nicht, dass QML eine deklarative JSON-ähnliche Sprache mit Javascript-Unterstützung ist).
Projektstruktur
Das Projekt besteht aus folgenden Dateien:
- TargetGame.pro - Projektprofil;
- deployment.pri - Projektdatei erstellen und bereitstellen;
- main.cpp - Datei mit der Hauptfunktion des Projekts;
- main.qml - die Hauptdatei der QML-Schicht mit dem Objekt des Hauptanwendungsfensters;
- Target.qml - QML-Datei mit einer Beschreibung des Zielobjekts;
- Target.png - das Erscheinungsbild des Ziels ist ein PNG-Bild;
- GameArea.qml - Spielarena mit einem Gitter zum Platzieren von Zielen;
- logic.js - Datei mit Javascript-Spiellogik.
Lassen Sie uns beim Erstellen des Projekts nur den Inhalt der Dateien untersuchen, die sich vom Standardinhalt unterscheiden.
main.qml
Das Hauptanwendungsfenster enthält ein Objekt vom Typ GameArea, das in GameArea.qml. beschrieben wird. Da die Breite und Höhe der Spielarena abhängig von der Größe des Rasters und der Rasterzellen automatisch berechnet werden, werden wir einfach platzieren die Spielarena in der Mitte des Anwendungsfensters.
Eine Besonderheit dieser Datei ist die Aufnahme der Datei logic.js als Typ Logic , die auf die Spiellogik reagiert und in dieser Datei nach dem Erstellen des Objekts das Hauptfenster der Anwendung enthält der Spielarena übergeben wir einen Zeiger auf die Spielarena an den Kern der Spiellogik, um uns den aktuellen Zustand des Spielfeldes zu merken und die Erstinitialisierung des Spielfeldes sowie des Spielfeldarrays durchzuführen , die dem Inhalt der Rasterzellen entsprechen. Dazu wird die Funktion newGameState(), aufgerufen und ihr die ID der Spielarena als Argument übergeben. In diesem Tutorial verfolgen wir nicht den Status des Spiels, d. h. ob das Spiel läuft oder angehalten wurde, sodass der Arena-Spiel-Timer sofort nach dem Start der Anwendung gestartet wird.
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
Da der logische Kern in Javascript in allen Aspekten des Spiels vorkommt, fügen wir diese Datei auch hier ein.
Die Spielarena ist ein Rechteck mit den Eigenschaften vordefinierter Zeilen und Spalten sowie der Größe einer Seite der Zelle. Abhängig von diesen Eigenschaften werden Breite und Höhe der Spielfläche berechnet. Auch in der Spielarena gibt es einen Timer, bei dessen Betätigung Ziele erstellt werden.
Die Erstellung von Zielen wird begleitet von der Überprüfung der Verfügbarkeit einer zufällig ausgewählten Zelle. Die Zellenauswahl erfolgt durch Auswahl einer zufälligen Zeile und Spalte, indem eine zufällige Ganzzahl im ausgewählten Bereich vom Minimum bis zum Maximum einschließlich mithilfe der Funktion getRandomRound(min, max) erhalten wird, die auch in beschrieben wird. logic.js.
Die Funktion checkEmptyField(row, column) prüft, ob ein Objekt in der angegebenen Zelle vorhanden ist, und wenn die Zelle leer ist, wird mithilfe der Funktion createTarget(parent, row, column) ein Ziel erstellt.**
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
Bevor wir den Inhalt des logischen Javascript-Kerns des Spiels untersuchen, wollen wir die Beschreibung des Zielobjekts untersuchen.
Die Zielverhaltenslogik ist wie folgt. Einmal erstellt, sollte das Ziel nach einer zufälligen Zeitspanne verschwinden. Und nicht einfach verschwinden, das heißt, indem sie den Grad der Transparenz ändern, sondern sich selbst zerstören. Dazu wird die Methode destroy(), verwendet, die auf das Root-Objekt mit der ID: root angewendet wird. Sie können auch die Funktion destroyTarget(row, column) im Javascript-Kern des Spiels sehen. Diese Funktion wird verwendet, um eine Zelle eines zweidimensionalen Gitterinformationsarrays zu löschen.
Da die Größe der Zelle 64 Pixel und die Größe des Ziels 56 Pixel beträgt, bestimmen wir mit einfachen Berechnungen die Position des Ziels auf dem Spielfeld, abhängig von der Spalte und Zeile, die es einnehmen soll.
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
Diese Datei enthält eine Deklaration .pragma-Bibliothek *. *
.pragma library
Dies geschieht aufgrund der Notwendigkeit, auf den Status des Spiels ( var gameState ) von überall zugreifen zu können, wo diese Datei in der QML-Schicht enthalten ist. Tatsächlich werden Variablen zu gemeinsam genutzten Ressourcen für verschiedene Objekte im Spiel. Und die wichtigste Ressource ist gameState (auch bekannt als game arena GameArea ). Tatsache ist, dass, wenn wir direkt per id von verschiedenen Objekten, die statisch in der Spielarena deklariert sind, oder in einer Datei, in der die Arena selbst deklariert ist, auf die Spielarena zugreifen können, dann in dynamischen Ressourcen, wie in * Ziel .qml , diese Methode funktioniert bei uns nicht. Dazu wird gameState aus logic.js * verwendet, mit dem Sie den Zustand der Spielarena unabhängig davon überprüfen können, ob das Objekt statisch deklariert ist (mit Zustand meine ich, ob das Spiel läuft oder pausiert, was in den folgenden Artikeln berücksichtigt wird).
Wenden wir uns nun dem Inhalt der Datei logic.js zu. Hier gibt es zwei höchst interessante Punkte:
- Dies ist die Erstellung eines zweidimensionalen Arrays in Javascript, das als Informationsmodell über die im Raster enthaltenen Objekte und als Überprüfung auf die Existenz eines Objekts in einer Zelle dient.
- Dies ist die Erstellung dynamischer Ziele.
Wenn mit dem ersten aus dem Code alles klar genug ist, werde ich ein paar Worte zum zweiten sagen. Um ein Ziel dynamisch zu erstellen, wird in diesem Fall die Blank-Komponente verwendet, die aus der Datei Target.qml. erzeugt wird, außerdem wird nur ein solches Blank benötigt, selbst für viele solcher Elemente des gleichen Typs das Spiel.
var targetComponent = Qt.createComponent("Target.qml");
Und die Erstellung des Objekts selbst in der Spielarena wird in der folgenden Funktion unten ausgeführt.
function createTarget(parent, row, column) { var target = targetComponent.createObject(parent, {"row": row, "col": column}) gameField[row][column] = target; }
In unserem Fall reicht es aus, ein Objekt zu erstellen, indem die createObject-Methode auf den Zielkomponenten-Rohling angewendet wird, wobei das übergeordnete Objekt angegeben wird, d. h. das Objekt, in dem sich das Ziel befinden wird (in diesem Fall ist es die Spielarena ) und listet die Parameter auf, die beim Erstellen des Objekts festgelegt werden müssen, in diesem Fall In diesem Fall ist dies die Zeile und Spalte, in der sich das Ziel befinden wird.
Vollständige Dateiliste
// Объявляем, как библиотеку, чтобы иметь доступ // к разделяемым ресурсам, например состоянию игры .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; } }
Insgesamt
Als Ergebnis erhalten wir ein Programm mit einem Spielfeld von 6 x 6, in dem beim Start verschwindende Ziele generiert werden.
Quelle herunterladen von QML-Spielbeispiel von GitHub