用Arduino Nano和SSSD1306 OLED構(gòu)建推盒游戲
用Arduino Nano和SSD1306 OLED與PCBX仿真構(gòu)建推盒游戲!創(chuàng)造有趣的互動體驗(yàn)。
介紹
Push Box Game基于Arduino Nano和SSD1306 OLED顯示屏,主要包括以下幾個部分:
在線仿真顯示
描述
Arduino Nano:
?作為游戲的控制單元,處理用戶輸入和游戲邏輯。
SSD1306 OLED顯示屏:
?用于顯示游戲界面,包括玩家、盒子、目標(biāo)位置和邊界。
按鈕:
?四個按鈕用于控制玩家的上下左右移動。
連接方法:
?SSD1306 OLED顯示屏通過I2C接口與Arduino Nano相連。
?四個按鈕連接到Arduino Nano的數(shù)字引腳上,每個按鈕都需要一個上拉電阻,以確保穩(wěn)定的信號讀取。
項(xiàng)目簡介:
該程序的主要功能和特點(diǎn)如下:
初始化(設(shè)置):
?設(shè)置串行通信。
?初始化OLED顯示屏并顯示游戲標(biāo)題。
?將按鈕引腳初始化為輸入上拉模式。
?調(diào)用initGame()函數(shù)來設(shè)置游戲的初始狀態(tài)。
游戲循環(huán)(循環(huán)):
?主游戲循環(huán),即持續(xù)讀取按鍵狀態(tài),更新游戲狀態(tài),并在游戲進(jìn)行期間重新繪制游戲地圖(gameRunning為真)。
初始化游戲:
?設(shè)置玩家的初始位置。
?隨機(jī)生成盒子和目標(biāo)的位置,確保它們距離邊界至少有10個像素(即兩個塊單元)。
繪制游戲地圖(drawMap):
?清除顯示并重新繪制游戲地圖,包括邊界、玩家、盒子和目標(biāo)位置。
讀取按鈕(readButtons):
?檢測按鈕狀態(tài),如果按鈕被按下,調(diào)用movePlayer()函數(shù)來移動播放器。
移動播放器(movePlayer):
?根據(jù)按鍵輸入更新玩家的位置。
?如果玩家移動到一個盒子的位置,嘗試推動盒子。
?檢查游戲是否獲勝。
檢查獲勝條件(checkWinCondition):
?如果所有方塊都在目標(biāo)位置,則顯示獲勝消息并重新開始游戲。
更新游戲(updateGame):
?可以在這里添加額外的游戲邏輯,如得分或關(guān)卡更改。
預(yù)防措施:
?確保所有連接正確,特別是I2C連接和按鈕的上拉電阻。
?在實(shí)際部署前測試每個組件,以確保它們正常工作。
?該程序使用隨機(jī)數(shù)來初始化盒子和目標(biāo)的位置,這可能導(dǎo)致一些游戲配置無法解決。您可能需要添加額外的邏輯來確保生成的布局是可解決的。
代碼
#include
#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 // 地圖寬度,方塊數(shù)
#define MAP_HEIGHT 8 // 地圖高度,方塊數(shù)
// 玩家、箱子和目標(biāo)的位置
int playerX = 1;
int playerY = 1;
int boxX[2] = {2, 4};
int boxY[2] = {1, 3};
int targetX[2] = {3, 5}; // 兩個目標(biāo)位置
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() {
// 初始化游戲狀態(tài)
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
}
// 繪制目標(biāo)位置,使用黃色表示
for (int i = 0; i < 2; i++) {
display.drawRect(targetX[i] * 5, targetY[i] * 5, 5, 5, WHITE); // 目標(biāo)方塊尺寸: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;
// 檢查新位置是否在地圖范圍內(nèi)
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() {
// 檢查所有箱子是否都在目標(biāo)位置
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() {
// 游戲邏輯更新
}
本文編譯自hackster.io