《用MATLAB玩转游戏开发:从零开始打造你的数字乐园》基础篇(2D图形交互)-俄罗斯方块:用旋转矩阵打造经典

《用MATLAB玩转游戏开发:从零开始打造你的数字乐园》基础篇(2D图形交互)-🎮 俄罗斯方块:用旋转矩阵打造经典 🧊

大家好!今天我将带大家用MATLAB实现经典的俄罗斯方块游戏。我们将从数学原理出发,通过旋转矩阵实现方块的变形,最终完成一个可玩的游戏版本。让我们一起进入这个充满趣味的编程之旅吧!✨

文章目录

  • [《用MATLAB玩转游戏开发:从零开始打造你的数字乐园》基础篇(2D图形交互)-🎮 俄罗斯方块:用旋转矩阵打造经典 🧊](#《用MATLAB玩转游戏开发:从零开始打造你的数字乐园》基础篇(2D图形交互)-🎮 俄罗斯方块:用旋转矩阵打造经典 🧊)
    • [1. 游戏设计思路 🧠](#1. 游戏设计思路 🧠)
      • [1.1 模块设计](#1.1 模块设计)
      • [1.2 流程设计](#1.2 流程设计)
        • [1.2.1 流程图说明](#1.2.1 流程图说明)
        • [1.2.2 关键路径说明](#1.2.2 关键路径说明)
    • [2. 旋转矩阵原理(重点) 🔄](#2. 旋转矩阵原理(重点) 🔄)
      • [2.1 旋转矩阵基础](#2.1 旋转矩阵基础)
      • [2.2 方块旋转实现](#2.2 方块旋转实现)
    • [3. 游戏架构设计 🏗️](#3. 游戏架构设计 🏗️)
    • [4. MATLAB实现详解 💻](#4. MATLAB实现详解 💻)
      • [4.1 初始化游戏](#4.1 初始化游戏)
      • [4.2 旋转函数实现](#4.2 旋转函数实现)
      • [4.3 碰撞检测](#4.3 碰撞检测)
    • [5. 完整代码 🎮](#5. 完整代码 🎮)
    • [6. 总结与扩展 🚀](#6. 总结与扩展 🚀)

🎉 正文开始前,先来看看最终效果图! 🎉

奈何我自己的水平有点菜🐣,只得了 900分......你们也来玩一局试试吧!看看自己能拿多少分💪

🔥 挑战一下,把你的得分打在评论区 🔥

看看谁的分数最高🏆,说不定你就是那个隐藏的大神哦!✨

(快来PK吧!😎)

1. 游戏设计思路 🧠

1.1 模块设计

俄罗斯方块的核心机制可以分解为以下几个部分:

  • 方块生成:7种基本形状(I, J, L, O, S, T, Z)
  • 方块旋转:通过旋转矩阵实现90°旋转
  • 方块移动:左右移动和加速下落
  • 碰撞检测:判断方块是否可以移动或旋转
  • 消行计分:当一行填满时消除并计分
  • 游戏结束:当方块堆叠到顶部时结束游戏

1.2 流程设计

以下是俄罗斯方块游戏的完整流程图设计,包含移动、碰撞检测、旋转处理等流程:
旋转处理 碰撞检测 是 否 左箭头 右箭头 下箭头 上箭头 空格 是 否 是 否 是 否 计算旋转后坐标 旋转方块 应用旋转矩阵 检查旋转合法性 检查边界 移动合法? 检查已有方块 返回检测结果 开始游戏 初始化游戏板 生成新方块 游戏结束? 游戏结束 绘制游戏界面 玩家输入 尝试左移 尝试右移 加速下落 快速下落 更新方块位置 固定方块 检查消除行 有完整行? 消除行并计分 自动下落计时结束? 尝试下落

1.2.1 流程图说明

1.2.1.1. 游戏初始化阶段

  • 初始化游戏板(20x10矩阵)
  • 生成第一个方块

1.2.1.2. 主游戏循环
绘制界面 处理输入 自动下落

1.2.1.3. 输入处理分支

  • 左右移动:修改x坐标
  • 旋转:应用旋转矩阵
  • 下落:修改y坐标

1.2.1.4. 关键判断节点

  • 移动/旋转合法性检查(碰撞检测)
  • 自动下落计时器
  • 行消除判断

1.2.1.5. 特殊处理流程

  • 旋转计算:应用旋转矩阵后必须通过碰撞检测
  • 快速下落:连续执行下落直到碰撞
1.2.2 关键路径说明

1.2.2.1. 方块移动流程

复制代码
玩家输入 → 计算新位置 → 碰撞检测 → 更新位置/固定方块

1.2.2.2. 旋转特殊处理

复制代码
旋转请求 → 计算旋转坐标 → 矩阵变换 → 合法性检查 → 更新状态

1.2.2.3. 自动下落逻辑

复制代码
计时结束 → 尝试下落 → 成功则更新/失败则固定

这个流程图完整展示了游戏的所有可能状态转换,特别是突出了旋转矩阵的应用位置(在旋转处理子流程中)。建议在阅读代码时对照此流程图理解游戏逻辑。

今天我们将重点讲解旋转矩阵的实现,这是游戏中最有趣的数学部分!🤓

2. 旋转矩阵原理(重点) 🔄

2.1 旋转矩阵基础

在二维空间中,旋转矩阵可以将任何点绕原点旋转θ角度。对于90°旋转(θ=π/2),旋转矩阵为:

R = [ 0 − 1 1 0 ] R = \begin{bmatrix} 0 & -1 \\ 1 & 0 \end{bmatrix} R=[01−10]

应用到点(x,y)上:
x ′ = 0 ⋅ x + ( − 1 ) ⋅ y = − y y ′ = 1 ⋅ x + 0 ⋅ y = x \begin{aligned} x' &= 0 \cdot x + (-1) \cdot y = -y \\ y' &= 1 \cdot x + 0 \cdot y = x \end{aligned} x′y′=0⋅x+(−1)⋅y=−y=1⋅x+0⋅y=x

2.2 方块旋转实现

俄罗斯方块中,每个方块由4个小方块(我们称为"方块块")组成。旋转时,我们需要:

  1. 找到方块的旋转中心(通常是中心块或靠近中心的位置)
  2. 将每个方块块相对于旋转中心应用旋转矩阵
  3. 确保旋转后的位置是合法的(不超出边界或与已有方块重叠)

💡 小技巧:我们可以将方块表示为相对坐标,这样旋转计算会更方便!

3. 游戏架构设计 🏗️

我们的MATLAB实现将采用以下结构:

  1. 主循环:控制游戏流程
  2. 游戏板:20行×10列的矩阵表示
  3. 当前方块:存储当前下落方块的信息
  4. 绘图函数:实时更新游戏界面
  5. 键盘回调:处理玩家输入

4. MATLAB实现详解 💻

4.1 初始化游戏

matlab 复制代码
function tetris()
    % 游戏参数
    boardWidth = 10;
    boardHeight = 20;
    blockSize = 25;  % 每个小方块的像素大小
    
    % 创建图形窗口
    fig = figure('Name','俄罗斯方块','NumberTitle','off',...
                 'KeyPressFcn',@keyPress,...
                 'Position',[200 200 boardWidth*blockSize+100 boardHeight*blockSize+100]);
    
    % 创建游戏板
    ax = axes('Parent',fig,'Position',[0.05 0.05 0.7 0.9]);
    axis equal; axis([0 boardWidth 0 boardHeight]);
    set(ax,'XTick',0:boardWidth,'YTick',0:boardHeight,'XColor','k','YColor','k');
    grid on; hold on;
    title('俄罗斯方块 - MATLAB版');
    
    % 初始化游戏板矩阵 (0=空, 1-7=不同颜色方块)
    board = zeros(boardHeight, boardWidth);
    
    % 方块形状定义 (相对坐标)
    shapes = {
        [0 0; 0 1; 0 2; 0 3],    % I
        [0 0; 0 1; 0 2; 1 0],     % J
        [0 0; 0 1; 0 2; 1 2],     % L
        [0 0; 0 1; 1 0; 1 1],     % O
        [0 0; 0 1; 1 1; 1 2],     % S
        [0 0; 0 1; 0 2; 1 1],     % T
        [0 0; 1 0; 1 1; 2 1]      % Z
    };
    
    % 方块颜色
    colors = [
        0 1 1;    % 青色 - I
        0 0 1;    % 蓝色 - J
        1 0.5 0;  % 橙色 - L
        1 1 0;    % 黄色 - O
        0 1 0;    % 绿色 - S
        1 0 1;    % 紫色 - T
        1 0 0     % 红色 - Z
    ];

4.2 旋转函数实现

这是最核心的部分!✨

matlab 复制代码
    % 旋转当前方块
    function rotatePiece()
        % 获取当前方块的旋转中心 (通常是第一个方块块)
        pivot = currentPiece(1,:);
        
        % 应用旋转矩阵
        rotated = zeros(size(currentPiece));
        for i = 1:size(currentPiece,1)
            % 相对于旋转中心的坐标
            relPos = currentPiece(i,:) - pivot;
            
            % 应用90度旋转矩阵
            newRelPos = [-relPos(2), relPos(1)];
            
            % 计算新位置
            rotated(i,:) = pivot + newRelPos;
        end
        
        % 检查旋转后是否合法
        if canMove(rotated, currentPos)
            currentPiece = rotated;
            drawBoard();
        end
    end

4.3 碰撞检测

matlab 复制代码
    % 检查移动/旋转是否合法
    function valid = canMove(piece, pos)
        valid = true;
        for i = 1:size(piece,1)
            % 计算实际位置
            x = pos(1) + piece(i,1);
            y = pos(2) + piece(i,2);
            
            % 检查边界
            if x < 1 || x > boardWidth || y < 1 || y > boardHeight
                valid = false;
                return;
            end
            
            % 检查是否与已有方块重叠
            if y <= boardHeight && board(y,x) ~= 0
                valid = false;
                return;
            end
        end
    end

5. 完整代码 🎮

以下是适配MATLAB 2016b的完整可运行代码:

matlab 复制代码
function tetris()
    % 游戏参数
    boardWidth = 10;
    boardHeight = 20;
    blockSize = 25;  % 每个小方块的像素大小
    
    % 创建图形窗口
    fig = figure('Name','俄罗斯方块','NumberTitle','off',...
                 'KeyPressFcn',@keyPress,...
                 'Position',[200 200 boardWidth*blockSize+100 boardHeight*blockSize+100]);
    
    % 创建游戏板
    ax = axes('Parent',fig,'Position',[0.05 0.05 0.7 0.9]);
    axis equal; axis([0 boardWidth 0 boardHeight]);
    set(ax,'XTick',0:boardWidth,'YTick',0:boardHeight,'XColor','k','YColor','k');
    grid on; hold on;
    title('俄罗斯方块 - MATLAB版');
    
    % 初始化游戏板矩阵 (0=空, 1-7=不同颜色方块)
    board = zeros(boardHeight, boardWidth);
    
    % 方块形状定义 (相对坐标)
    shapes = {
        [0 0; 0 1; 0 2; 0 3],    % I
        [0 0; 0 1; 0 2; 1 0],     % J
        [0 0; 0 1; 0 2; 1 2],     % L
        [0 0; 0 1; 1 0; 1 1],     % O
        [0 0; 0 1; 1 1; 1 2],     % S
        [0 0; 0 1; 0 2; 1 1],     % T
        [0 0; 1 0; 1 1; 2 1]      % Z
    };
    
    % 方块颜色
    colors = [
        0 1 1;    % 青色 - I
        0 0 1;    % 蓝色 - J
        1 0.5 0;  % 橙色 - L
        1 1 0;    % 黄色 - O
        0 1 0;    % 绿色 - S
        1 0 1;    % 紫色 - T
        1 0 0     % 红色 - Z
    ];
    
    % 游戏状态变量
    currentPiece = [];
    currentPos = [];
    currentColor = [];
    gameOver = false;
    score = 0;
    scoreText = uicontrol('Style','text','Position',[boardWidth*blockSize+20 300 80 30],...
                         'String',['分数: ' num2str(score)],...
                         'FontSize',12);
    
    % 开始新游戏
    newGame();
    
    % 主游戏循环
    while ~gameOver
        moveDown();
        pause(0.5);  % 控制下落速度
    end
    
    % 新游戏初始化
    function newGame()
        board = zeros(boardHeight, boardWidth);
        gameOver = false;
        score = 0;
        set(scoreText,'String',['分数: ' num2str(score)]);
        newPiece();
        drawBoard();
    end
    
    % 生成新方块
    function newPiece()
        shapeIdx = randi(7);
        currentPiece = shapes{shapeIdx};
        currentColor = colors(shapeIdx,:);
        currentPos = [floor(boardWidth/2), boardHeight];
        
        % 检查游戏是否结束
        if ~canMove(currentPiece, currentPos)
            gameOver = true;
            msgbox(['游戏结束! 最终分数: ' num2str(score)],'游戏结束');
        end
    end
    
    % 绘制游戏板
    function drawBoard()
        cla;  % 清除当前轴
        
        % 绘制已固定的方块
        for y = 1:boardHeight
            for x = 1:boardWidth
                if board(y,x) ~= 0
                    color = colors(board(y,x),:);
                    rectangle('Position',[x-1 y-1 1 1],'FaceColor',color,'EdgeColor','k');
                end
            end
        end
        
        % 绘制当前方块
        for i = 1:size(currentPiece,1)
            x = currentPos(1) + currentPiece(i,1);
            y = currentPos(2) + currentPiece(i,2);
            if y >= 1 && y <= boardHeight
                rectangle('Position',[x-1 y-1 1 1],'FaceColor',currentColor,'EdgeColor','k');
            end
        end
        
        drawnow;
    end
    
    % 旋转当前方块
    function rotatePiece()
        % 获取当前方块的旋转中心 (通常是第一个方块块)
        pivot = currentPiece(1,:);
        
        % 应用旋转矩阵
        rotated = zeros(size(currentPiece));
        for i = 1:size(currentPiece,1)
            % 相对于旋转中心的坐标
            relPos = currentPiece(i,:) - pivot;
            
            % 应用90度旋转矩阵
            newRelPos = [-relPos(2), relPos(1)];
            
            % 计算新位置
            rotated(i,:) = pivot + newRelPos;
        end
        
        % 检查旋转后是否合法
        if canMove(rotated, currentPos)
            currentPiece = rotated;
            drawBoard();
        end
    end
    
    % 检查移动/旋转是否合法
    function valid = canMove(piece, pos)
        valid = true;
        for i = 1:size(piece,1)
            % 计算实际位置
            x = pos(1) + piece(i,1);
            y = pos(2) + piece(i,2);
            
            % 检查边界
            if x < 1 || x > boardWidth || y < 1
                valid = false;
                return;
            end
            
            % 检查是否与已有方块重叠
            if y <= boardHeight && board(y,x) ~= 0
                valid = false;
                return;
            end
        end
    end
    
    % 移动方块
    function movePiece(dx, dy)
        newPos = currentPos + [dx, dy];
        if canMove(currentPiece, newPos)
            currentPos = newPos;
            drawBoard();
            return;
        end
        
        % 如果不能向下移动,固定方块
        if dy < 0
            fixPiece();
        end
    end
    
    % 快速下落
    function dropPiece()
        while canMove(currentPiece, currentPos + [0 -1])
            currentPos = currentPos + [0 -1];
        end
        fixPiece();
    end
    
    % 向下移动
    function moveDown()
        movePiece(0, -1);
    end
    
    % 固定方块到游戏板
    function fixPiece()
        for i = 1:size(currentPiece,1)
            x = currentPos(1) + currentPiece(i,1);
            y = currentPos(2) + currentPiece(i,2);
            
            if y >= 1 && y <= boardHeight
                % 确定颜色索引
                colorIdx = find(ismember(colors, currentColor, 'rows'));
                board(y,x) = colorIdx;
            end
        end
        
        % 检查是否有行可以消除
        checkLines();
        
        % 生成新方块
        newPiece();
        drawBoard();
    end
    
    % 检查并消除完整的行
    function checkLines()
        linesToClear = [];
        for y = 1:boardHeight
            if all(board(y,:) ~= 0)
                linesToClear = [linesToClear y];
            end
        end
        
        if ~isempty(linesToClear)
            % 消除行
            board(linesToClear,:) = [];
            
            % 添加新的空行
            newRows = zeros(length(linesToClear), boardWidth);
            board = [newRows; board];
            
            % 更新分数
            score = score + length(linesToClear) * 100;
            set(scoreText,'String',['分数: ' num2str(score)]);
        end
    end
    
    % 键盘回调函数
    function keyPress(~, event)
        if gameOver, return; end
        
        switch event.Key
            case 'leftarrow'
                movePiece(-1, 0);
            case 'rightarrow'
                movePiece(1, 0);
            case 'downarrow'
                moveDown();
            case 'uparrow'
                rotatePiece();
            case 'space'
                dropPiece();
        end
    end
end

6. 总结与扩展 🚀

恭喜你完成了MATLAB俄罗斯方块的实现!🎉 通过这个项目,我们学到了:

  1. 旋转矩阵在游戏开发中的应用
  2. 二维游戏的基本架构设计
  3. MATLAB图形界面和回调函数的用法

扩展思路

  • 添加音效和动画效果 🎵
  • 实现"下一个方块"预览功能 👀
  • 添加难度随分数增加而提高的机制 📈
  • 保存最高分记录 🏆

希望这篇教程对你有所帮助!如果有任何问题或建议,欢迎留言讨论。Happy coding! 💻😊

相关推荐
oioihoii1 小时前
C++23 新特性:为 std::pair 的转发构造函数添加默认实参
算法·c++23
智者知已应修善业1 小时前
【验证哥德巴赫猜想(奇数)】2021-11-19 15:54
c语言·c++·经验分享·笔记·算法
-qOVOp-2 小时前
zst-2001 历年真题 设计模式
java·算法·设计模式
evolution_language2 小时前
LintCode第68题-二叉树的前序遍历,第67题-二叉树的后序遍历
数据结构·算法·新手必刷编程50题
yxc_inspire3 小时前
C++STL在算法竞赛中的应用详解
c++·算法·stl
James. 常德 student3 小时前
leetcode-hot-100(哈希)
算法·leetcode·哈希算法
金融小师妹3 小时前
量化解析美英协议的非对称冲击:多因子模型与波动率曲面重构
大数据·人工智能·算法
是店小二呀3 小时前
【算法-哈希表】常见算法题的哈希表套路拆解
数据结构·c++·算法·散列表
绿皮的猪猪侠4 小时前
搜索与图论
算法·深度优先·图论