当前位置: 首页 > news >正文

《用MATLAB玩转游戏开发》Flappy Bird:小鸟飞行大战MATLAB趣味实现

《用MATLAB玩转游戏开发:从零开始打造你的数字乐园》基础篇(2D图形交互)-Flappy Bird:小鸟飞行大战MATLAB趣味实现

文章目录

  • 《用MATLAB玩转游戏开发:从零开始打造你的数字乐园》基础篇(2D图形交互)-Flappy Bird:小鸟飞行大战MATLAB趣味实现
    • 1.设计思路与原理⚙️
    • 2.完整流程图 📊
    • 3.分步实现教程 🛠️
      • 3.1. 初始化游戏环境
      • 3.2. 创建游戏界面
      • 3.3. 障碍物生成系统
      • 3.4. 游戏主循环
      • 3.5. 游戏逻辑实现
      • 3.6. 用户输入处理
      • 3.7. 游戏结束处理
      • 3.8. 重新开始游戏
      • 3.9. GIF保存功能
    • 4.完整可运行代码 🎮
    • 5.游戏操作说明 🎯
    • 6.扩展建议 💡

在这里插入图片描述

大家好!今天我将带大家用MATLAB实现经典游戏Flappy Bird!这个项目不仅能巩固你的MATLAB编程技能,还能让你了解游戏开发的基本原理。让我们开始这段有趣的编程之旅吧!🚀

1.设计思路与原理⚙️

Flappy Bird的核心机制其实非常简单,主要包括以下几个部分:

  1. 小鸟物理系统:模拟重力和跳跃
  2. 障碍物生成:随机生成上下管道
  3. 碰撞检测:判断小鸟是否撞到管道或地面
  4. 分数计算:记录玩家通过的管道数量
  5. 游戏循环:控制游戏节奏和画面更新

我们将使用MATLAB的图形处理功能来实现这些机制,主要依赖figurepatchtimer等对象。

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);catchwarning('背景音乐文件未找到,游戏将继续但没有背景音乐');end% 开始游戏start(gameTimer);

3.5. 游戏逻辑实现

实现游戏的核心逻辑:

    function gameLoop(~, ~)if game.gameOverreturn;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.xpipes(i).passed = true;game.score = game.score + 1;set(scoreText, 'String', num2str(game.score));endend% 移除屏幕外的管道if ~isempty(pipes) && pipes(1).bottom.x + game.pipeWidth < 0delete(pipes(1).bottom.handle);delete(pipes(1).top.handle);pipes(1) = [];end% 生成新管道if isempty(pipes) || pipes(end).bottom.x < game.screenWidth - 200generatePipe();end% 碰撞检测if bird.y < game.groundHeight || bird.y + game.birdSize > game.screenHeightgameOver();endfor 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.heightgameOver();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.ygameOver();endend% 强制刷新图形drawnow;end

3.6. 用户输入处理

实现键盘控制:

    function keyPress(~, event)if strcmp(event.Key, 'space') && ~game.gameOverbird.velocity = game.jumpStrength;elseif strcmp(event.Key, 'r') && game.gameOverrestartGame();endend

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');% 保存GIFsaveGif();end

3.8. 重新开始游戏

    function restartGame()% 清除现有管道for i = 1:length(pipes)delete(pipes(i).bottom.handle);delete(pipes(i).top.handle);endpipes = [];% 重置小鸟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);catchwarning('背景音乐文件未找到,游戏将继续但没有背景音乐');end% 开始游戏start(gameTimer);function gameLoop(~, ~)if game.gameOverreturn;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.xpipes(i).passed = true;game.score = game.score + 1;set(scoreText, 'String', num2str(game.score));endend% 移除屏幕外的管道if ~isempty(pipes) && pipes(1).bottom.x + game.pipeWidth < 0delete(pipes(1).bottom.handle);delete(pipes(1).top.handle);pipes(1) = [];end% 生成新管道if isempty(pipes) || pipes(end).bottom.x < game.screenWidth - 200generatePipe();end% 碰撞检测if bird.y < game.groundHeight || bird.y + game.birdSize > game.screenHeightgameOver();endfor 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.heightgameOver();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.ygameOver();endend% 强制刷新图形drawnow;endfunction keyPress(~, event)if strcmp(event.Key, 'space') && ~game.gameOverbird.velocity = game.jumpStrength;elseif strcmp(event.Key, 'r') && game.gameOverrestartGame();endendfunction 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');% 保存GIFsaveGif();endfunction restartGame()% 清除现有管道for i = 1:length(pipes)delete(pipes(i).bottom.handle);delete(pipes(i).top.handle);endpipes = [];% 重置小鸟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);endfunction 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.扩展建议 💡

如果你想进一步改进这个游戏,可以考虑:

  1. 添加更多的视觉效果,如云朵、背景滚动等
  2. 实现难度递增机制(随着分数增加,管道移动速度加快)
  3. 添加音效(跳跃音效、碰撞音效等)
  4. 实现高分记录功能

希望你喜欢这个MATLAB版的Flappy Bird!通过这个项目,你不仅学会了游戏开发的基本原理,还巩固了MATLAB的图形编程技巧。快去挑战你的最高分吧!🏆

如果有任何问题或建议,欢迎在评论区留言讨论哦!😊

相关文章:

  • 【Leetcode 每日一题】1550. 存在连续三个奇数的数组
  • android-ndk开发(12): 获取ndk内置clang的版本详情
  • spark-Schema 定义字段强类型和弱类型
  • TCP/IP 模型每层的封装格式
  • leetcode 15. 三数之和
  • 【25软考网工】第六章(3)数字签名和数字证书
  • 在C++中,符号位是否参与位运算
  • 使用vue3-seamless-scroll实现列表自动滚动播放
  • 【通讯录教程】如何将号码快速导入手机通讯录,支持苹果和安卓手机,一次性导入大量号码进入手机通讯录,基于WPF的解决方案
  • 实战项目5(08)
  • 1688 API 自动化采集实践:商品详情实时数据接口开发与优化
  • 每日c/c++题 备战蓝桥杯(洛谷P1115 最大子段和)
  • 滑动窗口——将x减到0的最小操作数
  • 自然语言处理NLP中的连续词袋(Continuous bag of words,CBOW)方法、优势、作用和程序举例
  • 嵌入式硬件篇---IIC
  • Linux:43线程封装与互斥lesson31
  • upload-labs靶场通关详解:第五关
  • RAII是什么?
  • 9.1.领域驱动设计
  • Spring Boot项目(Vue3+ElementPlus+Axios+MyBatisPlus+Spring Boot前后端分离)
  • 中国-拉共体成员国重点领域合作共同行动计划(2025-2027)
  • 上海北外滩开发建设五周年交出亮眼答卷,未来五年有何新目标?
  • 沙县小吃中东首店在沙特首都利雅得开业,首天营业额5万元
  • 影子调查丨三名“淘金客”殒命雪峰山:千余废弃金矿洞的监管难题
  • 科学家用AI寻找外星生命
  • 人民日报钟声:平等对话是解决大国间问题的正确之道