当前位置: 首页 > article >正文

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

前言

弹球消砖块游戏想必大家都玩过,很简单的小游戏,通过移动挡板反弹下落的小球,然后撞击砖块将其消除。本文使用QML来简单实现这个小游戏。
效果图:

在这里插入图片描述
在这里插入图片描述

正文

代码目录结构如下:
在这里插入图片描述
首先是小球部分,逻辑比较麻烦一点,需要检查与砖块的碰撞以及与挡板的碰撞,还要更新小球的轨迹位置,代码如下:
Ball.qml

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

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, 挡板可以通过键盘左右键进行移动,也可以直接使用鼠标进行左右拖动,代码如下:

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下载


http://www.kler.cn/a/585715.html

相关文章:

  • WPF中查看PDF文件之MoonPdfLib类库
  • c++ auto关键字皮毛
  • ACL初级总结
  • 人工智能时代教育行业该如何转型:迎接挑战,塑造未来教育生态
  • 揭秘AI:机器学习与深度学习的奥秘
  • 浅谈数据分析及数据思维
  • SSM企业台账管理平台
  • NodeJS接口
  • DB-GPT-0.7版本win11安装,最新版本,安装方式变更了
  • Microsoft Excel 2024 LTSC mac v16.95 表格处理软件 支持M、Intel芯片
  • Java入职篇(1)——心态篇
  • jvm汇总
  • 基于相量测量单元(PMU)的电力系统故障分析MATLAB仿真
  • linux常用基本指令汇总
  • 散货拼柜业务痛点有哪些?货代公司如何通过散拼系统提高效率?
  • ABC 375
  • Mybaties批量操作
  • SAP BC 记一次 DBCO 链接ORACLE DBCC 连接测试突然失败的问题
  • Python中将Markdown文件转换为Word
  • 【Linux】从互斥原理到C++ RAII封装实践