使用Arduino和Adafruit SSD1306庫(kù)在OLED顯示器上實(shí)現(xiàn)了一個(gè)基本的坦克戰(zhàn)斗游戲
掃描二維碼
隨時(shí)隨地手機(jī)看文章
這個(gè)代碼提供了一個(gè)簡(jiǎn)單但引人入勝的坦克戰(zhàn)斗游戲體驗(yàn),結(jié)合了基本的移動(dòng),射擊和碰撞檢測(cè)機(jī)制。
這段代碼使用Arduino和Adafruit SSD1306庫(kù)在OLED顯示器上實(shí)現(xiàn)了一個(gè)基本的坦克戰(zhàn)斗游戲。以下是游戲的主要特點(diǎn)和功能:
?玩家坦克:玩家控制一輛坦克,它可以通過(guò)連接到特定引腳的按鈕向上、向下、向左和向右移動(dòng)。坦克定時(shí)自動(dòng)發(fā)射子彈。
?敵人坦克:有6輛敵人坦克在屏幕上隨機(jī)移動(dòng)。每個(gè)敵方坦克也會(huì)向玩家的坦克發(fā)射子彈。
?子彈:玩家和敵人的坦克都會(huì)射擊子彈。子彈的速度是坦克移動(dòng)速度的兩倍。當(dāng)玩家的子彈擊中敵人的坦克時(shí),敵人的坦克就會(huì)被摧毀,玩家就會(huì)獲得一分。敵人的子彈不會(huì)影響其他敵人的坦克。
?碰撞檢測(cè):游戲檢查玩家的子彈與敵人坦克之間的碰撞,以及敵人的子彈與玩家坦克之間的碰撞。如果敵人的子彈擊中玩家的坦克,玩家就會(huì)失去一條生命。
?生命和得分:玩家一開始有三條生命。每摧毀一輛敵方坦克,玩家的分?jǐn)?shù)就會(huì)增加。如果玩家失去所有三條生命,游戲?qū)@示“游戲結(jié)束”并重置。
?游戲重置:當(dāng)玩家的生命值為零時(shí),游戲會(huì)顯示“游戲結(jié)束”屏幕,然后重新開始,重置分?jǐn)?shù)、玩家生命值和敵方坦克位置。
?顯示:游戲在OLED屏幕上顯示,游戲區(qū)和計(jì)分區(qū)用一條豎線隔開。得分區(qū)域顯示當(dāng)前得分和剩余命值。
?初始化和設(shè)置:在游戲開始時(shí),敵人的坦克會(huì)出現(xiàn),玩家的坦克會(huì)出現(xiàn)在屏幕的中下方。游戲以一個(gè)顯示“Tank Battle”的啟動(dòng)畫面開始。
圖表
坦克戰(zhàn)
這個(gè)代碼提供了一個(gè)簡(jiǎn)單但引人入勝的坦克戰(zhàn)斗游戲體驗(yàn),結(jié)合了基本的移動(dòng),射擊和碰撞檢測(cè)機(jī)制。
代碼
#include
#include
#include
// Define constants
const int ENEMY_COUNT = 6;
const int SCREEN_WIDTH = 128;
const int SCREEN_HEIGHT = 64;
const int GAME_AREA_WIDTH = 100; // Game area width
const int SCORE_AREA_WIDTH = SCREEN_WIDTH - GAME_AREA_WIDTH; // Score area width
const int TANK_WIDTH = 6; // Tank width reduced by 5 times
const int TANK_HEIGHT = 6; // Tank height reduced by 5 times
const int BULLET_SPEED = 5; // Bullet speed
const int PLAYER_SHOT_INTERVAL = 1000; // Player tank firing interval in milliseconds
const int LIVES = 3; // Initial number of player tank lives
// Define I2C address and reset pin
#define OLED_RESET -1 // Use default reset pin
#define SCREEN_ADDRESS 0x3C
// Initialize OLED display
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
// Define direction enumeration
enum Direction { LEFT, RIGHT, UP, DOWN };
// Enemy structure
struct Enemy {
int x, y; // Position
int dx, dy; // Increment per frame
Direction dir; // Current moving direction
unsigned long lastShotTime; // Last firing time
bool active; // Whether the enemy is active
};
// Bullet structure
struct Bullet {
int x, y; // Position
int dx, dy; // Increment per frame
Direction dir; // Bullet direction
bool active; // Whether activated
};
// Global variables
Enemy enemies[ENEMY_COUNT];
Bullet bullets[10]; // Assume a maximum of 10 bullets at the same time
int playerX = GAME_AREA_WIDTH / 2 - TANK_WIDTH / 2; // Initial position of the player tank
int playerY = SCREEN_HEIGHT - TANK_HEIGHT - 1;
Direction playerDir = UP; // Initial direction of the player tank
unsigned long lastPlayerShotTime = 0; // Last firing time of the player tank
int score = 0; // Score
int lives = LIVES; // Player tank lives
void setup() {
// Initialize serial communication for debugging information
Serial.begin(9600);
// Initialize display
if (!display.begin(SSD1306_SWITCHCAPVCC, SCREEN_ADDRESS)) {
Serial.println(F("SSD1306 allocation failed"));
for (;;); // If initialization fails, enter an infinite loop
}
display.clearDisplay();
// Show splash screen
showSplashScreen();
// Initialize enemies
for (int i = 0; i < ENEMY_COUNT; i++) {
spawnEnemy(i);
}
// Initialize bullets
for (int i = 0; i < sizeof(bullets) / sizeof(bullets[0]); i++) {
bullets[i].active = false;
}
}
void loop() {
// Clear screen
display.clearDisplay();
// Detect button input to control player tank movement
if (digitalRead(2) == HIGH) { // Assume pin 2 is connected to the up direction key
playerDir = UP;
playerY = max(playerY - 1, 0);
} else if (digitalRead(3) == HIGH) { // Assume pin 3 is connected to the down direction key
playerDir = DOWN;
playerY = min(playerY + 1, SCREEN_HEIGHT - TANK_HEIGHT);
} else if (digitalRead(4) == HIGH) { // Assume pin 4 is connected to the left direction key
playerDir = LEFT;
playerX = max(playerX - 1, 0);
} else if (digitalRead(5) == HIGH) { // Assume pin 5 is connected to the right direction key
playerDir = RIGHT;
playerX = min(playerX + 1, GAME_AREA_WIDTH - TANK_WIDTH);
}
// Automatically fire player tank's bullets
if (millis() - lastPlayerShotTime > PLAYER_SHOT_INTERVAL) {
fireBullet(playerX + TANK_WIDTH / 2, playerY + TANK_HEIGHT / 2, playerDir);
lastPlayerShotTime = millis();
}
// Move enemies
moveEnemies();
// Automatically fire enemy bullets
autoFireEnemies();
// Update and draw bullets
updateBullets();
drawBullets();
// Check collisions
checkCollisions();
// Draw player tank
drawMiniTank(playerX, playerY, playerDir, 0);
// Draw enemies
drawEnemies();
// Draw vertical line to separate game area and score area
display.drawLine(GAME_AREA_WIDTH, 0, GAME_AREA_WIDTH, SCREEN_HEIGHT, SSD1306_WHITE);
// Show score
displayScore();
// Display content
display.display();
// If player tank's lives are used up, restart the game
if (lives <= 0) {
resetGame();
}
// Wait for a while to simulate frame rate
delay(50);
}
void showSplashScreen() {
// Set font size
display.setTextSize(2);
display.setTextColor(SSD1306_WHITE);
// Clear screen
display.clearDisplay();
// Calculate the width and height of "Tank" text
int16_t x1, y1;
uint16_t w1, h1;
display.getTextBounds("Tank", 0, 0, &x1, &y1, &w1, &h1);
// Calculate the width and height of "Battle" text
int16_t x2, y2;
uint16_t w2, h2;
display.getTextBounds("Battle", 0, 0, &x2, &y2, &w2, &h2);
// Calculate the total height of the two lines of text
uint16_t totalHeight = h1 + h2 + 4; // Add some spacing
// Calculate the starting y-coordinate to center the two lines of text
int16_t startY = (SCREEN_HEIGHT - totalHeight) / 2;
// Display "Tank" text, centered
display.setCursor((SCREEN_WIDTH - w1) / 2, startY);
display.println("Tank");
// Display "Battle" text, centered
display.setCursor((SCREEN_WIDTH - w2) / 2, startY + h1 + 4); // Add the height of the previous line and spacing
display.println("Battle");
// Update display
display.display();
// Pause for a while to allow the user to see the splash screen
delay(2000);
}
void drawMiniTank(int x, int y, Direction dir, int rotation) {
// Define the position and size of the tank's body and turret (reduced by 5 times)
int turretWidth = 2; // Turret width reduced by 5 times
int turretHeight = 2; // Turret height reduced by 5 times
// Calculate the turret's position based on the tank's moving direction
int turretX, turretY;
switch (dir) {
case LEFT:
turretX = x - turretWidth / 2;
turretY = y + (TANK_HEIGHT - turretHeight) / 2;
break;
case RIGHT:
turretX = x + TANK_WIDTH - turretWidth / 2;
turretY = y + (TANK_HEIGHT - turretHeight) / 2;
break;
case UP:
turretX = x + (TANK_WIDTH - turretWidth) / 2;
turretY = y - turretHeight;
break;
case DOWN:
turretX = x + (TANK_WIDTH - turretWidth) / 2;
turretY = y + TANK_HEIGHT - turretHeight / 2;
break;
}
// Draw the tank's body (square)
display.fillRect(x, y, TANK_WIDTH, TANK_HEIGHT, SSD1306_WHITE);
// Draw the tank's turret (small rectangle above the body)
display.fillRect(turretX, turretY, turretWidth, turretHeight, SSD1306_WHITE);
}
void moveEnemies() {
for (int i = 0; i < ENEMY_COUNT; i++) {
if (enemies[i].active) {
// Move the enemy
enemies[i].x += enemies[i].dx;
enemies[i].y += enemies[i].dy;
// If the enemy moves out of the screen, re-enter from the other side
if (enemies[i].x > GAME_AREA_WIDTH || enemies[i].x < 0) {
enemies[i].dx *= -1; // Reverse direction
enemies[i].dir = (enemies[i].dx > 0) ? RIGHT : LEFT;
}
if (enemies[i].y > SCREEN_HEIGHT || enemies[i].y < 0) {
enemies[i].dy *= -1; // Reverse direction
enemies[i].dir = (enemies[i].dy > 0) ? DOWN: UP;
}
}
}
}
void autoFireEnemies() {
for (int i = 0; i < ENEMY_COUNT; i++) {
if (enemies[i].active) {
if (millis() - enemies[i].lastShotTime > PLAYER_SHOT_INTERVAL) {
fireBullet(enemies[i].x + (TANK_WIDTH / 2) - 1, enemies[i].y + (TANK_HEIGHT / 2) - 1, enemies[i].dir);
enemies[i].lastShotTime = millis();
}
}
}
}
void fireBullet(int x, int y, Direction dir) {
for (int i = 0; i < sizeof(bullets) / sizeof(bullets[0]); i++) {
if (!bullets[i].active) {
bullets[i].x = x;
bullets[i].y = y;
bullets[i].dir = dir;
bullets[i].active = true;
setBulletSpeed(bullets[i], dir);
break;
}
}
}
void setBulletSpeed(Bullet& bullet, Direction dir) {
switch (dir) {
case LEFT:
bullet.dx = -2 * BULLET_SPEED;
bullet.dy = 0;
break;
case RIGHT:
bullet.dx = 2 * BULLET_SPEED;
bullet.dy = 0;
break;
case UP:
bullet.dx = 0;
bullet.dy = -2 * BULLET_SPEED;
break;
case DOWN:
bullet.dx = 0;
bullet.dy = 2 * BULLET_SPEED;
break;
}
}
void drawEnemies() {
for (int i = 0; i < ENEMY_COUNT; i++) {
if (enemies[i].active) {
drawMiniTank(enemies[i].x, enemies[i].y, enemies[i].dir, 0);
}
}
}
void updateBullets() {
for (int i = 0; i < sizeof(bullets) / sizeof(bullets[0]); i++) {
if (bullets[i].active) {
bullets[i].x += bullets[i].dx;
bullets[i].y += bullets[i].dy;
if (bullets[i].x < 0 || bullets[i].x > GAME_AREA_WIDTH || bullets[i].y < 0 || bullets[i].y > SCREEN_HEIGHT) {
bullets[i].active = false;
}
}
}
}
void drawBullets() {
for (int i = 0; i < sizeof(bullets) / sizeof(bullets[0]); i++) {
if (bullets[i].active) {
display.fillRect(bullets[i].x, bullets[i].y, 2, 2, SSD1306_WHITE);
}
}
}
void checkCollisions() {
// Check for collisions between player bullets and enemies
for (int i = 0; i < sizeof(bullets) / sizeof(bullets[0]); i++) {
if (bullets[i].active && bullets[i].dir == playerDir) { // Ensure it's the player's bullet
for (int j = 0; j < ENEMY_COUNT; j++) {
if (enemies[j].active && bullets[i].x >= enemies[j].x && bullets[i].x < enemies[j].x + TANK_WIDTH &&
bullets[i].y >= enemies[j].y && bullets[i].y < enemies[j].y + TANK_HEIGHT) {
// Player bullet hits enemy
bullets[i].active = false;
enemies[j].active = false;
score++; // Increase score
spawnEnemy(j); // Spawn a new enemy tank
break; // Exit inner loop
}
}
}
}
// Check for collisions between enemy bullets and player tank
for (int i = 0; i < sizeof(bullets) / sizeof(bullets[0]); i++) {
if (bullets[i].active && bullets[i].dir != playerDir) { // Ensure it's the enemy's bullet
if (bullets[i].x >= playerX && bullets[i].x < playerX + TANK_WIDTH &&
bullets[i].y >= playerY && bullets[i].y < playerY + TANK_HEIGHT) {
// Enemy bullet hits player tank
bullets[i].active = false;
lives--; // Player tank loses a life
if (lives <= 0) {
resetGame(); // If lives are used up, restart the game
}
}
}
}
}
void spawnEnemy(int index) {
enemies[index].active = true;
if (index < ENEMY_COUNT / 2) {
enemies[index].x = random(0, GAME_AREA_WIDTH / 2);
enemies[index].y = random(0, SCREEN_HEIGHT);
enemies[index].dx = 1;
enemies[index].dy = 0;
enemies[index].dir = RIGHT;
} else {
enemies[index].x = random(0, GAME_AREA_WIDTH);
enemies[index].y = random(0, SCREEN_HEIGHT / 2);
enemies[index].dx = 0;
enemies[index].dy = 1;
enemies[index].dir = DOWN;
}
enemies[index].lastShotTime = millis() - PLAYER_SHOT_INTERVAL; // Ensure immediate firing
fireBullet(enemies[index].x + (TANK_WIDTH / 2) - 1, enemies[index].y + (TANK_HEIGHT / 2) - 1, enemies[index].dir);
}
void displayScore() {
display.setTextSize(1);
display.setTextColor(SSD1306_WHITE);
display.setCursor(GAME_AREA_WIDTH + 5, 0);
display.print("PTS");
display.setCursor(GAME_AREA_WIDTH + 10, 16);
display.println(score);
display.setCursor(GAME_AREA_WIDTH + 7, 32);
display.print("LV");
display.setCursor(GAME_AREA_WIDTH + 10, 48);
display.println(lives);
}
void resetGame() {
display.clearDisplay();
display.setTextSize(2);
display.setTextColor(SSD1306_WHITE);
int16_t x, y;
uint16_t w, h;
display.getTextBounds("Game Over", 0, 0, &x, &y, &w, &h);
display.setCursor((SCREEN_WIDTH - w) / 2, (SCREEN_HEIGHT - h) / 2);
display.println("Game Over");
display.display();
delay(3000);
for (int i = 0; i < ENEMY_COUNT; i++) {
spawnEnemy(i);
}
for (int i = 0; i < sizeof(bullets) / sizeof(bullets[0]); i++) {
bullets[i].active = false;
}
playerX = GAME_AREA_WIDTH / 2 - TANK_WIDTH / 2;
playerY = SCREEN_HEIGHT - TANK_HEIGHT - 1;
playerDir = UP;
score = 0;
lives = LIVES;
}
本文編譯自hackster.io