《用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! 💻🐍