/*************************************************************************************
EZUBI | E-HACKS
ARDUINO-AUDIO-PONG
************************************************************************************
Arduino Pong
By Pete Lamonica
modified by duboisvb
updated by James Bruce (http://www.makeuseof.com/tag/author/jbruce
modified for audio input by Roland Batroff (http://www.kollektorbase.net/)
updated for audio input by David Ziegler (http://www.goleon.de/)
****************************************************************************************/
#include "TVout.h"
#include "TVoutfonts/fontALL.h"
#define WHEEL_ONE_PIN 0 // ANALOG AUDIO INPUT 1
#define WHEEL_TWO_PIN 1 // ANALOG AUDIO INPUT 2
#define BUTTON_ONE_PIN 2 // BUTTON PIN
#define PADDLE_HEIGHT 14
#define PADDLE_WIDTH 1
#define RIGHT_PADDLE_X (TV.hres()-4)
#define LEFT_PADDLE_X 2
#define IN_GAMEA 0 //in game state - draw constants of the game box
#define IN_GAMEB 0 //in game state - draw the dynamic part of the game
#define IN_MENU 1 //in menu state
#define GAME_OVER 2 //game over state
#define LEFT_SCORE_X (TV.hres()/2-15)
#define RIGHT_SCORE_X (TV.hres()/2+10)
#define SCORE_Y 4
#define MAX_Y_VELOCITY 6
#define PLAY_TO 7
#define LEFT 0
#define RIGHT 1
TVout TV;
unsigned char x, y;
/*************************************************************************************
* VARIABLES FOR AUDIO INPUT AND SAMPLING
*************************************************************************************/
const int sampleWindow = 50; // Sample window width in mS (50 mS = 20Hz)
unsigned long startMillis = millis(); // Start of sample window
unsigned int sample_1;
unsigned int sample_2;
unsigned int peakToPeak_1 = 0; // peak-to-peak level
unsigned int peakToPeak_2 = 0;
unsigned int signalMax_1 = 0;
unsigned int signalMax_2 = 0;
unsigned int signalMin_1 = 1024;
unsigned int signalMin_2 = 1024;
/*********************************************/
boolean button1Status = false;
int wheelOnePosition = 0;
int wheelTwoPosition = 0;
int rightPaddleY = 0;
int leftPaddleY = 0;
unsigned char ballX = 0;
unsigned char ballY = 0;
char ballVolX = 2;
char ballVolY = 2;
int leftPlayerScore = 0;
int rightPlayerScore = 0;
int frame = 0;
int state = IN_MENU;
int damperOne[5];
int damperTwo[5];
void processInputs() {
/*************************************************************************************
* AUDIO SAMPLING FOR BOTH MICROPHONES
*************************************************************************************/
// collect data for 50 mS
unsigned long startMillis = millis(); // Start of sample window
unsigned int signalMax_1 = 0;
unsigned int signalMin_1 = 512;
unsigned int signalMax_2 = 0;
unsigned int signalMin_2 = 512;
while (millis() - startMillis < sampleWindow) {
sample_1 = analogRead(0);
sample_2 = analogRead(1);
if (sample_1 < 1024) {
if (sample_1 > signalMax_1) {
signalMax_1 = sample_1; // save just the max levels
}
else if (sample_1 < signalMin_1) {
signalMin_1 = sample_1; // save just the min levels
}
}
if (sample_2 < 1024) {
if (sample_2 > signalMax_2) {
signalMax_2 = sample_2; // save just the max levels
}
else if (sample_2 < signalMin_2) {
signalMin_2 = sample_2; // save just the min levels
}
}
}
/*************************************************************************************/
/*************************************************************************************
* Signal Smoothing via Dampervals
*************************************************************************************/
for (int i = 0; i < 4; i++) {
damperOne[i] = damperOne[i + 1];
damperTwo[i] = damperTwo[i + 1];
}
damperOne[4] = (signalMax_1 - signalMin_1) * 2;
damperTwo[4] = (signalMax_2 - signalMin_2) * 2;
int val1 = 0;
int val2 = 0;
for (int i = 0; i < 5; i++) {
val1 += damperOne[i];
val2 += damperTwo[i];
}
wheelOnePosition = val1 / 5;
wheelTwoPosition = val2 / 5;
button1Status = (digitalRead(BUTTON_ONE_PIN));
if ((button1Status == HIGH) && (state == GAME_OVER)) {
Serial.println("game over, drawing menu");
drawMenu ();
}
/*************************************************************************************/
}
void drawGameScreen() {
//draw right paddle
rightPaddleY = ((wheelOnePosition / 8) * (TV.vres() - PADDLE_HEIGHT)) / 128;
x = RIGHT_PADDLE_X;
for (int i = 0; i < PADDLE_WIDTH; i++) {
TV.draw_line(x + i, rightPaddleY, x + i, rightPaddleY + PADDLE_HEIGHT, 1);
}
//draw left paddle
leftPaddleY = ((wheelTwoPosition / 8) * (TV.vres() - PADDLE_HEIGHT)) / 128;
x = LEFT_PADDLE_X;
for (int i = 0; i < PADDLE_WIDTH; i++) {
TV.draw_line(x + i, leftPaddleY, x + i, leftPaddleY + PADDLE_HEIGHT, 1);
}
//draw score
TV.print_char(LEFT_SCORE_X, SCORE_Y, '0' + leftPlayerScore);
TV.print_char(RIGHT_SCORE_X, SCORE_Y, '0' + rightPlayerScore);
//draw ball
TV.set_pixel(ballX, ballY, 2);
}
void playerScored(byte player) {
if (player == LEFT) leftPlayerScore++;
if (player == RIGHT) rightPlayerScore++;
//check for win
if (leftPlayerScore == PLAY_TO || rightPlayerScore == PLAY_TO) {
state = GAME_OVER;
}
ballVolX = -ballVolX;
}
void drawBox() {
TV.clear_screen();
//draw net
for (int i = 1; i < TV.vres() - 4; i += 6) {
TV.draw_line(TV.hres() / 2, i, TV.hres() / 2, i + 3, 1);
}
// had to make box a bit smaller to fit tv
TV.draw_line(0, 0, 0, 95, 1 ); // left
TV.draw_line(0, 0, 126, 0, 1 ); // top
TV.draw_line(126, 0, 126, 95, 1 ); // right
TV.draw_line(0, 95, 126, 95, 1 ); // bottom
state = IN_GAMEB;
}
void drawMenu() {
x = 0;
y = 0;
char volX = 3;
char volY = 3;
TV.clear_screen();
TV.select_font(font8x8);
TV.print(10, 5, "E-HACKS");
TV.print(10, 20, "Arduino Pong");
TV.select_font(font4x6);
TV.print(22, 45, "Press Button");
TV.print(30, 55, "To Start");
delay(1000);
while (button1Status) {
processInputs();
TV.delay_frame(3);
if (x + volX < 1 || x + volX > TV.hres() - 1) volX = -volX;
if (y + volY < 1 || y + volY > TV.vres() - 1) volY = -volY;
if (TV.get_pixel(x + volX, y + volY)) {
TV.set_pixel(x + volX, y + volY, 0);
if (TV.get_pixel(x + volX, y - volY) == 0) {
volY = -volY;
}
else if (TV.get_pixel(x - volX, y + volY) == 0) {
volX = -volX;
}
else {
volX = -volX;
volY = -volY;
}
}
TV.set_pixel(x, y, 0);
x += volX;
y += volY;
TV.set_pixel(x, y, 1);
}
TV.select_font(font4x6);
state = IN_GAMEA;
}
void setup() {
x = 0;
y = 0;
TV.begin(_NTSC);
ballX = TV.hres() / 2;
ballY = TV.vres() / 2;
pinMode(BUTTON_ONE_PIN, INPUT_PULLUP);
}
void loop() {
processInputs();
if (state == IN_MENU) {
drawMenu();
}
if (state == IN_GAMEA) {
drawBox();
}
if (state == IN_GAMEB) {
//every third frame
if (frame % 3 == 0) {
ballX += ballVolX;
ballY += ballVolY;
//change if hit top or bottom
if (ballY <= 1 || ballY >= TV.vres() - 1) {
ballVolY = -ballVolY;
delay(100);
TV.tone(2000, 30);
}
// test left side for wall hit
if (ballVolX < 0 && ballX == LEFT_PADDLE_X + PADDLE_WIDTH - 1 && ballY >= leftPaddleY && ballY <= leftPaddleY + PADDLE_HEIGHT) {
ballVolX = -ballVolX;
ballVolY += 2 * ((ballY - leftPaddleY) - (PADDLE_HEIGHT / 2)) / (PADDLE_HEIGHT / 2);
delay(100);
TV.tone(2000, 30 );
}
// test right side for wall hit
if (ballVolX > 0 && ballX == RIGHT_PADDLE_X && ballY >= rightPaddleY && ballY <= rightPaddleY + PADDLE_HEIGHT) {
ballVolX = -ballVolX;
ballVolY += 2 * ((ballY - rightPaddleY) - (PADDLE_HEIGHT / 2)) / (PADDLE_HEIGHT / 2);
delay(100);
TV.tone( 2000, 30 );
}
//limit vertical speed
if (ballVolY > MAX_Y_VELOCITY) ballVolY = MAX_Y_VELOCITY;
if (ballVolY < -MAX_Y_VELOCITY) ballVolY = -MAX_Y_VELOCITY;
// Scoring
if (ballX <= 1) {
playerScored(RIGHT);
delay(100);
TV.tone( 500, 300 );
}
if (ballX >= TV.hres() - 1) {
playerScored(LEFT);
// sound
delay(100);
TV.tone( 500, 300 );
}
}
drawGameScreen();
}
if (state == GAME_OVER) {
drawGameScreen();
TV.select_font(font8x8);
TV.print(29, 25, "GAME");
TV.print(68, 25, "OVER");
while (!button1Status) {
processInputs();
delay(50);
}
TV.select_font(font4x6); //reset the font
//reset the scores
leftPlayerScore = 0;
rightPlayerScore = 0;
state = IN_MENU;
}
TV.delay_frame(1);
if (++frame == 60) frame = 0; //increment and/or reset frame counter
}