《用MATLAB玩转游戏开发:从零开始打造你的数字乐园》基础篇(2D图形交互)-俄罗斯方块:用旋转矩阵打造经典
《用MATLAB玩转游戏开发:从零开始打造你的数字乐园》基础篇(2D图形交互)-🎮 俄罗斯方块:用旋转矩阵打造经典 🧊
大家好!今天我将带大家用MATLAB实现经典的俄罗斯方块游戏。我们将从数学原理出发,通过旋转矩阵实现方块的变形,最终完成一个可玩的游戏版本。让我们一起进入这个充满趣味的编程之旅吧!✨
文章目录
- 《用MATLAB玩转游戏开发:从零开始打造你的数字乐园》基础篇(2D图形交互)-🎮 俄罗斯方块:用旋转矩阵打造经典 🧊
- 1. 游戏设计思路 🧠
- 1.1 模块设计
- 1.2 流程设计
- 1.2.1 流程图说明
- 1.2.2 关键路径说明
- 2. 旋转矩阵原理(重点) 🔄
- 2.1 旋转矩阵基础
- 2.2 方块旋转实现
- 3. 游戏架构设计 🏗️
- 4. MATLAB实现详解 💻
- 4.1 初始化游戏
- 4.2 旋转函数实现
- 4.3 碰撞检测
- 5. 完整代码 🎮
- 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个小方块(我们称为"方块块")组成。旋转时,我们需要:
- 找到方块的旋转中心(通常是中心块或靠近中心的位置)
- 将每个方块块相对于旋转中心应用旋转矩阵
- 确保旋转后的位置是合法的(不超出边界或与已有方块重叠)
💡 小技巧:我们可以将方块表示为相对坐标,这样旋转计算会更方便!
3. 游戏架构设计 🏗️
我们的MATLAB实现将采用以下结构:
- 主循环:控制游戏流程
- 游戏板:20行×10列的矩阵表示
- 当前方块:存储当前下落方块的信息
- 绘图函数:实时更新游戏界面
- 键盘回调:处理玩家输入
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; % 青色 - I0 0 1; % 蓝色 - J1 0.5 0; % 橙色 - L1 1 0; % 黄色 - O0 1 0; % 绿色 - S1 0 1; % 紫色 - T1 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();endend
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 > boardHeightvalid = false;return;end% 检查是否与已有方块重叠if y <= boardHeight && board(y,x) ~= 0valid = false;return;endendend
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; % 青色 - I0 0 1; % 蓝色 - J1 0.5 0; % 橙色 - L1 1 0; % 黄色 - O0 1 0; % 绿色 - S1 0 1; % 紫色 - T1 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 ~gameOvermoveDown();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)],'游戏结束');endend% 绘制游戏板function drawBoard()cla; % 清除当前轴% 绘制已固定的方块for y = 1:boardHeightfor x = 1:boardWidthif board(y,x) ~= 0color = colors(board(y,x),:);rectangle('Position',[x-1 y-1 1 1],'FaceColor',color,'EdgeColor','k');endendend% 绘制当前方块for i = 1:size(currentPiece,1)x = currentPos(1) + currentPiece(i,1);y = currentPos(2) + currentPiece(i,2);if y >= 1 && y <= boardHeightrectangle('Position',[x-1 y-1 1 1],'FaceColor',currentColor,'EdgeColor','k');endenddrawnow;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();endend% 检查移动/旋转是否合法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 < 1valid = false;return;end% 检查是否与已有方块重叠if y <= boardHeight && board(y,x) ~= 0valid = false;return;endendend% 移动方块function movePiece(dx, dy)newPos = currentPos + [dx, dy];if canMove(currentPiece, newPos)currentPos = newPos;drawBoard();return;end% 如果不能向下移动,固定方块if dy < 0fixPiece();endend% 快速下落function dropPiece()while canMove(currentPiece, currentPos + [0 -1])currentPos = currentPos + [0 -1];endfixPiece();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;endend% 检查是否有行可以消除checkLines();% 生成新方块newPiece();drawBoard();end% 检查并消除完整的行function checkLines()linesToClear = [];for y = 1:boardHeightif all(board(y,:) ~= 0)linesToClear = [linesToClear y];endendif ~isempty(linesToClear)% 消除行board(linesToClear,:) = [];% 添加新的空行newRows = zeros(length(linesToClear), boardWidth);board = [newRows; board];% 更新分数score = score + length(linesToClear) * 100;set(scoreText,'String',['分数: ' num2str(score)]);endend% 键盘回调函数function keyPress(~, event)if gameOver, return; endswitch event.Keycase 'leftarrow'movePiece(-1, 0);case 'rightarrow'movePiece(1, 0);case 'downarrow'moveDown();case 'uparrow'rotatePiece();case 'space'dropPiece();endend
end
6. 总结与扩展 🚀
恭喜你完成了MATLAB俄罗斯方块的实现!🎉 通过这个项目,我们学到了:
- 旋转矩阵在游戏开发中的应用
- 二维游戏的基本架构设计
- MATLAB图形界面和回调函数的用法
扩展思路:
- 添加音效和动画效果 🎵
- 实现"下一个方块"预览功能 👀
- 添加难度随分数增加而提高的机制 📈
- 保存最高分记录 🏆
希望这篇教程对你有所帮助!如果有任何问题或建议,欢迎留言讨论。Happy coding! 💻😊