回到首页 返回首页
回到顶部 回到顶部
返回上一页 返回上一页

Beetle 树莓派RP2350------初学者视角的试用第二步:FLAPPY_BIRD小游戏 简单

头像 蠍蠍蠍蠍蠍 2025.06.05 46 3

基于上次我们点亮oled之后,这次我们开始尝试利用按键配合oled屏幕实现一个小游戏的制作!

一想到控制简单的游戏,第一个想到的就是flappy_bird

image.png

在线游戏地址:https://flappybird.monster/zh-CN
大家可以回顾一下当年这个令人上头的小游戏,整理一下游戏的规则!

#开始制作

游戏的控制需要一个按键开启,我们下面在上周oled的基础上给Beetle RP2350连接一个按钮,为了保证按钮的电压稳定,这里我们采用上拉电阻的方式进行连接!

image.png
4bf04e5a896556ace725513d739de0a6.jpg

按钮连接完成后,我们将代码oled显示的第二行更换为按键的检测,确保我们的按键连接方式没有问题!

代码
#include <U8g2lib.h>
// 初始化U8g2库(I2C接口),指定地址为0x3C
U8G2_SSD1306_128X64_NONAME_F_HW_I2C u8g2(U8G2_R0, /* reset=*/ U8X8_PIN_NONE, 0x3C);

const int button = 9;

void setup() {
    Serial.begin(9600); 
    if (!u8g2.begin()) {
        Serial.println("lose");
        while (1);
    }
    pinMode(button,INPUT_PULLUP);
}

void loop() {
    u8g2.firstPage();
    do {
      int mode = digitalRead(button);
        // 显示第一行(四字一行)
        u8g2.setFont(u8g2_font_ncenB08_tr); // 更换字体
        u8g2.setCursor(0, 12);
        u8g2.print("DF ROBOT");
        u8g2.setCursor(0, 24);
        u8g2.print("Beetle RP2350");
        u8g2.setCursor(0, 36);
        u8g2.print(mode);
        u8g2.setCursor(0, 48);
        u8g2.print("DATE:2025/5/1");
    } while (u8g2.nextPage());
    delay(30);
}

下载完成后,屏幕上第三行会被更改为按键状态的检测,通过按压检测按键是否正常工作!

#下面开始我们的flappybird程序设计

主要分为以下步骤完成:

(一)变量定义

  1. OLED 控制相关
  2. 通过U8G2_SSD1306_128X64_NONAME_F_HW_I2C u8g2(U8G2_R0, /* reset=*/ U8X8_PIN_NONE, 0x3C);语句初始化 U8g2 库,以此来对 OLED 显示屏进行控制。这里,U8G2_SSD1306_128X64_NONAME_F_HW_I2C明确了 OLED 的型号以及采用硬件 I2C 接口进行通信的方式;U8G2_R0设定了显示方向;0x3C则是 OLED 的 I2C 地址,Beetle RP2040 凭借这个地址在 I2C 总线上与 OLED 进行通信。
  3. 游戏元素状态变量
  4. 定义了一系列变量来描述游戏中的各个元素状态。例如,birdY用于表示小鸟在屏幕中的垂直位置;pipeX表示管道在屏幕中的水平位置;pipeGap规定了上下管道之间的间隙距离;pipeHeightTop和pipeHeightBottom分别表示上下管道的高度;score用于记录玩家在游戏中的得分情况。
  5. 开关状态变量
  6. buttonPin指定了开关所连接的引脚为 9;buttonState和lastButtonState这两个变量用于监测开关状态的变化,进而实现游戏中的操作响应,比如控制小鸟飞行。
  7. 游戏逻辑变量
  8. gravity代表重力加速度,模拟现实中重力对小鸟的作用;velocity表示小鸟的飞行速度;gameStarted作为一个标志变量,用于判断游戏是否已经开始。

