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

在线游戏地址:https://flappybird.monster/zh-CN
大家可以回顾一下当年这个令人上头的小游戏,整理一下游戏的规则!
#开始制作
游戏的控制需要一个按键开启,我们下面在上周oled的基础上给Beetle RP2350连接一个按钮,为了保证按钮的电压稳定,这里我们采用上拉电阻的方式进行连接!


按钮连接完成后,我们将代码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程序设计
主要分为以下步骤完成:
(一)变量定义
- OLED 控制相关:
- 通过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 进行通信。
- 游戏元素状态变量:
- 定义了一系列变量来描述游戏中的各个元素状态。例如,birdY用于表示小鸟在屏幕中的垂直位置;pipeX表示管道在屏幕中的水平位置;pipeGap规定了上下管道之间的间隙距离;pipeHeightTop和pipeHeightBottom分别表示上下管道的高度;score用于记录玩家在游戏中的得分情况。
- 开关状态变量:
- buttonPin指定了开关所连接的引脚为 9;buttonState和lastButtonState这两个变量用于监测开关状态的变化,进而实现游戏中的操作响应,比如控制小鸟飞行。
- 游戏逻辑变量:
- gravity代表重力加速度,模拟现实中重力对小鸟的作用;velocity表示小鸟的飞行速度;gameStarted作为一个标志变量,用于判断游戏是否已经开始。
(二)setup函数
- 开关引脚初始化:
- pinMode(buttonPin, INPUT_PULLUP);语句将连接开关的 GPIO 3 引脚配置为输入模式,并启用内部上拉电阻。这意味着即使外部没有连接上拉电阻,引脚内部也会自动将其拉高到高电平状态,增强了电路的稳定性和可靠性。
- OLED 初始化:
- u8g2.begin();尝试初始化 OLED 显示屏。如果初始化成功,OLED 将准备好接收后续的显示指令;若初始化失败,可能需要检查硬件连接或 OLED 的工作状态。u8g2.setFont(u8g2_font_ncenB08_tr);设置了在 OLED 上显示文本所使用的字体为u8g2_font_ncenB08_tr,以便在屏幕上呈现特定风格的文字。
(三)自定义函数
- drawBird函数:
- 此函数负责在 OLED 屏幕上绘制小鸟的图形。通过u8g2.drawDisc(20, birdY, 5);语句,以坐标 (20, birdY) 为圆心,半径为 5 绘制一个实心圆来代表小鸟,其中birdY是小鸟在垂直方向上的位置变量,会随着游戏进行而动态改变。
- drawPipes函数:
- 该函数用于绘制游戏中的管道。通过u8g2.drawBox(pipeX, 0, 10, pipeHeightTop);和u8g2.drawBox(pipeX, pipeHeightTop + pipeGap, 10, pipeHeightBottom);两条语句,分别在指定的位置绘制上下两根管道。pipeX是管道的水平位置变量,会随着游戏进行而向左移动;pipeHeightTop和pipeHeightBottom分别是上下管道的高度,且它们的和加上pipeGap等于 OLED 屏幕的高度(64 像素)。
- checkCollision函数:
- 用于检测小鸟是否与管道或屏幕边界发生碰撞。首先判断小鸟的垂直位置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的基本功能测试完成,真的是体验感太好了!!!
对此简单整理一下本次试用中,对于这块开源版的判断吧:
- 强大的主控性能:
- Beetle RP2350 以 RP2040 芯片为核心,具备双 Arm Cortex - M0+ 内核,运行频率可达 133MHz。这使得它能够高效处理如 “飞扬的小鸟” 游戏中复杂的逻辑运算,包括小鸟飞行轨迹计算、管道移动控制、碰撞检测以及与 OLED 显示屏和按钮的交互等任务,保障游戏流畅运行。
- 丰富的接口资源:
- 拥有丰富的 GPIO 引脚,可灵活连接各类外部设备。在 “飞扬的小鸟” 游戏实现中,通过 GPIO 引脚与 OLED 显示屏进行 I2C 通信,传输显示数据以呈现精美的游戏画面;同时,利用 GPIO 引脚连接按钮,实现玩家与游戏的交互操作,方便检测按钮状态来控制小鸟飞行,充分展现了其接口应用的灵活性。
- 易于上手的开发:
- 支持 Arduino 开发环境,对于广大电子爱好者和开发者而言,无需深入掌握底层硬件知识和复杂的汇编语言编程。借助 Arduino 简洁的函数库和直观的代码结构,能够快速开发项目。如在开发 “飞扬的小鸟” 游戏时,开发者可轻松调用相关函数进行引脚初始化、显示控制、随机数生成等操作,降低了开发门槛,加速了项目开发进程。
- 良好的扩展性:
- 基于 RP2040 的架构设计,为项目的功能扩展提供了广阔空间。在 “飞扬的小鸟” 游戏基础上,可通过添加传感器(如加速度计实现小鸟更自然的飞行控制)、通信模块(如蓝牙实现多人对战)等外部设备,进一步丰富游戏功能。其扩展性为开发者提供了无限创意可能,满足不同应用场景需求。
- 稳定的电源管理:
- 具备稳定的电源管理系统,能够为连接的外部设备如 OLED 显示屏提供稳定的 3.3V 工作电压,确保设备正常运行。在游戏运行过程中,稳定的电源供应保障了显示屏显示的稳定性以及按钮检测的准确性,避免因电压波动导致的游戏异常情况。
Beetle RP2350 优势众多,它以 RP2040 芯片为核心,双 Arm Cortex - M0 + 内核与 133MHz 运行频率赋予其强大主控性能,可高效处理复杂逻辑;丰富 GPIO 引脚便于连接 OLED 显示屏、按钮等各类外部设备;支持 Arduino 开发环境,降低开发门槛,加速项目进程;扩展性强,为功能拓展提供广阔空间;稳定电源管理保障设备稳定运行;小巧外形适配多种应用场景;庞大活跃的开发者社区提供丰富技术资源与经验分享;性价比高,在低成本下实现丰富功能,是创意电子项目的最优选择!!!!!!!!!!
achao11112025.06.05
好玩好玩
小高……2025.06.05
还得是专业权威的老师
ROCK-PENG2025.06.05
点赞点赞