《用MATLAB玩转游戏开发:从零开始打造你的数字乐园》基础篇(2D图形交互)-🐍 贪吃蛇的百变玩法:从命令行到AI对战 🎮
欢迎来到这篇MATLAB贪吃蛇编程全攻略!本文将带你从零开始,一步步实现一个功能丰富的贪吃蛇游戏,最终进阶到AI自动对战。准备好你的MATLAB环境(2016b版本),让我们开始这段有趣的编程之旅吧!🚀
文章目录 📚
- 《用MATLAB玩转游戏开发:从零开始打造你的数字乐园》基础篇(2D图形交互)-🐍 贪吃蛇的百变玩法:从命令行到AI对战 🎮
- 1. 基础贪吃蛇实现 🏗️
- 1.1 设计思路
- 1.2 整体实现流程
- 1.2.1 流程图说明
- 1.2.2 关键路径说明
- 1.3 实现步骤
- 1.3.1 初始化游戏参数
- 1.3.2 主游戏循环
- 1.3.3 关键函数实现
- 2. 图形界面美化 🎨
- 2.1 创建图形窗口
- 2.2 改进绘制函数
- 3. 游戏功能扩展 ✨
- 3.1 障碍物模式
- 3.2 特殊食物效果
- 4. AI对战实现 🤖
- 4.1 路径寻找算法
- 4.2 启发式函数
- 4.3 AI决策函数
- 🎉 完整代码 🎉
- 结语 🏁
1. 基础贪吃蛇实现 🏗️
1.1 设计思路
贪吃蛇的基本原理很简单:控制蛇头移动,吃到食物后身体变长,碰到边界或自身游戏结束。我们需要考虑以下几个核心组件:
- 游戏区域:二维矩阵表示
- 蛇的表示:用坐标序列存储蛇身
- 食物生成:随机位置出现
- 游戏循环:处理输入、更新状态、渲染画面
1.2 整体实现流程
以下是贪吃蛇游戏的完整流程图设计,包含游戏初始化、主循环、用户输入、AI决策、碰撞检测等关键模块:
1.2.1 流程图说明
-
初始化阶段:
- 设置游戏区域(20x20网格)
- 创建长度为3的初始蛇(水平放置)
- 随机生成第一个食物(含不同类型)
- 初始化分数(0)和游戏速度(0.1秒/帧)
-
主游戏循环:
- 检测当前控制模式(AI/手动)
- AI模式使用简化A*算法寻路
- 手动模式响应键盘方向键
-
移动处理:
-
碰撞检测系统:
-
食物系统:
-
AI决策逻辑:
1.2.2 关键路径说明
-
正常游戏流程:
开始 → 初始化 → 主循环 → 输入处理 → 移动 → 碰撞检测 → 食物检测 → 画面更新 → 主循环
-
游戏结束条件:
碰撞检测 → 边界/自身/障碍物碰撞 → 结束画面 → 退出
-
AI决策流程:
AI模式激活 → 路径计算 → 存在路径 → 沿路径移动↘ 无路径 → 避险移动
小总结,以上所有流程图完整呈现了以下几部分内容,请再吸收一下哦:
- 游戏状态转换
- 用户输入与AI决策的并行处理
- 碰撞检测的三重判断
- 食物系统的概率分支
- 蛇移动的核心逻辑
建议读者在阅读后面的代码时可以对照此流程图咀嚼代码,便于清晰理解各模块的交互关系。
1.3 实现步骤
1.3.1 初始化游戏参数
% 游戏区域大小
width = 20;
height = 20;% 初始化蛇 (初始长度为3,水平放置)
snake = [10,10; 10,9; 10,8]; % 初始方向 (1=上, 2=右, 3=下, 4=左)
direction = 2; % 生成第一个食物
food = generateFood(width, height, snake);% 游戏状态
gameOver = false;
score = 0;
1.3.2 主游戏循环
while ~gameOver% 处理键盘输入[direction, exitFlag] = processInput(direction);if exitFlagbreak;end% 移动蛇[snake, ateFood] = moveSnake(snake, direction, food, width, height);% 检查游戏结束条件gameOver = checkCollision(snake, width, height);% 如果吃到食物if ateFoodscore = score + 10;food = generateFood(width, height, snake);end% 绘制游戏画面drawGame(snake, food, width, height, score);% 控制游戏速度pause(0.1);
end
1.3.3 关键函数实现
食物生成函数:
function food = generateFood(width, height, snake)% 生成不在蛇身上的随机位置while truefood = [randi(height), randi(width)];if ~ismember(food, snake, 'rows')break;endend
end
移动蛇函数:
function [newSnake, ateFood] = moveSnake(snake, direction, food, width, height)% 计算新头部位置head = snake(1,:);switch directioncase 1 % 上newHead = [head(1)-1, head(2)];case 2 % 右newHead = [head(1), head(2)+1];case 3 % 下newHead = [head(1)+1, head(2)];case 4 % 左newHead = [head(1), head(2)-1];end% 检查是否吃到食物ateFood = isequal(newHead, food);% 更新蛇身if ateFoodnewSnake = [newHead; snake]; % 吃到食物,不删除尾部elsenewSnake = [newHead; snake(1:end-1,:)]; % 没吃到,删除尾部end
end
2. 图形界面美化 🎨
命令行版本虽然功能完整,但视觉效果欠佳。让我们用MATLAB的图形功能来美化它!
2.1 创建图形窗口
function initGUI()figure('Name','MATLAB贪吃蛇','NumberTitle','off',...'MenuBar','none','Color',[0.2 0.2 0.2],...'KeyPressFcn',@keyPressHandler);axis equal; axis off; hold on;% 设置游戏区域set(gca,'XLim',[0.5 width+0.5],'YLim',[0.5 height+0.5],...'XTick',[],'YTick',[],'Color',[0.1 0.1 0.1]);% 分数显示scoreText = text(width+2, height/2, ['分数: 0'],...'Color','w','FontSize',12);
end
2.2 改进绘制函数
function drawGame(snake, food, width, height, score)cla; % 清除当前轴% 绘制网格for i = 1:widthfor j = 1:heightrectangle('Position',[i-0.5,j-0.5,1,1],...'EdgeColor',[0.3 0.3 0.3],...'FaceColor',[0.15 0.15 0.15]);endend% 绘制蛇for i = 1:size(snake,1)pos = snake(i,:);rectangle('Position',[pos(2)-0.5,pos(1)-0.5,1,1],...'Curvature',[0.3 0.3],...'FaceColor',[0 0.8 0],...'EdgeColor','none');end% 绘制蛇头 (不同颜色)head = snake(1,:);rectangle('Position',[head(2)-0.5,head(1)-0.5,1,1],...'Curvature',[0.3 0.3],...'FaceColor',[0 1 0],...'EdgeColor','none');% 绘制食物rectangle('Position',[food(2)-0.5,food(1)-0.5,1,1],...'Curvature',[1 1],...'FaceColor',[1 0 0],...'EdgeColor','none');% 更新分数scoreText.String = ['分数: ' num2str(score)];drawnow;
end
3. 游戏功能扩展 ✨
让我们为游戏添加更多有趣的功能!
3.1 障碍物模式
% 初始化障碍物
obstacles = [5,5; 5,6; 5,7; 15,15; 15,16; 15,17];% 修改碰撞检测函数
function collision = checkCollision(snake, width, height, obstacles)head = snake(1,:);% 检查边界碰撞if head(1) < 1 || head(1) > height || head(2) < 1 || head(2) > widthcollision = true;return;end% 检查自身碰撞if size(snake,1) > 1 && ismember(head, snake(2:end,:), 'rows')collision = true;return;end% 检查障碍物碰撞if exist('obstacles','var') && ~isempty(obstacles) && ismember(head, obstacles, 'rows')collision = true;return;endcollision = false;
end
3.2 特殊食物效果
% 定义食物类型
foodTypes = struct(...'normal', struct('color',[1 0 0], 'score',10, 'effect','none'),...'golden', struct('color',[1 1 0], 'score',50, 'effect','speedUp'),...'toxic', struct('color',[0 1 0], 'score',-20, 'effect','shrink')...
);% 修改食物生成函数
function [food, foodType] = generateFood(width, height, snake)% 80%普通食物,15%黄金食物,5%有毒食物r = rand();if r < 0.8foodType = 'normal';elseif r < 0.95foodType = 'golden';elsefoodType = 'toxic';end% 生成位置while truefood = [randi(height), randi(width)];if ~ismember(food, snake, 'rows')break;endend
end
4. AI对战实现 🤖
现在让我们实现一个简单的AI自动玩贪吃蛇!
4.1 路径寻找算法
我们将使用A*算法来寻找蛇头到食物的最短路径。
function path = findPath(snake, food, width, height, obstacles)% 实现A*算法寻找路径start = snake(1,:);goal = food;% 初始化开放集和关闭集openSet = start;closedSet = [];% 来自节点的路径cameFrom = containers.Map();% gScore[node] = 从起点到node的实际距离gScore = containers.Map(mat2str(start), 0);% fScore[node] = gScore[node] + h(node) (估计总距离)fScore = containers.Map(mat2str(start), heuristic(start, goal));while ~isempty(openSet)% 在开放集中找到fScore最小的节点[~, currentIdx] = min(cell2mat(values(fScore, mat2str(openSet))));current = openSet(currentIdx,:);% 如果到达目标if isequal(current, goal)path = reconstructPath(cameFrom, current);return;end% 从开放集移动到关闭集openSet(currentIdx,:) = [];closedSet = [closedSet; current];% 检查所有邻居neighbors = getNeighbors(current, width, height, snake, obstacles);for i = 1:size(neighbors,1)neighbor = neighbors(i,:);% 如果邻居在关闭集中,跳过if ismember(neighbor, closedSet, 'rows')continue;end% 计算从起点到邻居的临时gScoretempGScore = gScore(mat2str(current)) + 1;% 如果邻居不在开放集中,或者找到更好的路径if ~ismember(neighbor, openSet, 'rows') || ...tempGScore < gScore(mat2str(neighbor))cameFrom(mat2str(neighbor)) = current;gScore(mat2str(neighbor)) = tempGScore;fScore(mat2str(neighbor)) = tempGScore + heuristic(neighbor, goal);if ~ismember(neighbor, openSet, 'rows')openSet = [openSet; neighbor];endendendend% 开放集为空但未找到路径path = [];
end
4.2 启发式函数
function h = heuristic(pos, goal)% 曼哈顿距离h = abs(pos(1)-goal(1)) + abs(pos(2)-goal(2));
end
4.3 AI决策函数
function direction = decideAIMove(snake, food, width, height, obstacles)% 寻找路径path = findPath(snake, food, width, height, obstacles);if ~isempty(path)% 路径存在,按路径移动nextPos = path(1,:);head = snake(1,:);% 确定方向if nextPos(1) < head(1)direction = 1; % 上elseif nextPos(1) > head(1)direction = 3; % 下elseif nextPos(2) > head(2)direction = 2; % 右elsedirection = 4; % 左endelse% 没有找到路径,采取避险策略directions = [1,2,3,4]; % 上,右,下,左validDirections = [];% 检查每个方向是否安全for dir = directions[newSnake, ~] = moveSnake(snake, dir, food, width, height);if ~checkCollision(newSnake, width, height, obstacles)validDirections = [validDirections, dir];endend% 如果有安全方向,随机选择一个if ~isempty(validDirections)direction = validDirections(randi(length(validDirections)));elsedirection = 1; % 没有安全方向,默认向上(游戏结束)endend
end
🎉 完整代码 🎉
下面是一个完整可运行的贪吃蛇游戏代码,适配MATLAB 2016b:
function snakeGame()% 主贪吃蛇游戏函数% 初始化游戏参数clcclearclose allwidth = 20;height = 20;snake = [10,10; 10,9; 10,8]; % 初始蛇direction = 2; % 初始方向 (右)[food, foodType] = generateFood(width, height, snake);gameOver = false;score = 0;speed = 0.1;obstacles = [5,5; 5,6; 5,7; 15,15; 15,16; 15,17]; % 障碍物
% aiMode = false; % 是否启用AI模式aiMode = true; % 是否启用AI模式% 初始化图形界面fig = figure('Name','MATLAB贪吃蛇','NumberTitle','off',...'MenuBar','none','Color',[0.2 0.2 0.2],...'KeyPressFcn',@keyPressHandler);axis equal; axis off; hold on;set(gca,'XLim',[0.5 width+0.5],'YLim',[0.5 height+0.5],...'XTick',[],'YTick',[],'Color',[0.1 0.1 0.1]);% 分数显示scoreText = text(width+2, height/2, ['分数: 0'],...'Color','w','FontSize',12);% 游戏模式显示modeText.String = text(width+2, height/2+2, ['模式: 手动'],...'Color','w','FontSize',12);% 主游戏循环while ~gameOver% AI模式下的自动移动if aiModedirection = decideAIMove(snake, food, width, height, obstacles);modeText.String = '模式: AI自动';elsemodeText.String = '模式: 手动';end% 移动蛇[snake, ateFood] = moveSnake(snake, direction, food, width, height);% 检查游戏结束条件gameOver = checkCollision(snake, width, height, obstacles);% 如果吃到食物if ateFoodswitch foodTypecase 'normal'score = score + 10;case 'golden'score = score + 50;speed = max(0.05, speed * 0.9); % 加速case 'toxic'score = max(0, score - 20);if size(snake,1) > 3snake = snake(1:end-1,:); % 缩短endend[food, foodType] = generateFood(width, height, snake);end% 绘制游戏画面drawGame(snake, food, width, height, score, obstacles, foodType);% 控制游戏速度pause(speed);end% 游戏结束显示text(width/2-2, height/2, '游戏结束!', 'Color','r','FontSize',20);text(width/2-4, height/2-1, ['最终分数: ' num2str(score)],...'Color','w','FontSize',15);% 键盘处理函数function keyPressHandler(~, event)switch event.Keycase 'uparrow'if direction ~= 3 % 不能直接反向direction = 1;endcase 'rightarrow'if direction ~= 4direction = 2;endcase 'downarrow'if direction ~= 1direction = 3;endcase 'leftarrow'if direction ~= 2direction = 4;endcase 'space'aiMode = ~aiMode; % 切换AI模式case 'escape'gameOver = true;endend
endfunction [food, foodType] = generateFood(width, height, snake)% 80%普通食物,15%黄金食物,5%有毒食物r = rand();if r < 0.8foodType = 'normal';elseif r < 0.95foodType = 'golden';elsefoodType = 'toxic';end% 生成位置while truefood = [randi(height), randi(width)];if ~ismember(food, snake, 'rows')break;endend
endfunction [newSnake, ateFood] = moveSnake(snake, direction, food, width, height)% 计算新头部位置head = snake(1,:);switch directioncase 1 % 上newHead = [head(1)-1, head(2)];case 2 % 右newHead = [head(1), head(2)+1];case 3 % 下newHead = [head(1)+1, head(2)];case 4 % 左newHead = [head(1), head(2)-1];end% 检查是否吃到食物ateFood = isequal(newHead, food);% 更新蛇身if ateFoodnewSnake = [newHead; snake]; % 吃到食物,不删除尾部elsenewSnake = [newHead; snake(1:end-1,:)]; % 没吃到,删除尾部end
endfunction collision = checkCollision(snake, width, height, obstacles)head = snake(1,:);% 检查边界碰撞if head(1) < 1 || head(1) > height || head(2) < 1 || head(2) > widthcollision = true;return;end% 检查自身碰撞if size(snake,1) > 1 && ismember(head, snake(2:end,:), 'rows')collision = true;return;end% 检查障碍物碰撞if exist('obstacles','var') && ~isempty(obstacles) && ismember(head, obstacles, 'rows')collision = true;return;endcollision = false;
endfunction drawGame(snake, food, width, height, score, obstacles, foodType)cla; % 清除当前轴% 绘制网格for i = 1:widthfor j = 1:heightrectangle('Position',[i-0.5,j-0.5,1,1],...'EdgeColor',[0.3 0.3 0.3],...'FaceColor',[0.15 0.15 0.15]);endend% 绘制障碍物if exist('obstacles','var') && ~isempty(obstacles)for i = 1:size(obstacles,1)pos = obstacles(i,:);rectangle('Position',[pos(2)-0.5,pos(1)-0.5,1,1],...'FaceColor',[0.5 0.5 0.5],...'EdgeColor','none');endend% 绘制蛇for i = 1:size(snake,1)pos = snake(i,:);color = [0 0.8 0]; % 身体绿色if i == 1color = [0 1 0]; % 头部亮绿色endrectangle('Position',[pos(2)-0.5,pos(1)-0.5,1,1],...'Curvature',[0.3 0.3],...'FaceColor',color,...'EdgeColor','none');end% 绘制食物switch foodTypecase 'normal'foodColor = [1 0 0]; % 红色case 'golden'foodColor = [1 1 0]; % 黄色case 'toxic'foodColor = [0 1 0]; % 绿色endrectangle('Position',[food(2)-0.5,food(1)-0.5,1,1],...'Curvature',[1 1],...'FaceColor',foodColor,...'EdgeColor','none');% 更新分数textObjects = findobj(gca,'Type','text');for i = 1:length(textObjects)if strcmp(textObjects(i).String(1:2), '分数')textObjects(i).String = ['分数: ' num2str(score)];endenddrawnow;
endfunction direction = decideAIMove(snake, food, width, height, obstacles)% 简单AI决策:寻找食物路径或避险% 1. 尝试寻找路径到食物path = findPath(snake, food, width, height, obstacles);if ~isempty(path)% 路径存在,按路径移动nextPos = path(1,:);head = snake(1,:);% 确定方向if nextPos(1) < head(1)direction = 1; % 上elseif nextPos(1) > head(1)direction = 3; % 下elseif nextPos(2) > head(2)direction = 2; % 右elsedirection = 4; % 左endelse% 没有找到路径,采取避险策略directions = [1,2,3,4]; % 上,右,下,左validDirections = [];% 检查每个方向是否安全for dir = directions[newSnake, ~] = moveSnake(snake, dir, food, width, height);if ~checkCollision(newSnake, width, height, obstacles)validDirections = [validDirections, dir];endend% 如果有安全方向,随机选择一个if ~isempty(validDirections)direction = validDirections(randi(length(validDirections)));elsedirection = 1; % 没有安全方向,默认向上(游戏结束)endend
endfunction path = findPath(snake, food, width, height, obstacles)% 简化版A*路径寻找算法start = snake(1,:);goal = food;% 如果可以直接移动,直接返回if abs(start(1)-goal(1)) + abs(start(2)-goal(2)) == 1path = goal;return;end% 简单实现:总是尝试先水平后垂直或先垂直后水平path1 = []; path2 = [];% 尝试先水平后垂直if start(2) ~= goal(2)intermediate = [start(1), goal(2)];if ~checkCollision([intermediate; snake], width, height, obstacles) && ...~checkCollision([goal; intermediate; snake], width, height, obstacles)path1 = [intermediate; goal];endend% 尝试先垂直后水平if start(1) ~= goal(1)intermediate = [goal(1), start(2)];if ~checkCollision([intermediate; snake], width, height, obstacles) && ...~checkCollision([goal; intermediate; snake], width, height, obstacles)path2 = [intermediate; goal];endend% 返回找到的路径if ~isempty(path1)path = path1;elseif ~isempty(path2)path = path2;elsepath = [];end
end
结语 🏁
恭喜你完成了这个MATLAB贪吃蛇游戏的完整实现!🎉 从基础版本到图形界面,再到AI自动对战,我们一步步构建了一个功能丰富的游戏。
你可以进一步扩展这个项目:
- 添加更多特殊食物效果 🍎🍇🍍
- 实现多人对战模式 👥
- 改进AI算法,使用更智能的路径规划 🧠
- 添加音效和更多视觉特效 ✨
希望这篇教程对你有所帮助!Happy coding! 💻🐍