Совсем недавно обновился Qt, после обновления вспомнил о прекрасном инструменте QML. Соберем тетрис на нем, вспомним
import QtQuick 2.15
import QtQuick.Window 2.15
import QtQuick.Controls 2.15
Window {
width: 400
height: 800
visible: true
title: "Qt Tetris"
property int blockSize: 30
property int cols: 10
property int rows: 20
property var board: []
property var currentPiece
property int score: 0
property bool gameOver: false
Component.onCompleted: {
initBoard()
spawnPiece()
}
function initBoard() {
board = new Array(cols * rows)
board.fill(0)
}
function spawnPiece() {
const pieces = [
[[1,1,1,1]], // I
[[1,1],[1,1]], // O
[[1,1,1],[0,1,0]], // T
[[1,1,1],[1,0,0]], // L
[[1,1,1],[0,0,1]], // J
[[1,1,0],[0,1,1]], // S
[[0,1,1],[1,1,0]] // Z
]
currentPiece = {
shape: pieces[Math.floor(Math.random() * pieces.length)],
x: cols/2 - 1,
y: 0,
color: Qt.rgba(Math.random(), Math.random(), Math.random(), 1)
}
if (checkCollision(currentPiece.x, currentPiece.y, currentPiece.shape)) {
gameOver = true
}
}
function checkCollision(x, y, shape) {
for (let i = 0; i < shape.length; i++) {
for (let j = 0; j < shape[i].length; j++) {
if (shape[i][j]) {
let newX = x + j
let newY = y + i
if (newX < 0 || newX >= cols || newY >= rows) return true
if (newY >= 0 && board[newY * cols + newX]) return true
}
}
}
return false
}
function mergePiece() {
for (let i = 0; i < currentPiece.shape.length; i++) {
for (let j = 0; j < currentPiece.shape[i].length; j++) {
if (currentPiece.shape[i][j]) {
let x = currentPiece.x + j
let y = currentPiece.y + i
if (y >= 0) board[y * cols + x] = currentPiece.color
}
}
}
checkLines()
}
function checkLines() {
for (let row = rows - 1; row >= 0; row--) {
let full = true
for (let col = 0; col < cols; col++) {
if (!board[row * cols + col]) {
full = false
break
}
}
if (full) {
score += 100
board.splice(row * cols, cols)
board.unshift(...new Array(cols).fill(0))
row++
}
}
}
function moveLeft() {
if (!gameOver && !checkCollision(currentPiece.x - 1, currentPiece.y, currentPiece.shape)) {
currentPiece.x--
}
}
function moveRight() {
if (!gameOver && !checkCollision(currentPiece.x + 1, currentPiece.y, currentPiece.shape)) {
currentPiece.x++
}
}
function rotate() {
if (gameOver) return
let newShape = currentPiece.shape[0].map((_, i) =>
currentPiece.shape.map(row => row[i]).reverse()
)
if (!checkCollision(currentPiece.x, currentPiece.y, newShape)) {
currentPiece.shape = newShape
}
}
function moveDown() {
if (!gameOver && !checkCollision(currentPiece.x, currentPiece.y + 1, currentPiece.shape)) {
currentPiece.y++
} else {
mergePiece()
spawnPiece()
}
}
Keys.onPressed: {
switch(event.key) {
case Qt.Key_Left: moveLeft(); break
case Qt.Key_Right: moveRight(); break
case Qt.Key_Up: rotate(); break
case Qt.Key_Down: moveDown(); break
case Qt.Key_Space: while(!gameOver) moveDown(); break
}
}
Rectangle {
anchors.fill: parent
focus: true
Grid {
columns: cols
rows: rows
anchors.centerIn: parent
Repeater {
model: cols * rows
Rectangle {
width: blockSize
height: blockSize
border.color: "#333"
color: {
// Текущая фигурка
let x = index % cols
let y = Math.floor(index / cols)
for (let i = 0; i < currentPiece?.shape?.length; i++) {
for (let j = 0; j < currentPiece.shape[i].length; j++) {
if (currentPiece.shape[i][j] &&
x === currentPiece.x + j &&
y === currentPiece.y + i) {
return currentPiece.color
}
}
}
// Ячейки
return board[index] || "white"
}
}
}
}
Timer {
interval: 1000
running: !gameOver
repeat: true
onTriggered: moveDown()
}
Text {
anchors.top: parent.top
anchors.right: parent.right
text: "Очки: " + score
font.pixelSize: 20
color: "black"
}
Rectangle {
anchors.centerIn: parent
width: 200
height: 100
color: "lightgray"
visible: gameOver
Column {
anchors.centerIn: parent
spacing: 10
Text {
text: "Вот и все!"
font.pixelSize: 24
anchors.horizontalCenter: parent.horizontalCenter
}
Button {
text: "New Game"
onClicked: {
gameOver = false
score = 0
initBoard()
spawnPiece()
}
}
}
}
}
}