使用Arduino和Adafruit SSD1306库在OLED显示器上实现了一个基本的坦克战斗游戏
扫描二维码
随时随地手机看文章
这个代码提供了一个简单但引人入胜的坦克战斗游戏体验,结合了基本的移动,射击和碰撞检测机制。
这段代码使用Arduino和Adafruit SSD1306库在OLED显示器上实现了一个基本的坦克战斗游戏。以下是游戏的主要特点和功能:
•玩家坦克:玩家控制一辆坦克,它可以通过连接到特定引脚的按钮向上、向下、向左和向右移动。坦克定时自动发射子弹。
•敌人坦克:有6辆敌人坦克在屏幕上随机移动。每个敌方坦克也会向玩家的坦克发射子弹。
•子弹:玩家和敌人的坦克都会射击子弹。子弹的速度是坦克移动速度的两倍。当玩家的子弹击中敌人的坦克时,敌人的坦克就会被摧毁,玩家就会获得一分。敌人的子弹不会影响其他敌人的坦克。
•碰撞检测:游戏检查玩家的子弹与敌人坦克之间的碰撞,以及敌人的子弹与玩家坦克之间的碰撞。如果敌人的子弹击中玩家的坦克,玩家就会失去一条生命。
•生命和得分:玩家一开始有三条生命。每摧毁一辆敌方坦克,玩家的分数就会增加。如果玩家失去所有三条生命,游戏将显示“游戏结束”并重置。
•游戏重置:当玩家的生命值为零时,游戏会显示“游戏结束”屏幕,然后重新开始,重置分数、玩家生命值和敌方坦克位置。
•显示:游戏在OLED屏幕上显示,游戏区和计分区用一条竖线隔开。得分区域显示当前得分和剩余命值。
•初始化和设置:在游戏开始时,敌人的坦克会出现,玩家的坦克会出现在屏幕的中下方。游戏以一个显示“Tank Battle”的启动画面开始。
图表
坦克战
这个代码提供了一个简单但引人入胜的坦克战斗游戏体验,结合了基本的移动,射击和碰撞检测机制。
代码
#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