用Arduino Nano和SSSD1306 OLED构建推盒游戏

2025-01-24

用Arduino Nano和SSD1306 OLED与PCBX仿真构建推盒游戏!创造有趣的互动体验。

介绍

Push Box Game基于Arduino Nano和SSD1306 OLED显示屏,主要包括以下几个部分:

在线仿真显示

用Arduino Nano和SSSD1306 OLED构建推盒游戏 (https://ic.work/) 工控技术 第1张

描述

Arduino Nano:

•作为游戏的控制单元,处理用户输入和游戏逻辑。

SSD1306 OLED显示屏:

•用于显示游戏界面,包括玩家、盒子、目标位置和边界。

按钮:

•四个按钮用于控制玩家的上下左右移动。

连接方法:

•SSD1306 OLED显示屏通过I2C接口与Arduino Nano相连。

•四个按钮连接到Arduino Nano的数字引脚上,每个按钮都需要一个上拉电阻,以确保稳定的信号读取。

项目简介:

该程序的主要功能和特点如下:

初始化(设置):

•设置串行通信。

•初始化OLED显示屏并显示游戏标题。

•将按钮引脚初始化为输入上拉模式。

•调用initGame()函数来设置游戏的初始状态。

游戏循环(循环):

•主游戏循环,即持续读取按键状态,更新游戏状态,并在游戏进行期间重新绘制游戏地图(gameRunning为真)。

初始化游戏:

•设置玩家的初始位置。

•随机生成盒子和目标的位置,确保它们距离边界至少有10个像素(即两个块单元)。

绘制游戏地图(drawMap):

•清除显示并重新绘制游戏地图,包括边界、玩家、盒子和目标位置。

读取按钮(readButtons):

•检测按钮状态,如果按钮被按下,调用movePlayer()函数来移动播放器。

移动播放器(movePlayer):

•根据按键输入更新玩家的位置。

•如果玩家移动到一个盒子的位置,尝试推动盒子。

•检查游戏是否获胜。

检查获胜条件(checkWinCondition):

•如果所有方块都在目标位置,则显示获胜消息并重新开始游戏。

更新游戏(updateGame):

•可以在这里添加额外的游戏逻辑,如得分或关卡更改。

预防措施:

•确保所有连接正确,特别是I2C连接和按钮的上拉电阻。

•在实际部署前测试每个组件,以确保它们正常工作。

•该程序使用随机数来初始化盒子和目标的位置,这可能导致一些游戏配置无法解决。您可能需要添加额外的逻辑来确保生成的布局是可解决的。

代码


#


i


nclude


#include


#include


#define SCREEN_WIDTH 128 // OLED display width, in pixels


#define SCREEN_HEIGHT 64 // OLED display height, in pixels


#define OLED_RESET -1 // Reset pin # (or -1 if sharing Arduino reset pin)


// 初始化OLED显示屏


Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);


// 游戏地图的尺寸(以方块为单位)


#define MAP_WIDTH 16 // 地图宽度,方块数


#define MAP_HEIGHT 8 // 地图高度,方块数


// 玩家、箱子和目标的位置


int playerX = 1;


int playerY = 1;


int boxX[2] = {2, 4};


int boxY[2] = {1, 3};


int targetX[2] = {3, 5}; // 两个目标位置


int targetY[2] = {1, 3};


bool gameRunning = true;


// 按钮引脚


const int buttonUp = 2;


const int buttonDown = 3;


const int buttonLeft = 4;


const int buttonRight = 5;


void setup() {


Serial.begin(9600);


display.begin(SSD1306_SWITCHCAPVCC, 0x3C); // Address 0x3C for 128x64


display.clearDisplay();


display.setTextSize(1);


display.setTextColor(WHITE);


display.setCursor(0, 0);


display.println("推箱子游戏");


display.display();


delay(2000); // 显示初始化效果2秒


initGame();


// 初始化按钮引脚为输入上拉模式


pinMode(buttonUp, INPUT_PULLUP);


pinMode(buttonDown, INPUT_PULLUP);


pinMode(buttonLeft, INPUT_PULLUP);


pinMode(buttonRight, INPUT_PULLUP);


}


void loop() {


if (gameRunning) {


readButtons();


updateGame();


drawMap();


delay(100);


}


}


void initGame() {


// 初始化游戏状态


playerX = 1;


playerY = 1;


boxX[0] = 2;


boxY[0] = 1;


boxX[1] = 4;


boxY[1] = 3;


gameRunning = true;


}


