PongSpiel/VSCode/main.cpp
2022-02-08 17:42:36 +00:00

302 lines
No EOL
6.2 KiB
C++

#include <Arduino.h>
/**
* Player 1: x = 0
* Player 2: x = X_MAX-1
*/
#define PIN_LED_MATRIX 2
#define PIN_JOYSTICK_1_BUTTON 6
#define PIN_JOYSTICK_2_BUTTON 7
#define PIN_PIEZO 11
#define PIN_JOYSTICK_1_Y A0
#define PIN_JOYSTICK_2_Y A1
#define JOYSTICK_OFFSET_MIN 200
#define JOYSTICK_OFFSET_MAX 700
#define DEBOUNCE_TIME 300 // in ms
#define X_MAX 10
#define Y_MAX 20
#define GAME_DELAY 80 // in ms
#define BALL_DELAY_MAX 350 // in ms
#define BALL_DELAY_MIN 50 // in ms
#define BALL_DELAY_STEP 5 // in ms
#define PLAYER_AMOUNT 2
#define PLAYER_1 0
#define PLAYER_2 1
#define PADDLE_WIDTH 3
#define PADDLE_MOVE_NONE 0
#define PADDLE_MOVE_UP 1
#define PADDLE_MOVE_DOWN 2
#define LED_TYPE_OFF 1
#define LED_TYPE_PADDLE 2
#define LED_TYPE_BALL 3
#define LED_TYPE_BALL_RED 4
#define TONE_PLAYER 1
#define TONE_WALL 2
#define TONE_BUZZ 3
#define GAME_STATE_RUNNING 1
#define GAME_STATE_END 2
#define GAME_STATE_INIT 3
#include <Adafruit_NeoPixel.h>
struct Coords {
byte x;
byte y;
};
Adafruit_NeoPixel pixels = Adafruit_NeoPixel(X_MAX * Y_MAX, PIN_LED_MATRIX, NEO_GRB + NEO_KHZ800);
bool buttonPressed = false;
byte gameState;
byte joystickPins[PLAYER_AMOUNT] = {PIN_JOYSTICK_1_Y, PIN_JOYSTICK_2_Y};
Coords paddles[PLAYER_AMOUNT][PADDLE_WIDTH];
Coords ball;
int ballMovement[2];
unsigned int ballDelay;
unsigned long lastDrawUpdate = 0;
unsigned long lastBallUpdate = 0;
unsigned long lastButtonClick = 0;
void setup()
{
pinMode(PIN_JOYSTICK_1_Y, INPUT);
pinMode(PIN_JOYSTICK_1_BUTTON, INPUT_PULLUP);
pinMode(PIN_JOYSTICK_2_Y, INPUT);
pinMode(PIN_JOYSTICK_2_BUTTON, INPUT_PULLUP);
pixels.begin();
resetLEDs();
gameState = GAME_STATE_END;
}
void loop()
{
switch(gameState) {
case GAME_STATE_INIT:
initGame();
break;
case GAME_STATE_RUNNING:
updateBall();
updateGame();
break;
case GAME_STATE_END:
if (isButtonPressed()) {
gameState = GAME_STATE_INIT;
}
break;
}
}
void initGame()
{
resetLEDs();
lastButtonClick = millis();
ball.x = 1;
ball.y = (Y_MAX/2) - (PADDLE_WIDTH/2) + 1;
ballMovement[0] = 1;
ballMovement[1] = -1;
ballDelay = BALL_DELAY_MAX;
for(byte i=0; i<PADDLE_WIDTH; i++) {
paddles[PLAYER_1][i].x = 0;
paddles[PLAYER_1][i].y = (Y_MAX/2) - (PADDLE_WIDTH/2) + i;
paddles[PLAYER_2][i].x = X_MAX - 1;
paddles[PLAYER_2][i].y = paddles[PLAYER_1][i].y;
}
gameState = GAME_STATE_RUNNING;
}
void updateBall()
{
bool hitBall = false;
if ((millis() - lastBallUpdate) < ballDelay) {
return;
}
lastBallUpdate = millis();
toggleLed(ball.x, ball.y, LED_TYPE_OFF);
// collision detection for player 1
if (ballMovement[0] == -1 && ball.x == 1) {
for(byte i=0; i<PADDLE_WIDTH; i++) {
if (paddles[PLAYER_1][i].y == ball.y) {
hitBall = true;
break;
}
}
}
// collision detection for player 2
if (ballMovement[0] == 1 && ball.x == X_MAX-2) {
for(byte i=0; i<PADDLE_WIDTH; i++) {
if (paddles[PLAYER_2][i].y == ball.y) {
hitBall = true;
break;
}
}
}
if (hitBall == true) {
ballMovement[0] *= -1;
playTone(TONE_PLAYER);
if (ballDelay > BALL_DELAY_MIN) {
ballDelay -= BALL_DELAY_STEP;
}
}
ball.x += ballMovement[0];
ball.y += ballMovement[1];
if (ball.x <=0 || ball.x >= X_MAX-1) {
endGame();
return;
}
if (ball.y <= 0 || ball.y >= Y_MAX-1) {
ballMovement[1] *= -1;
playTone(TONE_WALL);
}
toggleLed(ball.x, ball.y, LED_TYPE_BALL);
pixels.show();
}
void endGame()
{
gameState = GAME_STATE_END;
toggleLed(ball.x, ball.y, LED_TYPE_BALL_RED);
pixels.show();
playTone(TONE_BUZZ);
}
void updateGame()
{
if ((millis() - lastDrawUpdate) < GAME_DELAY) {
return;
}
lastDrawUpdate = millis();
// turn off paddle LEDs
for(byte p=0; p<PLAYER_AMOUNT; p++) {
for(byte i=0; i<PADDLE_WIDTH; i++) {
toggleLed(paddles[p][i].x, paddles[p][i].y, LED_TYPE_OFF);
}
}
// move paddles
for(byte p=0; p<PLAYER_AMOUNT; p++) {
byte movement = getPlayerMovement(p);
if (movement == PADDLE_MOVE_UP && paddles[p][PADDLE_WIDTH-1].y < (Y_MAX-1)) {
for(byte i=0; i<PADDLE_WIDTH; i++) {
paddles[p][i].y++;
}
}
if (movement == PADDLE_MOVE_DOWN && paddles[p][0].y > 0) {
for(byte i=0; i<PADDLE_WIDTH; i++) {
paddles[p][i].y--;
}
}
}
// show paddle LEDs
for(byte p=0; p<PLAYER_AMOUNT; p++) {
for(byte i=0; i<PADDLE_WIDTH; i++) {
toggleLed(paddles[p][i].x, paddles[p][i].y, LED_TYPE_PADDLE);
}
}
pixels.show();
}
byte getPlayerMovement(byte playerId)
{
int value = analogRead(joystickPins[playerId]);
if (value < JOYSTICK_OFFSET_MIN) {
if (playerId == PLAYER_2) {
return PADDLE_MOVE_DOWN;
} else {
return PADDLE_MOVE_UP;
}
} else if (value > JOYSTICK_OFFSET_MAX) {
if (playerId == PLAYER_2) {
return PADDLE_MOVE_UP;
} else {
return PADDLE_MOVE_DOWN;
}
}
return PADDLE_MOVE_NONE;
}
bool isButtonPressed()
{
if ((millis() - lastButtonClick) < DEBOUNCE_TIME) {
return false;
}
if (digitalRead(PIN_JOYSTICK_1_BUTTON) == LOW) {
lastButtonClick = millis();
return true;
}
if (digitalRead(PIN_JOYSTICK_2_BUTTON) == LOW) {
lastButtonClick = millis();
return true;
}
return false;
}
void resetLEDs()
{
for(byte i=0; i<X_MAX*Y_MAX; i++) {
pixels.setPixelColor(i, pixels.Color(0, 0, 0));
}
pixels.show();
}
void toggleLed(byte x, byte y, byte type)
{
byte ledIndex = y * X_MAX + x;
uint32_t color;
switch(type) {
case LED_TYPE_PADDLE:
color = pixels.Color(0, 8, 8);
break;
case LED_TYPE_BALL_RED:
color = pixels.Color(12, 0, 0);
break;
case LED_TYPE_BALL:
color = pixels.Color(0, 10, 0);
break;
case LED_TYPE_OFF:
color = pixels.Color(0, 0, 0);
break;
}
pixels.setPixelColor(ledIndex, color);
}
void playTone(byte type)
{
switch(type) {
case TONE_PLAYER:
tone(PIN_PIEZO, 440, 50);
break;
case TONE_WALL:
tone(PIN_PIEZO, 550, 50);
break;
case TONE_BUZZ:
for(byte i=0; i<20; i++) {
tone(PIN_PIEZO, 220-i*10, 50);
delay(50);
}
break;
}
}