(二)setup函数

  1. 开关引脚初始化
  2. pinMode(buttonPin, INPUT_PULLUP);语句将连接开关的 GPIO 3 引脚配置为输入模式,并启用内部上拉电阻。这意味着即使外部没有连接上拉电阻,引脚内部也会自动将其拉高到高电平状态,增强了电路的稳定性和可靠性。
  3. OLED 初始化
  4. u8g2.begin();尝试初始化 OLED 显示屏。如果初始化成功,OLED 将准备好接收后续的显示指令;若初始化失败,可能需要检查硬件连接或 OLED 的工作状态。u8g2.setFont(u8g2_font_ncenB08_tr);设置了在 OLED 上显示文本所使用的字体为u8g2_font_ncenB08_tr,以便在屏幕上呈现特定风格的文字。

(三)自定义函数

  1. drawBird函数
  2. 此函数负责在 OLED 屏幕上绘制小鸟的图形。通过u8g2.drawDisc(20, birdY, 5);语句,以坐标 (20, birdY) 为圆心,半径为 5 绘制一个实心圆来代表小鸟,其中birdY是小鸟在垂直方向上的位置变量,会随着游戏进行而动态改变。
  3. drawPipes函数
  4. 该函数用于绘制游戏中的管道。通过u8g2.drawBox(pipeX, 0, 10, pipeHeightTop);和u8g2.drawBox(pipeX, pipeHeightTop + pipeGap, 10, pipeHeightBottom);两条语句,分别在指定的位置绘制上下两根管道。pipeX是管道的水平位置变量,会随着游戏进行而向左移动;pipeHeightTop和pipeHeightBottom分别是上下管道的高度,且它们的和加上pipeGap等于 OLED 屏幕的高度(64 像素)。
  5. checkCollision函数
  6. 用于检测小鸟是否与管道或屏幕边界发生碰撞。首先判断小鸟的垂直位置birdY是否超出屏幕范围(birdY = 64),如果超出则返回true,表示发生碰撞。接着检查小鸟与管道在水平方向上是否接近(pipeX + 10 >= 20 && pipeX

代码
#include <U8g2lib.h>
// 初始化U8g2库(I2C接口),指定地址为0x3C
U8G2_SSD1306_128X64_NONAME_F_HW_I2C u8g2(U8G2_R0, /* reset=*/ U8X8_PIN_NONE, 0x3C);

const int buttonPin = 9;
int birdY = 32;
int pipeX = 128;
int pipeGap = 30;
int pipeHeightTop = random(20, 40);
int pipeHeightBottom = 64 - pipeHeightTop - pipeGap;
int score = 0;
bool buttonState = HIGH;
bool lastButtonState = HIGH;
int gravity = 1;
int velocity = 0;
bool gameStarted = false;
int countdown = 3;

void setup() {
    pinMode(buttonPin, INPUT_PULLUP);
    u8g2.begin();
    u8g2.setFont(u8g2_font_ncenB08_tr);
}

void drawBird() {
    u8g2.drawDisc(20, birdY, 5);
}

void drawPipes() {
    u8g2.drawBox(pipeX, 0, 10, pipeHeightTop);
    u8g2.drawBox(pipeX, pipeHeightTop + pipeGap, 10, pipeHeightBottom);
}

bool checkCollision() {
    if (birdY <= 0 || birdY >= 64) {
        return true;
    }
    if (pipeX + 10 >= 20 && pipeX <= 30) {
        if (birdY <= pipeHeightTop || birdY >= pipeHeightTop + pipeGap) {
            return true;
        }
    }
    return false;
}

void showStartScreen() {
    u8g2.clearBuffer();
    if (!gameStarted) {
        u8g2.setCursor(40, 32);
        u8g2.print("Ready");
    } else if (countdown > 0) {
        u8g2.setCursor(60, 32);
        u8g2.print(countdown);
    } else {
        u8g2.setCursor(55, 32);
        u8g2.print("Go");
    }
    u8g2.sendBuffer();
}

void loop() {
    if (!gameStarted) {
        buttonState = digitalRead(buttonPin);
        if (buttonState != lastButtonState && buttonState == LOW) {
            gameStarted = true;
        }
        lastButtonState = buttonState;
        showStartScreen();
        if (gameStarted) {
            delay(1000);
            countdown--;
            while (countdown > 0) {
                showStartScreen();
                delay(1000);
                countdown--;
            }
            showStartScreen();
            delay(1000);
        }
    } else {
        buttonState = digitalRead(buttonPin);
        if (buttonState != lastButtonState && buttonState == LOW) {
            velocity = -5;
        }
        lastButtonState = buttonState;

        velocity += gravity;
        birdY += velocity;

        pipeX -= 2;
        if (pipeX < -10) {
            pipeX = 128;
            pipeHeightTop = random(20, 40);
            pipeHeightBottom = 64 - pipeHeightTop - pipeGap;
            score++;
        }

        if (checkCollision()) {
            // 游戏结束逻辑,这里简单清空屏幕并显示分数
            u8g2.clearBuffer();
            u8g2.setCursor(0, 32);
            u8g2.print("Game Over!");
            u8g2.setCursor(0, 42);
            u8g2.print("Score: ");
            u8g2.print(score);
            u8g2.sendBuffer();
            while (1);
        }

        u8g2.firstPage();
        do {
            u8g2.clearBuffer();
            drawBird();
            drawPipes();
            u8g2.setCursor(0, 10);
            u8g2.print("Score: ");
            u8g2.print(score);
            } while (u8g2.nextPage());
             delay(20);
}
}

游戏成功运行!!!

下面我们对界面的UI进行优化,显著提升了游戏的视觉吸引力和用户体验,同时保持了代码的简洁性和性能。游戏现在具有更专业的视觉效果,游戏元素的识别度更高,界面交互也更加友好。

主要优化内容:

鸟的造型优化

使用椭圆绘制身体,更符合鸟类形态

添加三角形翅膀并实现拍打动画(150ms切换一次)

增加头部细节(眼睛、鸟嘴)

碰撞检测区域更精确

障碍物造型优化

管道宽度增加到15像素

添加管道顶部/底部边缘(3像素厚)

管道之间有更明显的间隙

整体UI优化

开始界面

添加游戏标题"FLAPPY BIRD"

装饰边框和静态鸟图案

清晰的开始提示

游戏界面

添加地平线

背景云朵装饰

分数显示带背景框(提高可读性)

结束界面

添加装饰边框

大字体显示"GAME OVER"

分数居中显示

明确的重启提示

游戏流程优化

添加完整的游戏重启功能

改进碰撞检测逻辑

增加游戏元素之间的间距

添加视觉反馈(鸟的翅膀动画)

代码
#include <U8g2lib.h>
// 初始化U8g2库(I2C接口),指定地址为0x3C
U8G2_SSD1306_128X64_NONAME_F_HW_I2C u8g2(U8G2_R0, /* reset=*/ U8X8_PIN_NONE, 0x3C);

const int buttonPin = 9;
int birdY = 32;
int pipeX = 128;
int pipeGap = 30;
int pipeHeightTop = 0;
int pipeHeightBottom = 0;
int score = 0;
bool buttonState = HIGH;
bool lastButtonState = HIGH;
int gravity = 1;
int velocity = 0;
bool gameStarted = false;
int countdown = 3;
unsigned long lastFlapTime = 0;
int wingOffset = 0;
bool wingDirection = false;

void setup() {
    pinMode(buttonPin, INPUT_PULLUP);
    u8g2.begin();
    u8g2.setFont(u8g2_font_ncenB08_tr);
    generatePipeHeights();
}

// 生成合理的管道高度
void generatePipeHeights() {
    int minTopHeight = 10;
    int maxTopHeight = 40;
    pipeHeightTop = random(minTopHeight, maxTopHeight);
    pipeHeightBottom = 64 - pipeHeightTop - pipeGap;
    // 确保管道间隙始终存在
    while (pipeHeightBottom < 10) {
        pipeHeightTop = random(minTopHeight, maxTopHeight);
        pipeHeightBottom = 64 - pipeHeightTop - pipeGap;
    }
}

// 优化的鸟造型(带翅膀动画)
void drawBird() {
    // 鸟身体(椭圆)
    u8g2.drawEllipse(20, birdY, 6, 4, U8G2_DRAW_ALL);

    // 鸟头
    u8g2.drawCircle(26, birdY - 2, 3);

    // 眼睛
    u8g2.drawPixel(27, birdY - 2);

    // 鸟嘴(三角形)
    u8g2.drawTriangle(28, birdY - 2, 31, birdY - 3, 28, birdY);

    // 翅膀(带动画效果)
    if (millis() - lastFlapTime > 150) {
        lastFlapTime = millis();
        wingDirection =!wingDirection;
        wingOffset = wingDirection? 2 : -2;
    }
    u8g2.drawTriangle(18, birdY, 15, birdY - 2 + wingOffset, 15, birdY + 2 + wingOffset);
}

// 优化的障碍物造型(带管道细节)
void drawPipes() {
    // 顶部管道
    u8g2.drawBox(pipeX, 0, 15, pipeHeightTop - 3);
    // 管道顶部边缘
    u8g2.drawBox(pipeX - 2, pipeHeightTop - 3, 19, 3);

    // 底部管道
    u8g2.drawBox(pipeX, pipeHeightTop + pipeGap + 3, 15, pipeHeightBottom - 3);
    // 管道底部边缘
    u8g2.drawBox(pipeX - 2, pipeHeightTop + pipeGap, 19, 3);
}

bool checkCollision() {
    // 碰撞检测区域(比视觉造型稍小)
    if (birdY <= 5 || birdY >= 59) {
        return true;
    }
    if (pipeX + 15 >= 15 && pipeX <= 30) {
        if (birdY <= pipeHeightTop || birdY >= pipeHeightTop + pipeGap) {
            return true;
        }
    }
    return false;
}

// 优化的UI界面
void showStartScreen() {
    u8g2.clearBuffer();
    u8g2.setDrawColor(1);

    // 绘制装饰边框
    u8g2.drawFrame(0, 0, 128, 64);

    if (!gameStarted) {
        u8g2.setFont(u8g2_font_ncenB14_tr);
        u8g2.drawStr(10, 25, "FLAPPY");
        u8g2.drawStr(20, 45, "BIRD");

        u8g2.setFont(u8g2_font_ncenB08_tr);
        u8g2.drawStr(10, 60, "Press to Start");

        // 绘制装饰鸟
        u8g2.drawDisc(100, 15, 5);
        u8g2.drawTriangle(95, 15, 90, 13, 90, 17);
    }
    else if (countdown > 0) {
        u8g2.setFont(u8g2_font_ncenB24_tr);
        char countStr[2];
        sprintf(countStr, "%d", countdown);
        u8g2.drawStr(60, 40, countStr);
    }
    else {
        u8g2.setFont(u8g2_font_ncenB24_tr);
        u8g2.drawStr(50, 40, "GO!");
    }
    u8g2.sendBuffer();
}

// 优化的游戏结束界面
void showGameOver() {
    u8g2.clearBuffer();

    // 绘制装饰边框
    u8g2.drawFrame(0, 0, 128, 64);

    u8g2.setFont(u8g2_font_ncenB14_tr);
    u8g2.drawStr(10, 25, "GAME OVER");

    u8g2.setFont(u8g2_font_ncenB10_tr);
    u8g2.setCursor(10, 45);
    u8g2.print("Score: ");
    u8g2.print(score);

    u8g2.setFont(u8g2_font_ncenB08_tr);
    u8g2.drawStr(10, 60, "Press to Restart");

    u8g2.sendBuffer();
}

void resetGame() {
    birdY = 32;
    pipeX = 128;
    score = 0;
    velocity = 0;
    generatePipeHeights();
    countdown = 3;
    gameStarted = false;
}

void loop() {
    if (!gameStarted) {
        buttonState = digitalRead(buttonPin);
        if (buttonState!= lastButtonState && buttonState == LOW) {
            gameStarted = true;
        }
        lastButtonState = buttonState;
        showStartScreen();
        if (gameStarted) {
            delay(1000);
            countdown--;
            while (countdown > 0) {
                showStartScreen();
                delay(1000);
                countdown--;
            }
            showStartScreen();
            delay(1000);
        }
    }
    else {
        buttonState = digitalRead(buttonPin);
        if (buttonState!= lastButtonState && buttonState == LOW) {
            velocity = -5;
        }
        lastButtonState = buttonState;

        velocity += gravity;
        birdY += velocity;

        pipeX -= 2;
        if (pipeX < -15) {
            pipeX = 128;
            generatePipeHeights();
            score++;
        }

        if (checkCollision()) {
            showGameOver();
            delay(1000);

            // 等待重启
            while (digitalRead(buttonPin) == HIGH) {
                delay(10);
            }
            resetGame();
            return;
        }

        u8g2.firstPage();
        do {
            u8g2.clearBuffer();

            // 绘制背景地平线
            u8g2.drawHLine(0, 63, 128);

            // 绘制云朵装饰
            u8g2.drawDisc(20, 15, 4);
            u8g2.drawDisc(25, 12, 5);
            u8g2.drawDisc(30, 15, 4);

            // 绘制游戏元素
            drawPipes();
            drawBird();

            // 绘制分数(带背景框)
            u8g2.setDrawColor(0);
            u8g2.drawBox(0, 0, 40, 12);
            u8g2.setDrawColor(1);
            u8g2.setFont(u8g2_font_ncenB08_tr);
            u8g2.setCursor(5, 10);
            u8g2.print("Score: ");
            u8g2.print(score);

        } while (u8g2.nextPage());
        delay(20);
    }
}

到此,bettle rp2350的基本功能测试完成,真的是体验感太好了!!!

对此简单整理一下本次试用中,对于这块开源版的判断吧:

  1. 强大的主控性能
  2. Beetle RP2350 以 RP2040 芯片为核心,具备双 Arm Cortex - M0+ 内核,运行频率可达 133MHz。这使得它能够高效处理如 “飞扬的小鸟” 游戏中复杂的逻辑运算,包括小鸟飞行轨迹计算、管道移动控制、碰撞检测以及与 OLED 显示屏和按钮的交互等任务,保障游戏流畅运行。
  3. 丰富的接口资源
  4. 拥有丰富的 GPIO 引脚,可灵活连接各类外部设备。在 “飞扬的小鸟” 游戏实现中,通过 GPIO 引脚与 OLED 显示屏进行 I2C 通信,传输显示数据以呈现精美的游戏画面;同时,利用 GPIO 引脚连接按钮,实现玩家与游戏的交互操作,方便检测按钮状态来控制小鸟飞行,充分展现了其接口应用的灵活性。
  5. 易于上手的开发
  6. 支持 Arduino 开发环境,对于广大电子爱好者和开发者而言,无需深入掌握底层硬件知识和复杂的汇编语言编程。借助 Arduino 简洁的函数库和直观的代码结构,能够快速开发项目。如在开发 “飞扬的小鸟” 游戏时,开发者可轻松调用相关函数进行引脚初始化、显示控制、随机数生成等操作,降低了开发门槛,加速了项目开发进程。
  7. 良好的扩展性
  8. 基于 RP2040 的架构设计,为项目的功能扩展提供了广阔空间。在 “飞扬的小鸟” 游戏基础上,可通过添加传感器(如加速度计实现小鸟更自然的飞行控制)、通信模块(如蓝牙实现多人对战)等外部设备,进一步丰富游戏功能。其扩展性为开发者提供了无限创意可能,满足不同应用场景需求。
  9. 稳定的电源管理
  10. 具备稳定的电源管理系统,能够为连接的外部设备如 OLED 显示屏提供稳定的 3.3V 工作电压,确保设备正常运行。在游戏运行过程中,稳定的电源供应保障了显示屏显示的稳定性以及按钮检测的准确性,避免因电压波动导致的游戏异常情况。

Beetle RP2350 优势众多,它以 RP2040 芯片为核心,双 Arm Cortex - M0 + 内核与 133MHz 运行频率赋予其强大主控性能,可高效处理复杂逻辑;丰富 GPIO 引脚便于连接 OLED 显示屏、按钮等各类外部设备;支持 Arduino 开发环境,降低开发门槛,加速项目进程;扩展性强,为功能拓展提供广阔空间;稳定电源管理保障设备稳定运行;小巧外形适配多种应用场景;庞大活跃的开发者社区提供丰富技术资源与经验分享;性价比高,在低成本下实现丰富功能,是创意电子项目的最优选择!!!!!!!!!!

评论

user-avatar
  • achao1111

    achao11112025.06.05

    好玩好玩

    0
    • 小高……

      小高……2025.06.05

      还得是专业权威的老师

      0
      • ROCK-PENG

        ROCK-PENG2025.06.05

        点赞点赞

        0