《用MATLAB玩转游戏开发:从零开始打造你的数字乐园》基础篇(2D图形交互)-贪吃蛇的百变玩法:从命令行到AI对战

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

《用MATLAB玩转游戏开发:从零开始打造你的数字乐园》基础篇(2D图形交互)-🐍 贪吃蛇的百变玩法:从命令行到AI对战 🎮

欢迎来到这篇MATLAB贪吃蛇编程全攻略!本文将带你从零开始,一步步实现一个功能丰富的贪吃蛇游戏,最终进阶到AI自动对战。准备好你的MATLAB环境(2016b版本),让我们开始这段有趣的编程之旅吧!🚀

在这里插入图片描述

1. 基础贪吃蛇实现 🏗️

1.1 设计思路

贪吃蛇的基本原理很简单:控制蛇头移动,吃到食物后身体变长,碰到边界或自身游戏结束。我们需要考虑以下几个核心组件:

  • 游戏区域:二维矩阵表示
  • 蛇的表示:用坐标序列存储蛇身
  • 食物生成:随机位置出现
  • 游戏循环:处理输入、更新状态、渲染画面

1.2 整体实现流程

以下是贪吃蛇游戏的完整流程图设计,包含游戏初始化、主循环、用户输入、AI决策、碰撞检测等关键模块:

食物系统
碰撞检测
AI决策
初始化
AI模式
手动模式
碰撞
无碰撞
吃到食物
未吃到
路径存在
无路径
随机位置生成
生成新食物
确定食物类型
不同颜色渲染
检测边界碰撞
检测碰撞
检测自身碰撞
检测障碍物碰撞
计算到食物路径
AI决策移动方向
沿路径移动
避险策略
随机选择安全方向
设置游戏区域大小
初始化游戏
创建蛇初始位置
生成第一个食物
设置初始分数/速度
开始
绘制游戏界面
游戏主循环
检测输入模式
检测键盘输入
更新蛇位置
游戏结束处理
检测是否吃到食物
增加蛇长+更新分数
正常移动
绘制新帧
结束
1.2.1 流程图说明
  1. 初始化阶段

    • 设置游戏区域(20x20网格)
    • 创建长度为3的初始蛇(水平放置)
    • 随机生成第一个食物(含不同类型)
    • 初始化分数(0)和游戏速度(0.1秒/帧)
  2. 主游戏循环

    • 检测当前控制模式(AI/手动)
    • AI模式使用简化A*算法寻路
    • 手动模式响应键盘方向键
  3. 移动处理

    H
    计算新头部位置
    是否吃到食物
    保留尾部
    删除尾部
  4. 碰撞检测系统

    头部越界
    碰到身体
    碰到障碍物
    I1
    J
    I2
    I3
  5. 食物系统

    80%
    15%
    5%
    N2
    普通食物+10分
    黄金食物+50分+加速
    有毒食物-20分+缩短
  6. AI决策逻辑

    F1
    计算水平优先路径
    计算垂直优先路径
    路径有效?
    F3
1.2.2 关键路径说明
  1. 正常游戏流程

    开始 → 初始化 → 主循环 → 输入处理 → 移动 → 碰撞检测 → 食物检测 → 画面更新 → 主循环
    
  2. 游戏结束条件

    碰撞检测 → 边界/自身/障碍物碰撞 → 结束画面 → 退出
    
  3. 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! 💻🐍


网站公告

今日签到

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