Gra na arduino Statek kosmiczny.
[przykładowe - prosimy o edycję]
1. Płytka Arduino UNO
2. 3 przyciski,
3. ekranik OLED 0.96''
Gra statki kosmiczne nawiązuje mechaniką do gry z starych konsol w której należy unikać spadających asteroid statkiem kosmicznym. Do dyspozycji statek ma pociski którymi możemy zniszczyć asteroidy, w magazynku możemy mieć trzy serie pocisków (każda seria to dwa pociski, jeden wylatujący z lewej drugi z prawej strony statku) i mają z góry narzucony czas odnawiania. Sterowanie statkiem i strzelanie kontrolowane jest przez 3 przyciski. Statek porusza się tylko po poziomej osi. Gracz przegrywa gdy statek zderzy się z asteroidą. Liczba asteroid rośnie wraz z biegiem czasu, na początku jest ich 8 i co każde 10 sekund dodaje się kolejna. Asteroidy mają losowo generowany rozmiar, prędkość i miejsce z którego wylatują.
Projekt stworzony przy pomocy AI.
#include <SPI.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
#define OLED_RESET -1
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
// Define triangle vertices (upside-down)
int triangleTopX = SCREEN_WIDTH / 2;
int triangleTopY = 50; // Moved to the top
int triangleBaseX1 = triangleTopX - 5;
int triangleBaseX2 = triangleTopX + 5;
int triangleBaseY = triangleTopY + 10; // Adjusted to maintain triangle size
// Define button pins
const int leftButtonPin = 10;
const int rightButtonPin = 8;
const int spawnButtonPin = 7;
// Define dot structure
struct Dot {
int x;
int y;
float speed; // Change speed type to float for better precision
int size; // Size of the dot
};
// Maximum and minimum number of dots
const int minDots = 7;
const int maxDots = 20;
// Array to store dots
Dot dots[maxDots];
// Rectangle properties
int rectWidth = 2; // Width of the rectangle
int rectHeight = 6; // Height of the rectangle
struct Rectangle {
int x; // X position of the rectangle
int y; // Y position of the rectangle
bool spawned; // Flag to indicate if the rectangle has been spawned
};
const int maxRectangles = 6; // Maximum number of rectangles
Rectangle rectangles[maxRectangles]; // Array to store rectangles
// Flag to indicate game over
bool gameOver = false;
// Counter for the number of dots
int dotCounter = 0;
unsigned long previousDotUpdateMillis = 0;
unsigned long previousCollisionCheckMillis = 0;
const unsigned long dotUpdateInterval = 100; // Update dot positions every 100 milliseconds
const unsigned long collisionCheckInterval = 1000; // Check for collisions every 1000 milliseconds
// Global variable to store the elapsed time in seconds
unsigned long previousTimeUpdateMillis = 0;
int czas = 0;
void setup() {
// Setup OLED display
if (!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) {
Serial.println(F("SSD1306 allocation failed"));
for (;;);
}
Serial.begin(9600);
display.setRotation(2); // Rotate the display upside-down
display.display();
delay(500); // Pause for 1 second before dots start spawning
// Clear the display
display.clearDisplay();
// Setup buttons
pinMode(leftButtonPin, INPUT_PULLUP);
pinMode(rightButtonPin, INPUT_PULLUP);
pinMode(spawnButtonPin, INPUT_PULLUP);
// Initialize dots after the initial delay
initializeDots();
}
// Define button 7 properties
const int button7Pin = 7;
int availablePresses = 3; // Initially, button 7 can be pressed 3 times
void loop() {
unsigned long currentMillis = millis();
if (!gameOver) {
// Check if left button is pressed
if (digitalRead(leftButtonPin) == LOW) {
// Move triangle to the left (1.5 times faster)
triangleTopX -= 4;
triangleBaseX1 -= 4;
triangleBaseX2 -= 4;
// Wrap triangle position around when it reaches the left edge
if (triangleTopX < 0) {
triangleTopX = SCREEN_WIDTH - 1;
triangleBaseX1 = triangleTopX - 5;
triangleBaseX2 = triangleTopX + 5;
}
}
// Check if right button is pressed
if (digitalRead(rightButtonPin) == LOW) {
// Move triangle to the right (1.5 times faster)
triangleTopX += 4;
triangleBaseX1 += 4;
triangleBaseX2 += 4;
// Wrap triangle position around when it reaches the right edge
if (triangleTopX >= SCREEN_WIDTH) {
triangleTopX = 0;
triangleBaseX1 = triangleTopX - 5;
triangleBaseX2 = triangleTopX + 5;
}
}
// Check if spawn button is pressed
if (digitalRead(spawnButtonPin) == LOW && availablePresses > 0) {
// Find an available slot for the rectangle
for (int i = 0; i < maxRectangles; i++) {
if (!rectangles[i].spawned) {
// Spawn rectangle
rectangles[i].x = triangleBaseX1; // Spawn at the side of the screen where the triangle is
rectangles[i].y = triangleBaseY; // Move the spawn point 5 pixels to the top
rectangles[i].spawned = true;
i = i + 1;
rectangles[i].x = triangleBaseX2; // Spawn at the side of the screen where the triangle is
rectangles[i].y = triangleBaseY; // Move the spawn point 5 pixels to the top
rectangles[i].spawned = true;
availablePresses--; // Decrease available presses
break; // Exit loop after spawning a rectangle
}
}
}
// Update dots at regular intervals
if (currentMillis - previousDotUpdateMillis >= dotUpdateInterval) {
updateDots();
previousDotUpdateMillis = currentMillis;
}
// Clear display
display.clearDisplay();
// Draw upside-down triangle
display.fillTriangle(triangleTopX, triangleTopY,
triangleBaseX1, triangleBaseY,
triangleBaseX2, triangleBaseY, SSD1306_WHITE);
// Draw rectangles
for (int i = 0; i < maxRectangles; i++) {
if (rectangles[i].spawned) {
display.fillRect(rectangles[i].x, rectangles[i].y, rectWidth, rectHeight, SSD1306_WHITE);
// Move rectangle
rectangles[i].y -= 6; // Move the rectangle upwards at three times the speed
// Check if rectangle reaches the top of the screen
if (rectangles[i].y <= 0) {
rectangles[i].spawned = false; // Reset rectangle spawn flag
}
}
}
// Check for collision between dots and rectangles
for (int i = 0; i < maxDots; i++) {
if (dots[i].speed != 0) {
for (int j = 0; j < maxRectangles; j++) {
if (rectangles[j].spawned) {
if (dotRectangleCollision(dots[i].x, dots[i].y, dots[i].size, rectangles[j].x, rectangles[j].y, rectWidth, rectHeight)) {
dots[i].speed = 0; // Set dot's speed to zero
rectangles[j].spawned = false; // Reset rectangle spawn flag
spawnDot(); // Spawn a new dot
break; // Exit the loop once a collision is detected for this dot
}
}
}
}
}
// Draw dots
dotCounter = 0; // Reset dot counter
for (int i = 0; i < maxDots; i++) {
if (dots[i].speed != 0) { // Only draw dots that are initialized
int dotSize = dots[i].size; // Get the size of the dot
display.fillCircle(dots[i].x, dots[i].y, dotSize, SSD1306_WHITE);
dotCounter++; // Increment dot counter
}
}
// Display dot counter
display.setTextSize(1);
display.setTextColor(SSD1306_WHITE);
display.setCursor(0, 15); // Set cursor to top-left corner
display.print("Dots: ");
display.println(dotCounter);
// Display available button 7 presses as small squares
int squareSize = 6; // Size of each square
for (int i = 0; i < availablePresses; i++) {
// Calculate position of each square
int squareX = SCREEN_WIDTH / 2 - 60 + i * (squareSize + 2); // Adjusted position for the squares
int squareY = 55; // Adjusted Y position
// Draw the square
display.fillRect(squareX, squareY, squareSize, squareSize, SSD1306_WHITE);
}
// Display time in seconds
display.setTextSize(1);
display.setTextColor(SSD1306_WHITE);
display.setCursor(0,0); // Adjust position for the counter
display.print("Time: ");
display.print(czas);
} else {
// Display "Game Over" when the game is over
display.setTextSize(2);
display.setTextColor(SSD1306_WHITE);
display.setCursor(10, 20);
display.println("Game Over");
// Show display buffer
display.display();
// Stop the program
while (true) {
// Empty loop
}
}
// Show display buffer
display.display();
// Update the time value every second
if (currentMillis - previousTimeUpdateMillis >= 1000) {
czas++; // Increment the time value
previousTimeUpdateMillis = currentMillis; // Update the previous update time
// Increase available presses for button 7 every 5 seconds up to the maximum limit
if (czas % 5 == 0 && availablePresses < 3) {
availablePresses++;
}
// Increase dot count every 10 seconds
if (czas % 10 == 0) {
increaseDots();
}
}
// Delay for visual effect
delay(100);
}
// Initialize dots
void initializeDots() {
int numDots = random(minDots, 8 + 1); // Generate a random number of dots between minDots and maxDots
for (int i = 0; i < numDots; i++) {
if (i < numDots) {
dots[i].x = random(0, SCREEN_WIDTH);
dots[i].y = SCREEN_HEIGHT; // Start dots from the bottom of the screen
dots[i].speed = random(3.0, 6.0); // Randomize speed between 1 and 2 times faster
dots[i].size = random(2, 7); // Randomize dot size (2 or 3 times bigger)
} else {
// Set the remaining dots outside the screen to avoid drawing them
dots[i].x = -1;
dots[i].y = -1;
dots[i].speed = 0;
dots[i].size = 0;
}
}
}
// Update dot positions
void updateDots() {
for (int i = 0; i < maxDots; i++) {
if (dots[i].speed != 0) {
// Move dots towards the bottom edge
dots[i].y += dots[i].speed;
// Wrap dot position around when it reaches the bottom edge
if (dots[i].y >= SCREEN_HEIGHT + 10) {
dots[i].y = 0;
// Respawn dot at a random position at the top edge
dots[i].x = random(0, SCREEN_WIDTH);
dots[i].speed = random(3.0, 6.0); // Randomize speed between 1 and 2 times faster
dots[i].size = random(2, 7); // Randomize dot size (2 or 3 times bigger)
}
// Check for collision with the triangle
if (triangleCollision(triangleTopX, triangleTopY, triangleBaseX1, triangleBaseX2, triangleBaseY, dots[i].x, dots[i].y, dots[i].size)) {
gameOver = true; // Collision detected, set game over flag
break; // Exit the loop early
}
}
}
}
// Function to check collision between triangle and dot
bool triangleCollision(int tx, int ty, int bx1, int bx2, int by, int dx, int dy, int dotSize) {
// Calculate the coordinates of the vertices of the triangle
int t1x = tx;
int t1y = ty;
int t2x = bx1;
int t2y = by;
int t3x = bx2;
int t3y = by;
// Calculate the coordinates of the center of the dot
int cx = dx;
int cy = dy;
// Check if any edge of the dot intersects with any edge of the triangle
if (lineIntersect(t1x, t1y, t2x, t2y, cx, cy, dotSize) ||
lineIntersect(t2x, t2y, t3x, t3y, cx, cy, dotSize) ||
lineIntersect(t3x, t3y, t1x, t1y, cx, cy, dotSize)) {
return true; // Collision detected
}
return false; // No collision
}
// Function to check if a line segment intersects with a circle
bool lineIntersect(int x1, int y1, int x2, int y2, int cx, int cy, int r) {
// Compute the squared distance from the circle center to the line segment
int dx = x2 - x1;
int dy = y2 - y1;
int len2 = dx * dx + dy * dy;
int dot = (cx - x1) * dx + (cy - y1) * dy;
float t = float(dot) / len2;
// Clamp t to the range [0, 1]
t = max(0.0f, min(1.0f, t));
// Compute the closest point on the line segment to the circle center
int closestX = x1 + int(t * dx);
int closestY = y1 + int(t * dy);
// Check if the closest point is within the circle's radius, considering the dot's size
int distance2 = (cx - closestX) * (cx - closestX) + (cy - closestY) * (cy - closestY);
return distance2 <= (r * r + 1); // Added 1 to account for small size differences
}
// Function to check collision between dot and rectangle
bool dotRectangleCollision(int dx, int dy, int dotSize, int rx, int ry, int rw, int rh) {
int margin = 2; // Margin around the dot
// Check if dot is inside the rectangle
if (dx + margin >= rx && dx - margin <= rx + rw && dy + margin >= ry && dy - margin <= ry + rh) {
return true; // Collision detected
}
// Check if dot is at the edge of the rectangle
if ((dx + dotSize + margin >= rx && dx + dotSize - margin <= rx + rw && dy + margin >= ry && dy - margin <= ry + rh) || // Right edge
(dx - dotSize + margin >= rx && dx - dotSize - margin <= rx + rw && dy + margin >= ry && dy - margin <= ry + rh) || // Left edge
(dx + margin >= rx && dx - margin <= rx + rw && dy + dotSize + margin >= ry && dy + dotSize - margin <= ry + rh) || // Bottom edge
(dx + margin >= rx && dx - margin <= rx + rw && dy - dotSize + margin >= ry && dy - dotSize - margin <= ry + rh)) { // Top edge
return true; // Collision detected
}
return false; // No collision
}
// Function to spawn a new dot
void spawnDot() {
// Find an available slot for the dot
for (int i = 0; i < maxDots; i++) {
if (dots[i].speed == 0) {
// Respawn dot at a random position at the top edge
dots[i].x = random(0, SCREEN_WIDTH);
dots[i].y = SCREEN_HEIGHT+10; // Start dot from the bottom of the screen
dots[i].speed = random(3.0, 6.0); // Randomize speed between 1 and 2 times faster
dots[i].size = random(2, 7); // Randomize dot size (2 or 3 times bigger)
break; // Exit loop after spawning a dot
}
}
}
// Increase the number of dots
void increaseDots() {
// Increase the number of dots if not already at maximum
if (dotCounter < maxDots) {
for (int i = 0; i < maxDots; i++) {
if (dots[i].speed == 0) {
// Respawn dot at a random position at the top edge
dots[i].x = random(0, SCREEN_WIDTH);
dots[i].y = SCREEN_HEIGHT+10; // Start dot from the bottom of the screen
dots[i].speed = random(3.0, 6.0); // Randomize speed between 1 and 2 times faster
dots[i].size = random(2, 7); // Randomize dot size (2 or 3 times bigger)
dotCounter++; // Increment dot counter
break; // Exit loop after spawning a dot
}
}
}