PONG
Ruben Martinez y Miguel Morales. Grupo 6
Introducción
Este proyecto esta basado en el clásico juego del PONG, creado en el año 1972 por la empresa Atari, fue el primer videojuego de éxito comercial. Es este hecho es por lo que nos decantamos a realizar un proyecto que no solo es historia dentro del mundo de la informática, sino que además queríamos realizar una practica que fuera fácil de usar, portable y que se pudiera jugar con amigos.
Hardware
Nuestro proyecto se basa en cinco componentes: pulsadores, cables, protoboard, display y Arduino. La implementación consiste en cinco botones los cuales sus patas derechas van conectadas a tierra, mientras que las de la izquierda se conectan a los pines digitales del 2 al 6. Si nos fijamos en la pantalla, podemos observar que tiene un modulo de conexión I2C, lo que nos simplificó mucho las conexiones. Podemos ver que la entrada VCC del display se conecta a la salida de 5V del arduino y la del GND a la toma de tierra del arduino. Tambien vemos que la señal de datos y la señal de reloj se conectan a las entradas analogicas A5 y A4 respectivamente.
Software
La explicación del software se realiza en el video explicativo que se muestra al final del código. No obstante, hemos añadido una serie de comentarios para que la comprensión del código sea lo mas sencillo posible
/*
Miguel Morales y Ruben Martinez:
PONG en Arduino
*/
#include <Wire.h>
#include <Adafruit_SH110X.h>
#include <Adafruit_GFX.h>
#define i2c_Address 0x3c
#define NEGRO SH110X_BLACK
#define BLANCO SH110X_WHITE
#define BotonSubirIzq 2
#define BotonBajarIzq 3
#define BotonSubirDer 4
#define BotonBajarDer 5
#define BotonReset 6
const unsigned long VelocidadPala = 20;
const unsigned long VelocidadBola = 1;
const uint8_t LongitudPala = 13;
bool resetBola = false;
int puntuacionJDer = 0;
int puntuacionJIzq = 0;
int PuntuacionMaxima = 5;
#define AnchoPantalla 128
#define AltoPantalla 64
#define OLED_RESET 4
Adafruit_SH1106G display = Adafruit_SH1106G (AnchoPantalla, AltoPantalla, &Wire, OLED_RESET);
#define anchoLogo 84
#define alturaLogo 52
/* definimos el mapa de bits del logo de la urjc */
const unsigned char PROGMEM logo[] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xe0, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0xe1, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x01, 0xf1, 0xf3, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xfb, 0xf3, 0xe0,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xfb, 0xf3, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x01, 0xf1, 0xf3, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe0, 0xe1,
0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x3f, 0xff, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f,
0xff, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0xff, 0x80, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xf8, 0x03, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x03, 0xf8, 0x03, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xf8, 0x03, 0xf0, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xf8, 0x03, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x03, 0xf8, 0x03, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xf8, 0x03, 0xf0,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xf8, 0x03, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x03, 0xf8, 0x03, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xf8, 0x03,
0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xf8, 0x03, 0xf0, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x03, 0xf8, 0x03, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xf8,
0x03, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xf8, 0x03, 0xf0, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x03, 0xf8, 0x03, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,
0xf8, 0x03, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xf8, 0x03, 0xf0, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xf8, 0x07, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x03, 0xf8, 0x07, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xf8, 0x0f, 0xf0, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xfc, 0x1f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x01, 0xff, 0xff, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xfb, 0xf0,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xf3, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x7f, 0xe3, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x80,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};
void drawBitmap(int16_t x, int16_t y, const uint8_t *bitmap, int16_t w, int16_t h, uint16_t color);
void DibujarLineasCampo();
void DibujarMarcador();
uint8_t Bola_X = 64, Bola_Y = 32;
uint8_t DireccionBola_X = 1, DireccionBola_Y = 1;
unsigned long ActualizarBola;
unsigned long ActualizarPala;
const uint8_t JugadorDer_X = 12;
uint8_t JugadorDer_Y = 16;
const uint8_t JugadorIzq_X = 115;
uint8_t JugadorIzq_Y = 16;
void setup() {
display.begin(i2c_Address, true);
display.clearDisplay();
display.setTextSize(1);
display.setTextColor(BLANCO);
display.setCursor(0, 0);
display.println("Ruben y Miguel");/* Ponemos nuestros nombres en la parte superior */
display.display();
display.drawLine(0, 10, 128, 10, BLANCO); /* dibujamos una linea separatoria entre el logo y nuestros nombres */
display.display();
display.drawBitmap((display.width() - anchoLogo) / 2, ((display.height() - alturaLogo) / 2) + 7, logo, anchoLogo, alturaLogo, BLANCO); /* pintamos el logo */
display.display();
/* definimos los componentes */
unsigned long InicioPartida = millis();
pinMode(BotonSubirIzq, INPUT);
pinMode(BotonBajarIzq, INPUT);
pinMode(BotonReset, INPUT_PULLUP);
digitalWrite(BotonSubirIzq, 1);
digitalWrite(BotonBajarIzq, 1);
pinMode(BotonSubirDer, INPUT);
pinMode(BotonBajarDer, INPUT);
digitalWrite(BotonSubirDer, 1);
digitalWrite(BotonBajarDer, 1);
/* dibujamos las lineas del campo y el marcador despues de 2s */
display.clearDisplay();
DibujarLineasCampo();
DibujarMarcador();
while (millis() - InicioPartida < 2000);
display.display();
ActualizarBola = millis();
ActualizarPala = ActualizarBola;
}
void loop() {
bool update = false;
unsigned long tiempo = millis();
static bool BotonSubirIzqPulsado = false;
static bool BotonBajarIzqPulsado = false;
static bool BotonSubirDerPulsado = false;
static bool BotonBajarDerPulsado = false;
if (digitalRead(BotonReset) == LOW) {
FinPartido();
}
if (resetBola)
{
if (puntuacionJDer == PuntuacionMaxima || puntuacionJIzq == PuntuacionMaxima)
{
FinPartido();
}
else {
display.fillScreen(NEGRO);
DibujarMarcador();
DibujarLineasCampo();
randomSeed(analogRead(0)); /* Establecemos la semilla para generar aleatorios y a continuacion generamos la bola en un punto y direccion de movimiento x,y aleatorio respecto al tamaño de la pantalla */
Bola_X = random(25, 70);
Bola_Y = random(5, 60);
do
{
DireccionBola_X = random(-1, 2);
} while (DireccionBola_X == 0);
do
{
DireccionBola_Y = random(-1, 2);
} while (DireccionBola_Y == 0);
resetBola = false;
}
}
BotonSubirIzqPulsado |= (digitalRead(BotonSubirIzq) == LOW);
BotonBajarIzqPulsado |= (digitalRead(BotonBajarIzq) == LOW);
BotonSubirDerPulsado |= (digitalRead(BotonSubirDer) == LOW);
BotonBajarDerPulsado |= (digitalRead(BotonBajarDer) == LOW);
if (tiempo > ActualizarBola) {
uint8_t Nueva_X = Bola_X + DireccionBola_X;
uint8_t Nueva_Y = Bola_Y + DireccionBola_Y;
// Comprobamos que se ha hecho un punto
if (Nueva_X == 0 || Nueva_X == 127) {
if (Nueva_X == 0) {
puntuacionJDer += 1;
display.fillScreen(NEGRO);
resetBola = true;
}
else if (Nueva_X == 127) {
puntuacionJIzq += 1;
display.fillScreen(NEGRO);
resetBola = true;
}
DireccionBola_X = -DireccionBola_X;
Nueva_X += DireccionBola_X + DireccionBola_X;
}
// Golpeo en las paredes horizontales
if (Nueva_Y == 0 || Nueva_Y == 63) {
DireccionBola_Y = -DireccionBola_Y;
Nueva_Y += DireccionBola_Y; //Repintamos de blanco las paredes
}
// Golpeo en la pala jugador derecha
if (Nueva_X == JugadorDer_X && Nueva_Y >= JugadorDer_Y && Nueva_Y <= JugadorDer_Y + LongitudPala) {
DireccionBola_X = -DireccionBola_X;
Nueva_X += DireccionBola_X; //Repintamos de blanco las paredes
}
// Golpeo en la Pala jugador izquierda
if (Nueva_X == JugadorIzq_X
&& Nueva_Y >= JugadorIzq_Y
&& Nueva_Y <= JugadorIzq_Y + LongitudPala)
{
DireccionBola_X = -DireccionBola_X;
Nueva_X += DireccionBola_X; //Repintamos de blanco las paredes
}
display.drawPixel(Bola_X, Bola_Y, NEGRO); /* borramos (pintamos a negro) por donde ha ido pasando la bola */
display.drawPixel(Nueva_X, Nueva_Y, BLANCO); /* y pintamos de blanco el punto en el que se encuentra actualmente */
Bola_X = Nueva_X;
Bola_Y = Nueva_Y;
ActualizarBola += VelocidadBola;
update = true;
}
update = true;
if (tiempo > ActualizarPala) {
ActualizarPala += VelocidadPala; //Definimos la velocidad de movimiento de la pala
// pala Jugador Derecha
display.drawFastVLine(JugadorDer_X, JugadorDer_Y, LongitudPala, NEGRO); //PINTAMOS DE NEGRO DESPUES DE MOVERNOS PARA ASI BORRAR EL RECORRIDO
const uint8_t CentroPala = LongitudPala / 2;
if (BotonSubirDerPulsado) {
JugadorDer_Y -= 1;
}
if (BotonBajarDerPulsado) {
JugadorDer_Y += 1;
}
/* definimos los limites de la pala para no salirnos del eje Y */
if (JugadorDer_Y < 1) JugadorDer_Y = 1;
if (JugadorDer_Y + LongitudPala > 63) JugadorDer_Y = 63 - LongitudPala;
display.drawFastVLine(JugadorDer_X, JugadorDer_Y, LongitudPala, BLANCO);
BotonSubirDerPulsado = BotonBajarDerPulsado = false;
/// Pala Jugador Izquierda
display.drawFastVLine(JugadorIzq_X, JugadorIzq_Y, LongitudPala, NEGRO);
if (BotonSubirIzqPulsado) {
JugadorIzq_Y -= 1;
}
if (BotonBajarIzqPulsado) {
JugadorIzq_Y += 1;
}
BotonSubirIzqPulsado = BotonBajarIzqPulsado = false;
if (JugadorIzq_Y < 1) JugadorIzq_Y = 1;
if (JugadorIzq_Y + LongitudPala > 63) JugadorIzq_Y = 63 - LongitudPala;
display.drawFastVLine(JugadorIzq_X, JugadorIzq_Y, LongitudPala, BLANCO);
}
if (update) {
display.display();
}
}
void DibujarLineasCampo() {
display.drawRect(0, 0, 128, 64, BLANCO);
}
void DibujarMarcador() {
display.setTextSize(1);
display.setTextColor(BLANCO);
display.setCursor(50, 5);
display.println(puntuacionJIzq);
display.setCursor(60, 5);
display.println('-');
display.setCursor(70, 5);
display.println(puntuacionJDer);
}
void FinPartido() {
display.fillScreen(NEGRO);
if (puntuacionJDer > puntuacionJIzq)
{
display.setCursor(0, 4);
display.setTextColor(BLANCO);
display.setTextSize(2);
display.print("Jugador Derecha Gana");
} else if (puntuacionJIzq > puntuacionJDer)
{
display.setCursor(0, 4);
display.setTextColor(BLANCO);
display.setTextSize(2);
display.print("Jugador Izquierda Gana");
}
else {
display.setCursor(30, 4);
display.setTextColor(BLANCO);
display.setTextSize(2);
display.print("EMPATE");
}
delay(200);
display.display();
delay(2000);
puntuacionJIzq = puntuacionJDer = 0;
unsigned long InicioPartida = millis();
while (millis() - InicioPartida < 2000);
ActualizarBola = millis();
ActualizarPala = ActualizarBola;
resetBola = true;
}