当前位置:首页 > 工业控制 > 电路设计项目集锦
[导读]这个代码提供了一个简单但引人入胜的坦克战斗游戏体验,结合了基本的移动,射击和碰撞检测机制。

这个代码提供了一个简单但引人入胜的坦克战斗游戏体验,结合了基本的移动,射击和碰撞检测机制。

这段代码使用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

本站声明: 本文章由作者或相关机构授权发布,目的在于传递更多信息,并不代表本站赞同其观点,本站亦不保证或承诺内容真实性等。需要转载请联系该专栏作者,如若文章内容侵犯您的权益,请及时联系本站删除。
换一批
延伸阅读

LED驱动电源的输入包括高压工频交流(即市电)、低压直流、高压直流、低压高频交流(如电子变压器的输出)等。

关键字: 驱动电源

在工业自动化蓬勃发展的当下,工业电机作为核心动力设备,其驱动电源的性能直接关系到整个系统的稳定性和可靠性。其中,反电动势抑制与过流保护是驱动电源设计中至关重要的两个环节,集成化方案的设计成为提升电机驱动性能的关键。

关键字: 工业电机 驱动电源

LED 驱动电源作为 LED 照明系统的 “心脏”,其稳定性直接决定了整个照明设备的使用寿命。然而,在实际应用中,LED 驱动电源易损坏的问题却十分常见,不仅增加了维护成本,还影响了用户体验。要解决这一问题,需从设计、生...

关键字: 驱动电源 照明系统 散热

根据LED驱动电源的公式,电感内电流波动大小和电感值成反比,输出纹波和输出电容值成反比。所以加大电感值和输出电容值可以减小纹波。

关键字: LED 设计 驱动电源

电动汽车(EV)作为新能源汽车的重要代表,正逐渐成为全球汽车产业的重要发展方向。电动汽车的核心技术之一是电机驱动控制系统,而绝缘栅双极型晶体管(IGBT)作为电机驱动系统中的关键元件,其性能直接影响到电动汽车的动力性能和...

关键字: 电动汽车 新能源 驱动电源

在现代城市建设中,街道及停车场照明作为基础设施的重要组成部分,其质量和效率直接关系到城市的公共安全、居民生活质量和能源利用效率。随着科技的进步,高亮度白光发光二极管(LED)因其独特的优势逐渐取代传统光源,成为大功率区域...

关键字: 发光二极管 驱动电源 LED

LED通用照明设计工程师会遇到许多挑战,如功率密度、功率因数校正(PFC)、空间受限和可靠性等。

关键字: LED 驱动电源 功率因数校正

在LED照明技术日益普及的今天,LED驱动电源的电磁干扰(EMI)问题成为了一个不可忽视的挑战。电磁干扰不仅会影响LED灯具的正常工作,还可能对周围电子设备造成不利影响,甚至引发系统故障。因此,采取有效的硬件措施来解决L...

关键字: LED照明技术 电磁干扰 驱动电源

开关电源具有效率高的特性,而且开关电源的变压器体积比串联稳压型电源的要小得多,电源电路比较整洁,整机重量也有所下降,所以,现在的LED驱动电源

关键字: LED 驱动电源 开关电源

LED驱动电源是把电源供应转换为特定的电压电流以驱动LED发光的电压转换器,通常情况下:LED驱动电源的输入包括高压工频交流(即市电)、低压直流、高压直流、低压高频交流(如电子变压器的输出)等。

关键字: LED 隧道灯 驱动电源
关闭