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

发布于:2025-05-10 ⋅ 阅读:(9) ⋅ 点赞:(0)

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

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

在这里插入图片描述
🎉 正文开始前,先来看看最终效果图! 🎉
在这里插入图片描述
奈何我自己的水平有点菜🐣,只得了 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=[0110]

应用到点(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} xy=0x+(1)y=y=1x+0y=x

2.2 方块旋转实现

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

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

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

3. 游戏架构设计 🏗️

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

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

4. MATLAB实现详解 💻

4.1 初始化游戏

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 旋转函数实现

这是最核心的部分!✨

    % 旋转当前方块
    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 碰撞检测

    % 检查移动/旋转是否合法
    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的完整可运行代码:

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! 💻😊


网站公告

今日签到

点亮在社区的每一天
去签到