// 绘制游戏地图


void drawMap() {


display.clearDisplay();


// 绘制游戏边界框的线条


display.drawLine(0, 0, MAP_WIDTH * 5 - 1, 0, BLACK); // 上边框


display.drawLine(0, 5 * MAP_HEIGHT - 1, MAP_WIDTH * 5 - 1, 5 * MAP_HEIGHT - 1, WHITE); // 下边框


display.drawLine(0, 0, 0, 5 * MAP_HEIGHT - 1, WHITE); // 左边框


display.drawLine(5 * MAP_WIDTH - 1, 0, 5 * MAP_WIDTH - 1, 5 * MAP_HEIGHT - 1, WHITE); // 右边框


// 绘制玩家位置


display.drawRect(playerX * 5, playerY * 5, 5, 5, WHITE); // 玩家方块尺寸:5x5


// 绘制箱子位置


for (int i = 0; i < 2; i++) {


display.fillRect(boxX[i] * 5, boxY[i] * 5, 5, 5, WHITE); // 箱子方块尺寸:5x5


}


// 绘制目标位置,使用黄色表示


for (int i = 0; i < 2; i++) {


display.drawRect(targetX[i] * 5, targetY[i] * 5, 5, 5, WHITE); // 目标方块尺寸:5x5,颜色:黄色(0xFFFF)


}


display.display();


}


// 按钮读取和消抖逻辑


void readButtons() {


static unsigned long lastDebounceTime = 0;


static unsigned long debounceDelay = 50;


unsigned long currentMillis = millis();


if (digitalRead(buttonUp) == LOW && (currentMillis - lastDebounceTime) > debounceDelay) {


lastDebounceTime = currentMillis;


movePlayer(0, -1);


}


if (digitalRead(buttonDown) == LOW && (currentMillis - lastDebounceTime) > debounceDelay) {


lastDebounceTime = currentMillis;


movePlayer(0, 1);


}


if (digitalRead(buttonLeft) == LOW && (currentMillis - lastDebounceTime) > debounceDelay) {


lastDebounceTime = currentMillis;


movePlayer(-1, 0);


}


if (digitalRead(buttonRight) == LOW && (currentMillis - lastDebounceTime) > debounceDelay) {


lastDebounceTime = currentMillis;


movePlayer(1, 0);


}


}


void movePlayer(int dx, int dy) {


// 玩家移动逻辑


int newX = playerX + dx;


int newY = playerY + dy;


// 检查新位置是否在地图范围内


if (newX >= 0 && newX < MAP_WIDTH && newY >= 0 && newY < MAP_HEIGHT) {


// 检查新位置是否不是墙壁


if (!isWall(newX, newY)) {


// 检查新位置是否是箱子


for (int i = 0; i < 2; i++) {


if (newX == boxX[i] && newY == boxY[i]) {


// 尝试推动箱子


int boxNewX = boxX[i] + dx;


int boxNewY = boxY[i] + dy;


if (!isWall(boxNewX, boxNewY) && !isBox(boxNewX, boxNewY)) {


boxX[i] = boxNewX;


boxY[i] = boxNewY;


playerX = newX;


playerY = newY;


checkWinCondition();


return;


}


}


}


// 移动玩家


playerX = newX;


playerY = newY;


}


}


}


bool isWall(int x, int y) {


// 定义墙壁位置


return (x == 0 || x == MAP_WIDTH - 1) || (y == 0 || y == MAP_HEIGHT - 1);


}


bool isBox(int x, int y) {


// 检查位置是否有箱子


for (int i = 0; i < 2; i++) {


if (x == boxX[i] && y == boxY[i]) {


return true;


}


}


return false;


}


void checkWinCondition() {


// 检查所有箱子是否都在目标位置


bool allBoxesAtTarget = true;


for (int i = 0; i < 2; i++) {


if (boxX[i] != targetX[i] || boxY[i] != targetY[i]) {


allBoxesAtTarget = false;


break;


}


}


if (allBoxesAtTarget) {


gameRunning = false;


display.clearDisplay();


display.setTextSize(1);


display.setTextColor(WHITE);


display.setCursor(0, 0);


display.println("Congratulations!!");


display.println("You win!");


display.display();


delay(300);


initGame(); // 重新开始游戏


}


}


void updateGame() {


// 游戏逻辑更新


}


本文编译自


h


ackster


.io

文章推荐

相关推荐