《用MATLAB玩转游戏开发:从零开始打造你的数字乐园》基础篇(2D图形交互)-🐍 贪吃蛇的百变玩法:从命令行到AI对战 🎮
欢迎来到这篇MATLAB贪吃蛇编程全攻略!本文将带你从零开始,一步步实现一个功能丰富的贪吃蛇游戏,最终进阶到AI自动对战。准备好你的MATLAB环境(2016b版本),让我们开始这段有趣的编程之旅吧!🚀
文章目录 📚
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 exitFlag
break;
end
% 移动蛇
[snake, ateFood] = moveSnake(snake, direction, food, width, height);
% 检查游戏结束条件
gameOver = checkCollision(snake, width, height);
% 如果吃到食物
if ateFood
score = 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 true
food = [randi(height), randi(width)];
if ~ismember(food, snake, 'rows')
break;
end
end
end
移动蛇函数:
function [newSnake, ateFood] = moveSnake(snake, direction, food, width, height)
% 计算新头部位置
head = snake(1,:);
switch direction
case 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 ateFood
newSnake = [newHead; snake]; % 吃到食物,不删除尾部
else
newSnake = [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:width
for j = 1:height
rectangle('Position',[i-0.5,j-0.5,1,1],...
'EdgeColor',[0.3 0.3 0.3],...
'FaceColor',[0.15 0.15 0.15]);
end
end
% 绘制蛇
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) > width
collision = 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;
end
collision = 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.8
foodType = 'normal';
elseif r < 0.95
foodType = 'golden';
else
foodType = 'toxic';
end
% 生成位置
while true
food = [randi(height), randi(width)];
if ~ismember(food, snake, 'rows')
break;
end
end
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
% 计算从起点到邻居的临时gScore
tempGScore = 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];
end
end
end
end
% 开放集为空但未找到路径
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; % 右
else
direction = 4; % 左
end
else
% 没有找到路径,采取避险策略
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];
end
end
% 如果有安全方向,随机选择一个
if ~isempty(validDirections)
direction = validDirections(randi(length(validDirections)));
else
direction = 1; % 没有安全方向,默认向上(游戏结束)
end
end
end
🎉 完整代码 🎉
下面是一个完整可运行的贪吃蛇游戏代码,适配MATLAB 2016b:
function snakeGame()
% 主贪吃蛇游戏函数
% 初始化游戏参数
clc
clear
close all
width = 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 aiMode
direction = decideAIMove(snake, food, width, height, obstacles);
modeText.String = '模式: AI自动';
else
modeText.String = '模式: 手动';
end
% 移动蛇
[snake, ateFood] = moveSnake(snake, direction, food, width, height);
% 检查游戏结束条件
gameOver = checkCollision(snake, width, height, obstacles);
% 如果吃到食物
if ateFood
switch foodType
case '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) > 3
snake = snake(1:end-1,:); % 缩短
end
end
[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.Key
case 'uparrow'
if direction ~= 3 % 不能直接反向
direction = 1;
end
case 'rightarrow'
if direction ~= 4
direction = 2;
end
case 'downarrow'
if direction ~= 1
direction = 3;
end
case 'leftarrow'
if direction ~= 2
direction = 4;
end
case 'space'
aiMode = ~aiMode; % 切换AI模式
case 'escape'
gameOver = true;
end
end
end
function [food, foodType] = generateFood(width, height, snake)
% 80%普通食物,15%黄金食物,5%有毒食物
r = rand();
if r < 0.8
foodType = 'normal';
elseif r < 0.95
foodType = 'golden';
else
foodType = 'toxic';
end
% 生成位置
while true
food = [randi(height), randi(width)];
if ~ismember(food, snake, 'rows')
break;
end
end
end
function [newSnake, ateFood] = moveSnake(snake, direction, food, width, height)
% 计算新头部位置
head = snake(1,:);
switch direction
case 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 ateFood
newSnake = [newHead; snake]; % 吃到食物,不删除尾部
else
newSnake = [newHead; snake(1:end-1,:)]; % 没吃到,删除尾部
end
end
function collision = checkCollision(snake, width, height, obstacles)
head = snake(1,:);
% 检查边界碰撞
if head(1) < 1 || head(1) > height || head(2) < 1 || head(2) > width
collision = 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;
end
collision = false;
end
function drawGame(snake, food, width, height, score, obstacles, foodType)
cla; % 清除当前轴
% 绘制网格
for i = 1:width
for j = 1:height
rectangle('Position',[i-0.5,j-0.5,1,1],...
'EdgeColor',[0.3 0.3 0.3],...
'FaceColor',[0.15 0.15 0.15]);
end
end
% 绘制障碍物
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');
end
end
% 绘制蛇
for i = 1:size(snake,1)
pos = snake(i,:);
color = [0 0.8 0]; % 身体绿色
if i == 1
color = [0 1 0]; % 头部亮绿色
end
rectangle('Position',[pos(2)-0.5,pos(1)-0.5,1,1],...
'Curvature',[0.3 0.3],...
'FaceColor',color,...
'EdgeColor','none');
end
% 绘制食物
switch foodType
case 'normal'
foodColor = [1 0 0]; % 红色
case 'golden'
foodColor = [1 1 0]; % 黄色
case 'toxic'
foodColor = [0 1 0]; % 绿色
end
rectangle('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)];
end
end
drawnow;
end
function 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; % 右
else
direction = 4; % 左
end
else
% 没有找到路径,采取避险策略
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];
end
end
% 如果有安全方向,随机选择一个
if ~isempty(validDirections)
direction = validDirections(randi(length(validDirections)));
else
direction = 1; % 没有安全方向,默认向上(游戏结束)
end
end
end
function path = findPath(snake, food, width, height, obstacles)
% 简化版A*路径寻找算法
start = snake(1,:);
goal = food;
% 如果可以直接移动,直接返回
if abs(start(1)-goal(1)) + abs(start(2)-goal(2)) == 1
path = 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];
end
end
% 尝试先垂直后水平
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];
end
end
% 返回找到的路径
if ~isempty(path1)
path = path1;
elseif ~isempty(path2)
path = path2;
else
path = [];
end
end
结语 🏁
恭喜你完成了这个MATLAB贪吃蛇游戏的完整实现!🎉 从基础版本到图形界面,再到AI自动对战,我们一步步构建了一个功能丰富的游戏。
你可以进一步扩展这个项目:
- 添加更多特殊食物效果 🍎🍇🍍
- 实现多人对战模式 👥
- 改进AI算法,使用更智能的路径规划 🧠
- 添加音效和更多视觉特效 ✨
希望这篇教程对你有所帮助!Happy coding! 💻🐍