Qt QML实现弹球消砖块小游戏

前言

弹球消砖块游戏想必大家都玩过,很简单的小游戏,通过移动挡板反弹下落的小球,然后撞击砖块将其消除。本文使用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);
            }
        }
    }
}

以上是核心控件的完整代码。


本文Demo下载

相关推荐
用户805533698033 天前
不止三件套:QObject 属性系统全关键字与运行时反射!
c++·qt
xcyxiner3 天前
DicomViewer (vcpkg Windows和ubuntu编译)7
qt
Quz8 天前
QML Hello World 入门示例
qt
xcyxiner11 天前
DicomViewer (dcmtk读取dcm文件)5
qt
xcyxiner12 天前
DicomViewer (后台线程处理文件)4
qt
xcyxiner12 天前
DicomViewer (添加模型类)3
qt
xcyxiner13 天前
DicomViewer (目录调整) 2
qt
xcyxiner13 天前
dcmtk vtk vtk-dicom(gdcm) 编译(debug) v2
qt
桥田智能15 天前
桥田智能 QT-650S:面向白车身焊装的 800kg 重载快换解决方案
开发语言·qt·系统架构
森G15 天前
75、服务器源码解析---------云视频服务项目
linux·服务器·网络·c++·qt