#include <SPI.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include "ball.h"
#include "pad.h"
#include "score.h"
//--------------- OLED ----------------------------------------
// Initialise the display
#define SCREEN_WIDTH 128 // OLED display width, in pixels
#define SCREEN_HEIGHT 64 // OLED display height, in pixels
// Declaration for an SSD1306 display connected to I2C (SDA, SCL pins)
#define OLED_RESET 4 // Reset pin # (or -1 if sharing Arduino reset pin)
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
//Define Pins
#define OLED_RESET 4
#define BEEPER 3
#define CONTROL_A A0
#define CONTROL_B A1
//Define Visuals
#define FONT_SIZE 2
#define SCREEN_WIDTH 127 //real size minus 1, because coordinate system starts with 0
#define SCREEN_HEIGHT 63 //real size minus 1, because coordinate system starts with 0
#define PADDLE_WIDTH 4
#define PADDLE_HEIGHT 10
#define PADDLE_PADDING 10
#define BALL_SIZE 3
#define SCORE_PADDING 10
#define EFFECT_SPEED 0.5
#define MIN_Y_SPEED 0.5
#define MAX_Y_SPEED 2
#define MAX_SCORE 10
/*int paddleLocationA = 0;
int paddleLocationB = 0;*/
Ball myBall(SCREEN_WIDTH/2, SCREEN_HEIGHT/2, 2, 1 );
Pad paddleA(0, 0, PADDLE_PADDING);
Pad paddleB(0, 0, SCREEN_WIDTH-PADDLE_WIDTH-PADDLE_PADDING);
/*int lastPaddleLocationA = 0;
int lastPaddleLocationB = 0;*/
Score scoreA(0);
Score scoreB(0);
/*int scoreA = 0;
int scoreB = 0*/;
//Setup
void setup()
{
display.begin(SSD1306_SWITCHCAPVCC, 0x3D); // initialize with the I2C addr 0x3D (for the 128x64)
// initialise the display
// SSD1306_SWITCHCAPVCC = generate display voltage from 3.3V internally
if (!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) { // Address 0x3C for 128x64
Serial.println(F("SSD1306 allocation failed"));
for (;;); // Don't proceed, loop forever
}
display.clearDisplay(); // clears the screen and buffer
display.display();
display.setTextWrap(false);
splash();
display.setTextColor(WHITE);
display.setTextSize(FONT_SIZE);
display.clearDisplay();
}
//Splash Screen
void splash()
{
display.clearDisplay();
display.setTextColor(WHITE);
centerPrint("PONG",0,3);
centerPrint("By Allan Alcorn",24,1);
centerPrint("Ported by",33,1);
centerPrint("MichaelTeeuw.nl",42,1);
display.fillRect(0,SCREEN_HEIGHT-10,SCREEN_WIDTH,10,WHITE);
display.setTextColor(BLACK);
centerPrint("Move paddle to start!",SCREEN_HEIGHT-9,1);
display.display();
int controlA = analogRead(CONTROL_A);
int controlB = analogRead(CONTROL_B);
while (abs(controlA - analogRead(CONTROL_A) + controlB - analogRead(CONTROL_B)) < 10) {
// show as long as the total absolute change of
// both potmeters is smaler than 5
}
soundStart();
}
//Winner Screen
void winSplash(char* player)
{
display.clearDisplay();
display.setTextColor(WHITE);
centerPrint("GAME OVER",10,2);
centerPrint("Player",26,1);
centerPrint(player,35,2);
centerPrint("wins!",49,1);
display.display();
}
//Restarts
void redefine()
{
/*paddleLocationA = 0;
paddleLocationB = 0;*/
paddleA.paddleLocation = 0;
paddleB.paddleLocation = 0;
/*ballX = SCREEN_WIDTH/2;
ballY = SCREEN_HEIGHT/2;
ballSpeedX = 2;
ballSpeedY = 1;*/
myBall.ballX=SCREEN_WIDTH/2;
myBall.ballY=SCREEN_HEIGHT/2;
/*lastPaddleLocationA = 0;
lastPaddleLocationB = 0;*/
paddleA.lastPaddleLocation = 0;
paddleB.lastPaddleLocation = 0;
scoreA.score = 0;
scoreB.score = 0;
display.clearDisplay(); // clears the screen and buffer
display.display();
display.setTextWrap(false);
setup();
}
//Loop
void loop()
{
calculateMovement();
draw();
}
void calculateMovement()
{
int controlA = analogRead(CONTROL_A);
int controlB = analogRead(CONTROL_B);
paddleA.paddleLocation = map(controlA, 0, 1023, 0, SCREEN_HEIGHT - PADDLE_HEIGHT);
paddleB.paddleLocation = map(controlB, 0, 1023, 0, SCREEN_HEIGHT - PADDLE_HEIGHT);
int paddleSpeedA = paddleA.paddleLocation - paddleA.lastPaddleLocation;
int paddleSpeedB = paddleB.paddleLocation - paddleB.lastPaddleLocation;
myBall.ballX += myBall.ballSpeedX;
myBall.ballY += myBall.ballSpeedY;
//bounce from top and bottom
if (myBall.ballY >= SCREEN_HEIGHT - BALL_SIZE || myBall.ballY <= 0) {
myBall.ballSpeedY *= -1;
soundBounce();
}
//bounce from paddle A
if (myBall.ballX >= PADDLE_PADDING && myBall.ballX <= PADDLE_PADDING+BALL_SIZE && myBall.ballSpeedX < 0) {
if (myBall.ballY > paddleA.paddleLocation - BALL_SIZE && myBall.ballY < paddleA.paddleLocation + PADDLE_HEIGHT) {
soundBounce();
myBall.ballSpeedX *= -1;
addEffect(paddleSpeedA);
}
}
//bounce from paddle B
if (myBall.ballX >= SCREEN_WIDTH-PADDLE_WIDTH-PADDLE_PADDING-BALL_SIZE && myBall.ballX <= SCREEN_WIDTH-PADDLE_PADDING-BALL_SIZE && myBall.ballSpeedX > 0) {
if (myBall.ballY > paddleB.paddleLocation - BALL_SIZE && myBall.ballY < paddleB.paddleLocation + PADDLE_HEIGHT) {
soundBounce();
myBall.ballSpeedX *= -1;
addEffect(paddleSpeedB);
}
}
//score points if ball hits wall behind paddle
if (myBall.ballX >= SCREEN_WIDTH - BALL_SIZE || myBall.ballX <= 0) {
if (myBall.ballSpeedX > 0) {
scoreA.increase();
myBall.ballX = SCREEN_WIDTH / 4;
}
if (myBall.ballSpeedX < 0) {
scoreB.increase();
myBall.ballX = SCREEN_WIDTH / 4 * 3;
}
soundPoint();
if (scoreA.score >= MAX_SCORE || scoreB.score >= MAX_SCORE) {
if (scoreA.score >= MAX_SCORE)
winSplash("1");
else if(scoreB.score >= MAX_SCORE)
winSplash("2");
else
winSplash("WTF?");
delay(5000);
redefine();
}
}
//set last paddle locations
paddleA.lastPaddleLocation = paddleA.paddleLocation;
paddleB.lastPaddleLocation = paddleB.paddleLocation;
}
void draw()
{
display.clearDisplay();
//draw paddle A
paddleA.draw();
//draw paddle B
paddleB.draw();
//draw center line
for (int i=0; i<SCREEN_HEIGHT; i+=4) {
display.drawFastVLine(SCREEN_WIDTH/2, i, 2, WHITE);
}
//draw ball
myBall.draw();
//print scores
//backwards indent score A. This is dirty, but it works ... ;)
int scoreAWidth = 5 * FONT_SIZE;
if (scoreA.score > 9) scoreAWidth += 6 * FONT_SIZE;
if (scoreA.score > 99) scoreAWidth += 6 * FONT_SIZE;
if (scoreA.score > 999) scoreAWidth += 6 * FONT_SIZE;
if (scoreA.score > 9999) scoreAWidth += 6 * FONT_SIZE;
display.setCursor(SCREEN_WIDTH/2 - SCORE_PADDING - scoreAWidth,0);
display.print(scoreA.score);
display.setCursor(SCREEN_WIDTH/2 + SCORE_PADDING+1,0); //+1 because of dotted line.
display.print(scoreB.score);
display.display();
}
void addEffect(int paddleSpeed)
{
float oldBallSpeedY = myBall.ballSpeedY;
//add effect to ball when paddle is moving while bouncing.
//for every pixel of paddle movement, add or substact EFFECT_SPEED to ballspeed.
for (int effect = 0; effect < abs(paddleSpeed); effect++) {
if (paddleSpeed > 0) {
myBall.ballSpeedY += EFFECT_SPEED;
} else {
myBall.ballSpeedY -= EFFECT_SPEED;
}
}
//limit to minimum speed
if (myBall.ballSpeedY < MIN_Y_SPEED && myBall.ballSpeedY > -MIN_Y_SPEED) {
if (myBall.ballSpeedY > 0) myBall.ballSpeedY = MIN_Y_SPEED;
if (myBall.ballSpeedY < 0) myBall.ballSpeedY = -MIN_Y_SPEED;
if (myBall.ballSpeedY == 0) myBall.ballSpeedY = oldBallSpeedY;
}
//limit to maximum speed
if (myBall.ballSpeedY > MAX_Y_SPEED) myBall.ballSpeedY = MAX_Y_SPEED;
if (myBall.ballSpeedY < -MAX_Y_SPEED) myBall.ballSpeedY = -MAX_Y_SPEED;
}
void soundStart()
{
tone(BEEPER, 250);
delay(100);
tone(BEEPER, 500);
delay(100);
tone(BEEPER, 1000);
delay(100);
noTone(BEEPER);
}
void soundBounce()
{
tone(BEEPER, 500, 50);
}
void soundPoint()
{
tone(BEEPER, 150, 150);
}
void centerPrint(char *text, int y, int size)
{
display.setTextSize(size);
display.setCursor(SCREEN_WIDTH/2 - ((strlen(text))*6*size)/2,y);
display.print(text);
}