《用MATLAB玩转游戏开发:从零开始打造你的数字乐园》基础篇(2D图形交互)-Flappy Bird:小鸟飞行大战MATLAB趣味实现
文章目录
大家好!今天我将带大家用MATLAB实现经典游戏Flappy Bird!这个项目不仅能巩固你的MATLAB编程技能,还能让你了解游戏开发的基本原理。让我们开始这段有趣的编程之旅吧!🚀
1.设计思路与原理⚙️
Flappy Bird的核心机制其实非常简单,主要包括以下几个部分:
- 小鸟物理系统:模拟重力和跳跃
- 障碍物生成:随机生成上下管道
- 碰撞检测:判断小鸟是否撞到管道或地面
- 分数计算:记录玩家通过的管道数量
- 游戏循环:控制游戏节奏和画面更新
我们将使用MATLAB的图形处理功能来实现这些机制,主要依赖figure
、patch
、timer
等对象。
2.完整流程图 📊
3.分步实现教程 🛠️
3.1. 初始化游戏环境
首先我们需要设置游戏窗口和基本参数:
function flappy_bird()
% 清空工作区
clc; clear; close all;
% 游戏参数设置
game.screenWidth = 400; % 屏幕宽度
game.screenHeight = 600; % 屏幕高度
game.gravity = 0.25; % 重力加速度
game.jumpStrength = -5; % 跳跃力度
game.pipeSpeed = 2; % 管道移动速度
game.pipeGap = 150; % 管道间隙
game.pipeWidth = 50; % 管道宽度
game.birdSize = 20; % 小鸟大小
game.groundHeight = 50; % 地面高度
game.score = 0; % 初始分数
game.gameOver = false; % 游戏状态标志
3.2. 创建游戏界面
接下来创建游戏图形界面和各种图形对象:
% 创建图形窗口
fig = figure('Name', 'Flappy Bird - MATLAB', ...
'NumberTitle', 'off', ...
'Position', [100, 100, game.screenWidth, game.screenHeight], ...
'Color', [0.7 0.9 1], ...
'KeyPressFcn', @keyPress, ...
'MenuBar', 'none', ...
'ToolBar', 'none');
% 创建坐标轴
ax = axes('Parent', fig, ...
'Position', [0 0 1 1], ...
'XLim', [0 game.screenWidth], ...
'YLim', [0 game.screenHeight], ...
'Visible', 'off');
hold(ax, 'on');
% 创建小鸟
bird.x = game.screenWidth / 4;
bird.y = game.screenHeight / 2;
bird.velocity = 0;
bird.handle = rectangle('Position', [bird.x, bird.y, game.birdSize, game.birdSize], ...
'FaceColor', [1 1 0], ...
'EdgeColor', [0 0 0], ...
'Curvature', [1 1]);
% 创建地面
ground.handle = patch([0, game.screenWidth, game.screenWidth, 0], ...
[0, 0, game.groundHeight, game.groundHeight], ...
[0.6 0.4 0.2], 'EdgeColor', 'none');
% 创建分数显示
scoreText = text(game.screenWidth/2, game.screenHeight-50, num2str(game.score), ...
'FontSize', 30, 'Color', [1 1 1], ...
'HorizontalAlignment', 'center', 'FontWeight', 'bold');
3.3. 障碍物生成系统
管道是游戏的主要障碍物,我们需要随机生成它们:
% 初始化管道
pipes = [];
function generatePipe()
% 随机生成管道间隙的位置
gapY = randi([game.groundHeight+50, game.screenHeight-50-game.pipeGap]);
% 下管道
bottomPipe = struct();
bottomPipe.x = game.screenWidth;
bottomPipe.height = gapY - game.pipeGap/2;
bottomPipe.handle = patch([0, game.pipeWidth, game.pipeWidth, 0]+bottomPipe.x, ...
[0, 0, bottomPipe.height, bottomPipe.height], ...
[0 0.8 0], 'EdgeColor', 'none');
% 上管道
topPipe = struct();
topPipe.x = game.screenWidth;
topPipe.height = game.screenHeight - gapY - game.pipeGap/2;
topPipe.y = gapY + game.pipeGap/2;
topPipe.handle = patch([0, game.pipeWidth, game.pipeWidth, 0]+topPipe.x, ...
[0, 0, topPipe.height, topPipe.height]+topPipe.y, ...
[0 0.8 0], 'EdgeColor', 'none');
% 添加到管道列表
pipes = [pipes; struct('bottom', bottomPipe, 'top', topPipe, 'passed', false)];
end
3.4. 游戏主循环
使用MATLAB的timer对象创建游戏循环:
% 创建游戏计时器
gameTimer = timer('ExecutionMode', 'fixedRate', ...
'Period', 0.02, ...
'TimerFcn', @gameLoop);
% 生成初始管道
generatePipe();
% 启动背景音乐
try
[y, Fs] = audioread('background_music.mp3');
player = audioplayer(y, Fs);
play(player);
catch
warning('背景音乐文件未找到,游戏将继续但没有背景音乐');
end
% 开始游戏
start(gameTimer);
3.5. 游戏逻辑实现
实现游戏的核心逻辑:
function gameLoop(~, ~)
if game.gameOver
return;
end
% 更新小鸟位置
bird.velocity = bird.velocity + game.gravity;
bird.y = bird.y + bird.velocity;
set(bird.handle, 'Position', [bird.x, bird.y, game.birdSize, game.birdSize]);
% 旋转小鸟(根据速度)
rotationAngle = min(max(bird.velocity * 5, -30), 30);
rotate(bird.handle, [0 0 1], rotationAngle, [bird.x, bird.y, 0]);
% 移动管道
for i = 1:length(pipes)
pipes(i).bottom.x = pipes(i).bottom.x - game.pipeSpeed;
pipes(i).top.x = pipes(i).top.x - game.pipeSpeed;
% 更新管道图形
set(pipes(i).bottom.handle, 'XData', [0, game.pipeWidth, game.pipeWidth, 0]+pipes(i).bottom.x);
set(pipes(i).top.handle, 'XData', [0, game.pipeWidth, game.pipeWidth, 0]+pipes(i).top.x);
% 检查是否通过管道
if ~pipes(i).passed && pipes(i).bottom.x + game.pipeWidth < bird.x
pipes(i).passed = true;
game.score = game.score + 1;
set(scoreText, 'String', num2str(game.score));
end
end
% 移除屏幕外的管道
if ~isempty(pipes) && pipes(1).bottom.x + game.pipeWidth < 0
delete(pipes(1).bottom.handle);
delete(pipes(1).top.handle);
pipes(1) = [];
end
% 生成新管道
if isempty(pipes) || pipes(end).bottom.x < game.screenWidth - 200
generatePipe();
end
% 碰撞检测
if bird.y < game.groundHeight || bird.y + game.birdSize > game.screenHeight
gameOver();
end
for i = 1:length(pipes)
% 检测与下管道的碰撞
if bird.x + game.birdSize > pipes(i).bottom.x && ...
bird.x < pipes(i).bottom.x + game.pipeWidth && ...
bird.y < pipes(i).bottom.height
gameOver();
end
% 检测与上管道的碰撞
if bird.x + game.birdSize > pipes(i).top.x && ...
bird.x < pipes(i).top.x + game.pipeWidth && ...
bird.y + game.birdSize > pipes(i).top.y
gameOver();
end
end
% 强制刷新图形
drawnow;
end
3.6. 用户输入处理
实现键盘控制:
function keyPress(~, event)
if strcmp(event.Key, 'space') && ~game.gameOver
bird.velocity = game.jumpStrength;
elseif strcmp(event.Key, 'r') && game.gameOver
restartGame();
end
end
3.7. 游戏结束处理
function gameOver()
game.gameOver = true;
stop(gameTimer);
% 显示游戏结束文字
text(game.screenWidth/2, game.screenHeight/2, '游戏结束!', ...
'FontSize', 40, 'Color', [1 0 0], ...
'HorizontalAlignment', 'center', 'FontWeight', 'bold');
text(game.screenWidth/2, game.screenHeight/2-50, ['得分: ' num2str(game.score)], ...
'FontSize', 30, 'Color', [1 1 1], ...
'HorizontalAlignment', 'center', 'FontWeight', 'bold');
text(game.screenWidth/2, game.screenHeight/2-100, '按R键重新开始', ...
'FontSize', 20, 'Color', [1 1 1], ...
'HorizontalAlignment', 'center');
% 保存GIF
saveGif();
end
3.8. 重新开始游戏
function restartGame()
% 清除现有管道
for i = 1:length(pipes)
delete(pipes(i).bottom.handle);
delete(pipes(i).top.handle);
end
pipes = [];
% 重置小鸟
bird.y = game.screenHeight / 2;
bird.velocity = 0;
set(bird.handle, 'Position', [bird.x, bird.y, game.birdSize, game.birdSize]);
rotate(bird.handle, [0 0 1], 0, [bird.x, bird.y, 0]);
% 重置分数
game.score = 0;
set(scoreText, 'String', '0');
% 删除游戏结束文本
delete(findall(gcf, 'Type', 'text', 'String', '游戏结束!'));
delete(findall(gcf, 'Type', 'text', 'String', ['得分: ' num2str(game.score)]));
delete(findall(gcf, 'Type', 'text', 'String', '按R键重新开始'));
% 生成新管道
generatePipe();
% 重置游戏状态
game.gameOver = false;
% 重新开始计时器
start(gameTimer);
end
3.9. GIF保存功能
function saveGif()
% 创建GIF文件名
timestamp = datestr(now, 'yyyy-mm-dd_HH-MM-SS');
gifFilename = ['flappy_bird_' timestamp '.gif'];
% 获取当前图形窗口内容
frame = getframe(fig);
im = frame2im(frame);
[imind, cm] = rgb2ind(im, 256);
% 写入GIF文件
imwrite(imind, cm, gifFilename, 'gif', 'Loopcount', inf, 'DelayTime', 0.1);
disp(['游戏已保存为GIF: ' gifFilename]);
end
end
4.完整可运行代码 🎮
将以下所有代码合并到一个.m文件中即可运行:
function flappy_bird()
% 清空工作区
clc; clear; close all;
% 游戏参数设置
game.screenWidth = 400; % 屏幕宽度
game.screenHeight = 600; % 屏幕高度
game.gravity = 0.25; % 重力加速度
game.jumpStrength = -5; % 跳跃力度
game.pipeSpeed = 2; % 管道移动速度
game.pipeGap = 150; % 管道间隙
game.pipeWidth = 50; % 管道宽度
game.birdSize = 20; % 小鸟大小
game.groundHeight = 50; % 地面高度
game.score = 0; % 初始分数
game.gameOver = false; % 游戏状态标志
% 创建图形窗口
fig = figure('Name', 'Flappy Bird - MATLAB', ...
'NumberTitle', 'off', ...
'Position', [100, 100, game.screenWidth, game.screenHeight], ...
'Color', [0.7 0.9 1], ...
'KeyPressFcn', @keyPress, ...
'MenuBar', 'none', ...
'ToolBar', 'none');
% 创建坐标轴
ax = axes('Parent', fig, ...
'Position', [0 0 1 1], ...
'XLim', [0 game.screenWidth], ...
'YLim', [0 game.screenHeight], ...
'Visible', 'off');
hold(ax, 'on');
% 创建小鸟
bird.x = game.screenWidth / 4;
bird.y = game.screenHeight / 2;
bird.velocity = 0;
bird.handle = rectangle('Position', [bird.x, bird.y, game.birdSize, game.birdSize], ...
'FaceColor', [1 1 0], ...
'EdgeColor', [0 0 0], ...
'Curvature', [1 1]);
% 创建地面
ground.handle = patch([0, game.screenWidth, game.screenWidth, 0], ...
[0, 0, game.groundHeight, game.groundHeight], ...
[0.6 0.4 0.2], 'EdgeColor', 'none');
% 创建分数显示
scoreText = text(game.screenWidth/2, game.screenHeight-50, num2str(game.score), ...
'FontSize', 30, 'Color', [1 1 1], ...
'HorizontalAlignment', 'center', 'FontWeight', 'bold');
% 初始化管道
pipes = [];
function generatePipe()
% 随机生成管道间隙的位置
gapY = randi([game.groundHeight+50, game.screenHeight-50-game.pipeGap]);
% 下管道
bottomPipe = struct();
bottomPipe.x = game.screenWidth;
bottomPipe.height = gapY - game.pipeGap/2;
bottomPipe.handle = patch([0, game.pipeWidth, game.pipeWidth, 0]+bottomPipe.x, ...
[0, 0, bottomPipe.height, bottomPipe.height], ...
[0 0.8 0], 'EdgeColor', 'none');
% 上管道
topPipe = struct();
topPipe.x = game.screenWidth;
topPipe.height = game.screenHeight - gapY - game.pipeGap/2;
topPipe.y = gapY + game.pipeGap/2;
topPipe.handle = patch([0, game.pipeWidth, game.pipeWidth, 0]+topPipe.x, ...
[0, 0, topPipe.height, topPipe.height]+topPipe.y, ...
[0 0.8 0], 'EdgeColor', 'none');
% 添加到管道列表
pipes = [pipes; struct('bottom', bottomPipe, 'top', topPipe, 'passed', false)];
end
% 创建游戏计时器
gameTimer = timer('ExecutionMode', 'fixedRate', ...
'Period', 0.02, ...
'TimerFcn', @gameLoop);
% 生成初始管道
generatePipe();
% 启动背景音乐
try
[y, Fs] = audioread('background_music.mp3');
player = audioplayer(y, Fs);
play(player);
catch
warning('背景音乐文件未找到,游戏将继续但没有背景音乐');
end
% 开始游戏
start(gameTimer);
function gameLoop(~, ~)
if game.gameOver
return;
end
% 更新小鸟位置
bird.velocity = bird.velocity + game.gravity;
bird.y = bird.y + bird.velocity;
set(bird.handle, 'Position', [bird.x, bird.y, game.birdSize, game.birdSize]);
% 旋转小鸟(根据速度)
rotationAngle = min(max(bird.velocity * 5, -30), 30);
rotate(bird.handle, [0 0 1], rotationAngle, [bird.x, bird.y, 0]);
% 移动管道
for i = 1:length(pipes)
pipes(i).bottom.x = pipes(i).bottom.x - game.pipeSpeed;
pipes(i).top.x = pipes(i).top.x - game.pipeSpeed;
% 更新管道图形
set(pipes(i).bottom.handle, 'XData', [0, game.pipeWidth, game.pipeWidth, 0]+pipes(i).bottom.x);
set(pipes(i).top.handle, 'XData', [0, game.pipeWidth, game.pipeWidth, 0]+pipes(i).top.x);
% 检查是否通过管道
if ~pipes(i).passed && pipes(i).bottom.x + game.pipeWidth < bird.x
pipes(i).passed = true;
game.score = game.score + 1;
set(scoreText, 'String', num2str(game.score));
end
end
% 移除屏幕外的管道
if ~isempty(pipes) && pipes(1).bottom.x + game.pipeWidth < 0
delete(pipes(1).bottom.handle);
delete(pipes(1).top.handle);
pipes(1) = [];
end
% 生成新管道
if isempty(pipes) || pipes(end).bottom.x < game.screenWidth - 200
generatePipe();
end
% 碰撞检测
if bird.y < game.groundHeight || bird.y + game.birdSize > game.screenHeight
gameOver();
end
for i = 1:length(pipes)
% 检测与下管道的碰撞
if bird.x + game.birdSize > pipes(i).bottom.x && ...
bird.x < pipes(i).bottom.x + game.pipeWidth && ...
bird.y < pipes(i).bottom.height
gameOver();
end
% 检测与上管道的碰撞
if bird.x + game.birdSize > pipes(i).top.x && ...
bird.x < pipes(i).top.x + game.pipeWidth && ...
bird.y + game.birdSize > pipes(i).top.y
gameOver();
end
end
% 强制刷新图形
drawnow;
end
function keyPress(~, event)
if strcmp(event.Key, 'space') && ~game.gameOver
bird.velocity = game.jumpStrength;
elseif strcmp(event.Key, 'r') && game.gameOver
restartGame();
end
end
function gameOver()
game.gameOver = true;
stop(gameTimer);
% 显示游戏结束文字
text(game.screenWidth/2, game.screenHeight/2, '游戏结束!', ...
'FontSize', 40, 'Color', [1 0 0], ...
'HorizontalAlignment', 'center', 'FontWeight', 'bold');
text(game.screenWidth/2, game.screenHeight/2-50, ['得分: ' num2str(game.score)], ...
'FontSize', 30, 'Color', [1 1 1], ...
'HorizontalAlignment', 'center', 'FontWeight', 'bold');
text(game.screenWidth/2, game.screenHeight/2-100, '按R键重新开始', ...
'FontSize', 20, 'Color', [1 1 1], ...
'HorizontalAlignment', 'center');
% 保存GIF
saveGif();
end
function restartGame()
% 清除现有管道
for i = 1:length(pipes)
delete(pipes(i).bottom.handle);
delete(pipes(i).top.handle);
end
pipes = [];
% 重置小鸟
bird.y = game.screenHeight / 2;
bird.velocity = 0;
set(bird.handle, 'Position', [bird.x, bird.y, game.birdSize, game.birdSize]);
rotate(bird.handle, [0 0 1], 0, [bird.x, bird.y, 0]);
% 重置分数
game.score = 0;
set(scoreText, 'String', '0');
% 删除游戏结束文本
delete(findall(gcf, 'Type', 'text', 'String', '游戏结束!'));
delete(findall(gcf, 'Type', 'text', 'String', ['得分: ' num2str(game.score)]));
delete(findall(gcf, 'Type', 'text', 'String', '按R键重新开始'));
% 生成新管道
generatePipe();
% 重置游戏状态
game.gameOver = false;
% 重新开始计时器
start(gameTimer);
end
function saveGif()
% 创建GIF文件名
timestamp = datestr(now, 'yyyy-mm-dd_HH-MM-SS');
gifFilename = ['flappy_bird_' timestamp '.gif'];
% 获取当前图形窗口内容
frame = getframe(fig);
im = frame2im(frame);
[imind, cm] = rgb2ind(im, 256);
% 写入GIF文件
imwrite(imind, cm, gifFilename, 'gif', 'Loopcount', inf, 'DelayTime', 0.1);
disp(['游戏已保存为GIF: ' gifFilename]);
end
end
5.游戏操作说明 🎯
- 空格键:让小鸟跳跃
- R键:游戏结束后重新开始
- 游戏会自动保存为GIF动画
6.扩展建议 💡
如果你想进一步改进这个游戏,可以考虑:
- 添加更多的视觉效果,如云朵、背景滚动等
- 实现难度递增机制(随着分数增加,管道移动速度加快)
- 添加音效(跳跃音效、碰撞音效等)
- 实现高分记录功能
希望你喜欢这个MATLAB版的Flappy Bird!通过这个项目,你不仅学会了游戏开发的基本原理,还巩固了MATLAB的图形编程技巧。快去挑战你的最高分吧!🏆
如果有任何问题或建议,欢迎在评论区留言讨论哦!😊