#include <SPI.h>
#include <Adafruit_GFX.h>
#include <Adafruit_ILI9341.h>
#define TFT_CS 10
#define TFT_RST 9
#define TFT_DC 8
int score = 0; // 初始化玩家分數為0
#include <EEPROM.h> // 包含EEPROM庫
#define HIGHEST_SCORE_ADDRESS 0 // EEPROM中最高分數的地址
int highestScore = 0; // 最高分數
bool gameEnded = false; // 遊戲是否結束的標誌
int height = 320;
Adafruit_ILI9341 tft = Adafruit_ILI9341(TFT_CS, TFT_DC, TFT_RST);
#define JOY_X A0
#define JOY_Y A1
#define JOY_BTN 2
#define TANK_SIZE 30 // 坦克車的大小
#define BULLET_SIZE 4 // 子彈的大小
#define BULLET_SPEED 10 // 子彈的速度
#define BASE_SIZE 20 // 基地的大小
#define BASE_COLOR ILI9341_GREEN // 基地的顏色
unsigned long lastDirectionChangeTime[2] = {0}; // 每輛坦克的上次方向改變時間
unsigned long directionChangeDelay = 1000; // 3秒
struct Base {
int x, y;
} playerBase;
enum Direction { UP, DOWN, LEFT, RIGHT };
struct Tank {
int x, y;
int speed;
Direction direction;
unsigned long hitTimer; // 被擊中後的計時器
}
player, enemyTanks[2]; // 玩家坦克和兩輛敵方坦克
unsigned long lastBulletTime = 0;
unsigned long bulletInterval = 700; // 子彈之間的間隔
Direction player_direction = UP;
struct Bullet {
int x, y;
bool active;
Direction direction;
} bullets[5]; // 最多5顆子彈
void setup() {
Serial.begin(9600);
tft.begin();
tft.setRotation(0); // 旋转屏幕方向为直向
pinMode(JOY_BTN, INPUT_PULLUP);
// 顯示初始畫面
tft.fillScreen(ILI9341_BLACK); // 清空屏幕
tft.setCursor(20, 40); // 設置文字位置
tft.setTextSize(2); // 設置文字大小
tft.setTextColor(ILI9341_WHITE); // 設置文字顏色
tft.println("Tank Battle"); // 顯示遊戲標題
// 顯示最高分數
tft.setCursor(20, 80);
tft.println("Highest Score: " + String(highestScore));
// 顯示難易度選單
tft.setCursor(20, 120);
tft.println("Select Difficulty:");
tft.setCursor(40, 160);
tft.println("Easy");
tft.setCursor(40, 200);
tft.println("Normal");
tft.setCursor(40, 240);
tft.println("Hard");
int arrowY = 160; // 初始化箭頭位置
int difficulty = 1; // 難易度變數 預設為1 簡單模式
// 等待玩家選擇難易度
while (true) {
int joy_y = analogRead(JOY_Y);
if (joy_y < 400 and difficulty > 1) {
difficulty--;
}
if (joy_y > 600 and difficulty < 3) {
difficulty++;
}
if (difficulty == 1) {
arrowY=160;
}
if (difficulty == 2) {
arrowY=200;
}
if (difficulty == 3) {
arrowY=240;
}
// 清除之前的箭頭
tft.fillRect(20, 160, 20, 120, ILI9341_BLACK);
// 繪製新的箭頭
tft.setCursor(20, arrowY);
tft.print(">");
delay(100);
if (digitalRead(JOY_BTN) == LOW) {
break;
}
}
while (digitalRead(JOY_BTN) == HIGH) {
delay(100);
}
// 讀取EEPROM中的最高分數
highestScore = EEPROM.read(HIGHEST_SCORE_ADDRESS);
// 如果最高分數是初始值255,則將其初始化為0
if (highestScore == 255) {
highestScore = 0;
EEPROM.write(HIGHEST_SCORE_ADDRESS, highestScore);
}
// 清空屏幕準備顯示難易度
tft.fillScreen(ILI9341_BLACK);
// 顯示當前的難易度,並根據難易度調整敵方坦克數值
int enemy_speed = 0;
tft.setCursor(40, 100);
tft.println("gamemode: ");
if (difficulty == 1) {
tft.setCursor(40, 160);
tft.println("Easy");
enemy_speed = 3;
}
if (difficulty == 2) {
tft.setCursor(40, 160);
tft.println("Normal");
enemy_speed = 5;
}
if (difficulty == 3) {
tft.setCursor(40, 160);
tft.println("hard");
enemy_speed = 7;
}
delay(2000);
// 清空屏幕準備開始遊戲
tft.fillScreen(ILI9341_BLACK);
// 將分數重置為零
score = 0;
// 初始玩家坦克位置和方向
player.x = tft.width() / 3 - TANK_SIZE / 2;
player.y = tft.height() - BASE_SIZE;
player.speed = 4; // 坦克速度
player_direction = UP;
// 隱藏原先的敵方坦克
tft.fillRect(0, 0, TANK_SIZE, TANK_SIZE, ILI9341_BLACK);
tft.fillRect(tft.width() - TANK_SIZE, 0, TANK_SIZE, TANK_SIZE, ILI9341_BLACK);
// 顯示複製的敵方坦克
drawTank(0, 35, DOWN, ILI9341_RED); // 左上角
drawTank(tft.width() - TANK_SIZE, 35, DOWN, ILI9341_RED); // 右上角
for (int i = 0; i < 5; i++) {
bullets[i].active = false;
}
drawTank(player.x, player.y, player_direction, ILI9341_WHITE); // 繪製初始玩家坦克
// 初始化我方基地位置
playerBase.x = tft.width() / 2 - BASE_SIZE / 2;
playerBase.y = tft.height() - BASE_SIZE;
// 初始化兩輛敵方坦克的位置和速度
enemyTanks[0].x = 0; // 左上角
enemyTanks[0].y = 35;
enemyTanks[0].speed = enemy_speed;
enemyTanks[1].x = tft.width() - TANK_SIZE; // 右上角
enemyTanks[1].y = 35;
enemyTanks[1].speed = enemy_speed;
for (int i = 0; i < 2; i++) {
enemyTanks[i].hitTimer = 0; // 初始化敵方坦克的計時器
}
}
void drawBase() {
tft.fillRect(playerBase.x, playerBase.y, BASE_SIZE, BASE_SIZE, BASE_COLOR);
}
void loop() {
updateScore();
int joy_x = analogRead(JOY_X);
int joy_y = analogRead(JOY_Y);
// 檢測按鈕狀態並發射子彈
if (digitalRead(JOY_BTN) == LOW && millis() - lastBulletTime > bulletInterval) {
fire(player_direction);
lastBulletTime = millis();
}
eraseTank(player.x, player.y, player_direction); // 擦除之前的玩家坦克
movePlayer(joy_x, joy_y);
drawTank(player.x, player.y, player_direction, ILI9341_WHITE); // 繪製新的玩家坦克
moveBullets();
drawBullets();
drawBase(); // 繪製我方基地
// 檢查敵方坦克是否碰撞到我方基地
for (int i = 0; i < 2; i++) {
if (checkTankBaseCollision(enemyTanks[i], playerBase)) {
// 如果敵方坦克碰撞到我方基地,結束遊戲
gameEnd();
return; // 結束遊戲迴圈
}
}
// 移動兩輛敵方坦克朝向我方基地
for (int i = 0; i < 2; i++) {
if (enemyTanks[i].hitTimer == 0) { // 確保計時器為零才移動敵方坦克
eraseTank(enemyTanks[i].x, enemyTanks[i].y, enemyTanks[i].direction); // 清除上一個位置的敵方坦克
moveEnemyTank(enemyTanks[i], i); // 移動敵方坦克
}
}
// 檢查子彈是否擊中敵方坦克
for (int i = 0; i < 5; i++) {
for (int j = 0; j < 2; j++) {
if (bullets[i].active && checkBulletTankCollision(bullets[i], enemyTanks[j], j)) {
bullets[i].active = false; // 子彈擊中,設置為非活躍狀態
eraseBullet(bullets[i].x, bullets[i].y); // 清除子彈
// 清除擊中的敵方坦克
eraseTank(enemyTanks[j].x, enemyTanks[j].y, enemyTanks[j].direction);
// 增加玩家分數
score += 1; // 假設擊中一輛敵方坦克得1分
// 在這裡可以添加擊中敵方坦克後的其他處理代碼
}
}
}
// 檢查敵方坦克是否被擊中後需要停留
for (int i = 0; i < 2; i++) {
if (enemyTanks[i].hitTimer > 0) {
enemyTanks[i].hitTimer -= 50; // 每次迴圈減少50毫秒
if (enemyTanks[i].hitTimer <= 0) {
// 計時器歸零後,重新移動敵方坦克
moveEnemyTank(enemyTanks[i], i);
}
}
}
// 檢查每輛敵方坦克的hitTimer,如果大於零,則顯示被擊中的狀態
for (int i = 0; i < 2; i++) {
if (enemyTanks[i].hitTimer > 0) {
tft.setCursor(enemyTanks[i].x, enemyTanks[i].y - 10); // 設置文字位置
tft.setTextSize(1); // 設置文字大小
tft.setTextColor(ILI9341_RED); // 設置文字顏色
tft.print("HIT"); // 顯示"HIT"
} else {
// 如果hitTimer計時完畢,則清除整個"HIT"提示
tft.fillRect(enemyTanks[i].x, enemyTanks[i].y - 10, 20, 8, ILI9341_BLACK);
}
}
updateScore(); // 繪製玩家分數
delay(50);
}
void updateScore() {
// 只有在分數發生變化時才更新顯示,以避免閃爍
static int lastScore = -1;
if (score != lastScore) {
// 清除之前的分數
tft.fillRect( 10, 10, 100, 40, ILI9341_BLACK);
// 顯示新的分數
tft.setCursor(10, 10);
tft.setTextColor(ILI9341_WHITE);
tft.setTextSize(2);
tft.print("Score:");
tft.println(score);
lastScore = score;
}
}
void movePlayer(int x, int y) {
eraseTank(player.x, player.y, player_direction); // 擦除之前的玩家坦克
if (x < 400) {
player_direction = RIGHT; // 將左移改為右移
player.x = constrain(player.x + player.speed, 0, tft.width() - TANK_SIZE); // 向右移動
} else if (x > 600) {
player_direction = LEFT; // 將右移改為左移
player.x = constrain(player.x - player.speed, 0, tft.width() - TANK_SIZE); // 向左移動
}
if (y < 400) {
player_direction = UP; // 上移保持不變
player.y = constrain(player.y - player.speed, 20, height - TANK_SIZE);
} else if (y > 600) {
player_direction = DOWN; // 下移保持不變
player.y = constrain(player.y + player.speed, 0, height - TANK_SIZE);
}
drawTank(player.x, player.y, player_direction, ILI9341_WHITE); // 繪製新的玩家坦克
}
void drawTank(int x, int y, Direction dir, uint16_t color) {
switch (dir) {
case UP:
tft.drawRect(x + 4, y + 4, 8, 16, color); // Tank body
tft.drawRect(x + 2, y + 8, 12, 8, color); // Tank body
tft.drawRect(x + 6, y, 4, 4, color); // Turret
tft.drawRect(x + 5, y + 1, 6, 2, color); // Turret
break;
case DOWN:
tft.drawRect(x + 4, y, 8, 16, color); // Tank body
tft.drawRect(x + 2, y + 4, 12, 8, color); // Tank body
tft.drawRect(x + 6, y + 16, 4, 4, color); // Turret
tft.drawRect(x + 5, y + 15, 6, 2, color); // Turret
break;
case LEFT:
tft.drawRect(x + 4, y + 4, 16, 8, color); // Tank body
tft.drawRect(x + 8, y + 2, 8, 12, color); // Tank body
tft.drawRect(x, y + 6, 4, 4, color); // Turret
tft.drawRect(x + 1, y + 5, 2, 6, color); // Turret
break;
case RIGHT:
tft.drawRect(x, y + 4, 16, 8, color); // Tank body
tft.drawRect(x + 4, y + 2, 8, 12, color); // Tank body
tft.drawRect(x + 16, y + 6, 4, 4, color); // Turret
tft.drawRect(x + 15, y + 5, 2, 6, color); // Turret
break;
}
}
void eraseTank(int x, int y, Direction dir) {
switch (dir) {
case UP:
tft.drawRect(x + 4, y + 4, 8, 16, ILI9341_BLACK); // Tank body
tft.drawRect(x + 2, y + 8, 12, 8, ILI9341_BLACK); // Tank body
tft.drawRect(x + 6, y, 4, 4, ILI9341_BLACK); // Turret
tft.drawRect(x + 5, y + 1, 6, 2, ILI9341_BLACK); // Turret
break;
case DOWN:
tft.drawRect(x + 4, y, 8, 16, ILI9341_BLACK); // Tank body
tft.drawRect(x + 2, y + 4, 12, 8, ILI9341_BLACK); // Tank body
tft.drawRect(x + 6, y + 16, 4, 4, ILI9341_BLACK); // Turret
tft.drawRect(x + 5, y + 15, 6, 2, ILI9341_BLACK); // Turret
break;
case LEFT:
tft.drawRect(x + 4, y + 4, 16, 8, ILI9341_BLACK); // Tank body
tft.drawRect(x + 8, y + 2, 8, 12, ILI9341_BLACK); // Tank body
tft.drawRect(x, y + 6, 4, 4, ILI9341_BLACK); // Turret
tft.drawRect(x + 1, y + 5, 2, 6, ILI9341_BLACK); // Turret
break;
case RIGHT:
tft.drawRect(x, y + 4, 16, 8, ILI9341_BLACK); // Tank body
tft.drawRect(x + 4, y + 2, 8, 12, ILI9341_BLACK); // Tank body
tft.drawRect(x + 16, y + 6, 4, 4, ILI9341_BLACK); // Turret
tft.drawRect(x + 15, y + 5, 2, 6, ILI9341_BLACK); // Turret
break;
}
}
void fire(Direction dir) {
for (int i = 0; i < 5; i++) {
if (!bullets[i].active) {
bullets[i].active = true;
bullets[i].x = player.x + TANK_SIZE / 2;
bullets[i].y = player.y + TANK_SIZE / 2;
bullets[i].direction = dir;
return;
}
}
}
void moveBullets() {
for (int i = 0; i < 5; i++) {
if (bullets[i].active) {
eraseBullet(bullets[i].x, bullets[i].y); // 擦除之前的子彈
switch (bullets[i].direction) {
case UP:
bullets[i].y = max(bullets[i].y - BULLET_SPEED, 0);
break;
case DOWN:
bullets[i].y = min(bullets[i].y + BULLET_SPEED, tft.height());
break;
case LEFT:
bullets[i].x = max(bullets[i].x - BULLET_SPEED, 0);
break;
case RIGHT:
bullets[i].x = min(bullets[i].x + BULLET_SPEED, tft.width());
break;
}
if (bullets[i].x <= 0 || bullets[i].x >= tft.width() || bullets[i].y <= 0 || bullets[i].y >= tft.height()) {
bullets[i].active = false;
}
}
}
}
void drawBullets() {
for (int i = 0; i < 5; i++) {
if (bullets[i].active) {
tft.fillRect(bullets[i].x, bullets[i].y, BULLET_SIZE, BULLET_SIZE, ILI9341_WHITE);
}
}
}
void eraseBullet(int x, int y) {
tft.fillRect(x, y, BULLET_SIZE, BULLET_SIZE, ILI9341_BLACK); // 使用黑色填充子彈
}
void moveEnemyTank(Tank &tank, int tankIndex) {
unsigned long currentTime = millis();
// 如果已經超過了方向改變的時間間隔,則重新選擇方向
if (currentTime - lastDirectionChangeTime[tankIndex] > directionChangeDelay) {
// 隨機生成移動方向
int randomDirection = random(4);
switch (randomDirection) {
case 0: // 上
tank.direction = UP;
break;
case 1: // 下
tank.direction = DOWN;
break;
case 2: // 左
tank.direction = LEFT;
break;
case 3: // 右
tank.direction = RIGHT;
break;
}
lastDirectionChangeTime[tankIndex] = currentTime; // 更新方向改變時間
}
// 根據方向移動坦克
switch (tank.direction) {
case UP:
tank.y = max(tank.y - tank.speed, 35);
break;
case DOWN:
tank.y = min(tank.y + tank.speed, tft.height() - TANK_SIZE);
break;
case LEFT:
tank.x = max(tank.x - tank.speed, 0);
break;
case RIGHT:
tank.x = min(tank.x + tank.speed, tft.width() - TANK_SIZE);
break;
}
// 繪製新的敵方坦克
drawTank(tank.x, tank.y, tank.direction, ILI9341_RED);
}
bool checkBulletTankCollision(Bullet bullet, Tank tank, int tankIndex) {
// 子彈的碰撞框
int bulletLeft = bullet.x;
int bulletRight = bullet.x + BULLET_SIZE;
int bulletTop = bullet.y;
int bulletBottom = bullet.y + BULLET_SIZE;
// 敵方坦克的碰撞框
int tankLeft = tank.x;
int tankRight = tank.x + TANK_SIZE;
int tankTop = tank.y;
int tankBottom = tank.y + TANK_SIZE;
// 檢查碰撞
if (bulletRight >= tankLeft && bulletLeft <= tankRight && bulletBottom >= tankTop && bulletTop <= tankBottom) {
if (enemyTanks[tankIndex].hitTimer == 0) { // 確認敵方坦克不在被擊中狀態
enemyTanks[tankIndex].hitTimer = 2000; // 設置被擊中計時器
return true; // 子彈擊中敵方坦克
} else {
return false; // 子彈未擊中敵方坦克,因為敵方坦克已經被擊中
}
} else {
return false; // 子彈未擊中敵方坦克
}
}
void removeEnemyTank(int tankIndex) {
// 將敵方坦克從敵方坦克陣列中移除
for (int i = tankIndex; i < 1; i++) {
enemyTanks[i] = enemyTanks[i + 1];
}
// 清除最後一個元素
enemyTanks[1] = {};
}
// 檢查坦克是否碰撞到基地
bool checkTankBaseCollision(Tank tank, Base base) {
// 坦克的碰撞框
int tankLeft = tank.x;
int tankRight = tank.x + TANK_SIZE;
int tankTop = tank.y;
int tankBottom = tank.y + TANK_SIZE;
// 基地的碰撞框
int baseLeft = base.x;
int baseRight = base.x + BASE_SIZE;
int baseTop = base.y;
int baseBottom = base.y + BASE_SIZE;
// 檢查碰撞
if (tankRight >= baseLeft && tankLeft <= baseRight && tankBottom >= baseTop && tankTop <= baseBottom) {
return true; // 坦克碰撞到基地
} else {
return false; // 坦克未碰撞到基地
}
}
// 遊戲結束
void gameEnd() {
// 在這裡添加遊戲結束的處理,例如顯示遊戲結束畫面、停止遊戲迴圈等。
// 你可以根據需求自定義遊戲結束的行為。
tft.fillScreen(ILI9341_BLACK); // 清空屏幕
tft.setCursor(20, 40); // 設置文字位置
tft.setTextSize(2); // 設置文字大小
tft.setTextColor(ILI9341_WHITE); // 設置文字顏色
tft.println("Game Over"); // 顯示遊戲結束消息
tft.setCursor(20, 80);
tft.println("Your Score: " + String(score)); // 顯示玩家得分
// 更新最高分數
if (score > highestScore) {
highestScore = score;
// 將最高分數寫入EEPROM
EEPROM.write(HIGHEST_SCORE_ADDRESS, highestScore);
}
// 顯示最高分數
tft.setCursor(20, 120);
tft.println("Highest Score: " + String(highestScore));
// 等待玩家按下按鈕回到初始畫面
tft.setCursor(20, 160);
tft.println("Press Button to ");
tft.println(" ");
tft.println(" Restart ");
// 等待按下按鈕
while (digitalRead(JOY_BTN) == HIGH) {
delay(100);
}
// 返回初始畫面
setup();
}