前言
弹球消砖块游戏想必大家都玩过,很简单的小游戏,通过移动挡板反弹下落的小球,然后撞击砖块将其消除。本文使用QML来简单实现这个小游戏。
效果图:
正文
代码目录结构如下:
首先是小球部分,逻辑比较麻烦一点,需要检查与砖块的碰撞以及与挡板的碰撞,还要更新小球的轨迹位置,代码如下:
Ball.qml
cpp
import QtQuick 2.12
Rectangle {
id: ball
width: 15
height: 15
radius: width / 2
color: "#FFFFFF"
border.color: "#DDDDDD"
border.width: 1
// 引用游戏区域和挡板
property var gameArea
property var paddle
// 球的速度和方向
property real speedX: 0
property real speedY: 0
property real baseSpeed: 3
// 信号:球丢失
signal ballLost()
// 重置球的位置和状态
function reset() {
x = gameArea.width / 2 - width / 2;
y = gameArea.height / 2;
speedX = 0;
speedY = 0;
}
// 开始移动球
function start() {
if (speedX === 0 && speedY === 0) {
// 随机初始方向,但确保向下
var angle = (Math.random() * Math.PI / 2) + Math.PI / 4; // 45-135度之间
speedX = Math.cos(angle) * baseSpeed;
speedY = Math.sin(angle) * baseSpeed;
}
}
// 检查与砖块的碰撞
function checkBrickCollision() {
for (var i = 0; i < gameArea.bricksRepeater.count; i++) {
var brick = gameArea.bricksRepeater.itemAt(i);
if (brick && !brick.destroyed) {
var brickPos = brick.mapToItem(gameArea, 0, 0);
if (x + width > brickPos.x && x < brickPos.x + brick.width &&
y + height > brickPos.y && y < brickPos.y + brick.height) {
// 确定碰撞方向并反弹
var centerX = x + width / 2;
var centerY = y + height / 2;
var brickCenterX = brickPos.x + brick.width / 2;
var brickCenterY = brickPos.y + brick.height / 2;
var dx = centerX - brickCenterX;
var dy = centerY - brickCenterY;
if (Math.abs(dx / brick.width) > Math.abs(dy / brick.height)) {
// 水平碰撞
speedX = -speedX;
} else {
// 垂直碰撞
speedY = -speedY;
}
// 击中砖块,立即消除当前碰撞的砖块
brick.hit();
return true;
}
}
}
return false;
}
// 检查与挡板的碰撞
function checkPaddleCollision() {
if (y + height >= paddle.y && y <= paddle.y + paddle.height &&
x + width >= paddle.x && x <= paddle.x + paddle.width) {
// 根据击中挡板的位置调整反弹角度
var paddleCenter = paddle.x + paddle.width / 2;
var ballCenter = x + width / 2;
var relativePosition = (ballCenter - paddleCenter) / (paddle.width / 2);
// 计算新的速度向量
var angle = relativePosition * (Math.PI / 3); // 最大±60度
var speed = Math.sqrt(speedX * speedX + speedY * speedY);
speedX = Math.sin(angle) * speed;
speedY = -Math.abs(Math.cos(angle) * speed); // 确保向上反弹
// 稍微增加速度
speedX *= 1.05;
speedY *= 1.05;
return true;
}
return false;
}
// 更新球的位置
Timer {
interval: 16 // 约60fps
running: gameArea.gameRunning
repeat: true
onTriggered: {
// 移动球
x += speedX;
y += speedY;
// 检查墙壁碰撞
if (x <= 0 || x + width >= gameArea.width) {
speedX = -speedX;
x = Math.max(0, Math.min(x, gameArea.width - width));
}
if (y <= 0) {
speedY = -speedY;
y = 0;
}
// 检查是否掉落
if (y + height >= gameArea.height - 20 && !checkPaddleCollision()) {
ballLost();
return;
}
// 检查砖块碰撞
checkBrickCollision();
// 检查挡板碰撞
checkPaddleCollision();
}
}
}
其次是砖块 Brick.qml
cpp
import QtQuick 2.12
Rectangle {
id: brick
radius: 3
// 砖块状态
property bool destroyed: false
property int colorIndex: 0
// 砖块颜色数组
readonly property var colors: [
"#FF5252", // 红色
"#FFAB40", // 橙色
"#FFEB3B", // 黄色
"#66BB6A", // 绿色
"#42A5F5" // 蓝色
]
// 砖块被销毁的信号
signal brickDestroyed()
// 设置砖块颜色
color: colors[colorIndex % colors.length]
border.color: Qt.darker(color, 1.2)
border.width: 1
visible: !destroyed
// 砖块被击中
function hit() {
if (!destroyed) {
destroyed = true;
visible = false;
brickDestroyed();
}
}
}
砖块击中后要销毁。
接着是挡板 Paddle.qml, 挡板可以通过键盘左右键进行移动,也可以直接使用鼠标进行左右拖动,代码如下:
cpp
import QtQuick 2.12
Rectangle {
id: paddle
width: 120
height: 20
radius: 10
color: "#2196F3"
border.color: "#1976D2"
border.width: 1
Component.onCompleted: {
// 初始化时设置挡板居中
x = (gameArea.width - width) / 2
}
// 引用游戏区域
property var gameArea
// 移动速度
property int speed: 15
property bool movingLeft: false
property bool movingRight: false
// 处理键盘按下事件
function handleKeyPress(key) {
if (key === Qt.Key_Left) {
movingLeft = true;
} else if (key === Qt.Key_Right) {
movingRight = true;
}
}
// 处理键盘释放事件
function handleKeyRelease(key) {
if (key === Qt.Key_Left) {
movingLeft = false;
} else if (key === Qt.Key_Right) {
movingRight = false;
}
}
// 鼠标控制
MouseArea {
anchors.fill: parent
drag.target: parent
drag.axis: Drag.XAxis
drag.minimumX: 0
drag.maximumX: gameArea.width - parent.width
}
// 定时器更新挡板位置
Timer {
interval: 16 // 约60fps
running: (movingLeft || movingRight) && gameArea.gameRunning
repeat: true
onTriggered: {
if (movingLeft) {
paddle.x = Math.max(0, paddle.x - speed);
}
if (movingRight) {
paddle.x = Math.min(gameArea.width - paddle.width, paddle.x + speed);
}
}
}
}
以上是核心控件的完整代码。