BricoGeek.com Blog

Si aún no eres usuario, puedes crear una cuenta o entrar
ArduGame: Juego portatil con Arduino, OLED y Acelerómetro

A todos nos gusta realizar proyectos electrónicos, sin embargo, muchas veces, una vez hechos, quedan en el olvido en algún cajón o incluso peor, pasan a ser placas de desecho en un baul para reaprovechar componentes. ArduGame nace con la idea de un proyecto divertido de realizar y divertido de utilizar, sin necesidad de muchas horas de desarrollo.

Os presentamos el último proyecto de BricoGeek.com: ArduGame. Se trata de un pequeño sistema de videojuego portatil basado en la plataforma Arduino (Atmega168) y que utiliza una pantalla color Micro-OLED de 4D Systems para visualizar los gráficos junto con un pequeño zumbador o "speaker" para emitir sonidos mientras se juega. En éste caso se han aprovechado los botones de una vieja PSP estropeada para hacer el mando.

Éste sistema ofrece infinitas posibilidades para programar nuestros propios juegos, de hecho, hemos programado un pequeño juego (el cual podeis descargar los fuentes gratuitamente) el cual por el momento no tiene nombre definido!

Todo esto está muy bien, pero como no hemos podido resistirnos, también hemos incorporado un pequeño acelerómetro ADXL 2g para jugar con movimiento!! La pelota posee aceleración y podemos seleccionar de qué forma queremos jugar desde el menu principal. Calcula los puntos acumulados y siempre tendremos un tiempo limitado entre cada nivel.

No os perdais el video altamente recomendado a continuación, asi como los detalles del proyecto.

Como siempre, esperamos vuestros comentarios y/o sugerencias ya que sois ya muchos los que colaborais a diario con BricoGeek y sus proyectos!

Actualización:

Por tercera vez, BricoGeek.com aparece en portada de Make Magazine, también en Hacked Gadgets y Daily DIY!

Otros sitios que enlazan el proyecto:
DIY Zedomax
Gadgets Blogs2k

Gracias por el post amiguetes! Esperamos vuestros comentarios!





Click here for english translation


ArduGame - Arduino Portable Gaming System - BricoGeek.com from BricoGeek on Vimeo.

Descripción
Todo el sistema está basado en la placa Arduino, con un microcontrolador ATMega168 corriendo a 16Mhz. Conectado a la placa Arduino se encuentra una pequeña placa para los mandos que fueron recuperados de una PSP estropeada. Practicando unos agujeros y teniendo un poco de paciencia, se puede colocar los botones facilmente.

La pantalla es una Micro-OLED-128-G1 color de 4D Systems, la cual es facilmente programable mediante un protocolo de interfaz série utilizando el UART del Atmega. Dispone de un zócalo para alojar una tarjeta de memoria MicroSD de hasta 2G, la cual permite incluso almacenar imagenes o videos que posteriormente podemos mostras en pantalla enviando unos simples comandos a la pantalla. Esto nos da muchas ventajas a la hora de hacer un juego, ya que no necesitamos desperdiciar el valioso espacio de programa, que es limitado, y asi poder guardar hasta 2Gb de gráficos! Quizas un Street Fighter sería viable... por que no? Abierto queda el debate.

El juego
No hay mucho que decir sobre éste sencillo juego, ya que principalmente es de prueba. Se trata de una pelota azul que deber somerse a las pelotas verdes. En cuanto se come una pelota verde, se incrementas los puntos en función del diámetro de la bola comida. A más tamaño, más puntos. Si tocamos una bola roja, restaremos puntos, y al igual que con las verdes, a mayor diametro, mas puntos perderemos al tocarlas. También si tocamos cualquiera de los bordes de pantalla, no restaremos puntos, pero estaremos perdiendo un valioso tiempo del cual disponemos para pasar al siguiente nivel.
Se calcula cada nivel (posición de las bolas de colores) de modo aleatorio con la función random. Para garantizar que siempre tendremos un número realmente aleatorio, se inicializa el seed (o semilla) mediante una lectura a un puerto ADC sin conectar. Esto nos proporciona un valor variable para la semilla.
Al mover los cursores, aceleramos o frenamos la bola azul, por lo que hay que tener cuidado de no acelerar mucho o perderemos el control!

Sobre el código fuente no hay mucho que decir ya es es bastante sencillo de leer y entender ya que se incluyen algunos comentarios útiles. Podeis descargar el archivo PDE de codigo fuente en los enlaces de abajo.

Conclusión
Me gusta mucho este proyecto, por que una vez tengamos todo el hardware funcional, podemos dedicarle un tiempo a desarrollar más juegos utizando los controles o bien el acelerómetro integrado para jugar con movimiento. De un mismo hardware, podemos pasar horas y horas desarrollando para esta pequeña consola con un aire similar a la ultra conocida Game Boy.

Lista de componentes usados:

Código fuente:

/********************************************
  ArduGame
  Oscar Gonzalez Varela 2008
  www.BricoGeek.com
 ********************************************/
#include <string.h>
#include <Stdio.h> 
#include <math.h>

#include <avr/pgmspace.h>
#include "oled160drv.h"

#define BUTTON_UP       7
#define BUTTON_RIGHT    8
#define BUTTON_DOWN     4
#define BUTTON_LEFT     3

#define BUTTON_A        12
#define BUTTON_B        13

#define SOUND_PIN      5

#define SENSOR_PIN_X       1
#define SENSOR_PIN_Y       0

#define BALL_ACCEL  0.3
#define BALL_DECEL  0.3

char use_adxl=0;
float ball_x = 64;
float ball_y = 64;
int ball_size= 5;
float acc_x = 0;
float acc_y = 0;
long last_time=0;
char game_time=0;
int game_totalpt=0;
char game_level=1;

#define MAX_PUNTOS  10

int mPuntos_val[MAX_PUNTOS];
int mPuntos_x[MAX_PUNTOS];
int mPuntos_y[MAX_PUNTOS];

void setup()                    // run once, when the sketch starts
{
  
   // Set I/O
   pinMode(OLED_RESETPIN, OUTPUT);   
   pinMode(SOUND_PIN, OUTPUT);   
   pinMode(BUTTON_UP, INPUT);
   pinMode(BUTTON_RIGHT, INPUT);
   pinMode(BUTTON_DOWN, INPUT);
   pinMode(BUTTON_LEFT, INPUT);
   pinMode(BUTTON_A, INPUT);
   pinMode(BUTTON_B, INPUT);
   
   pinMode(SENSOR_PIN_X, INPUT);
   pinMode(SENSOR_PIN_Y, INPUT);
   
   randomSeed(analogRead(0));
     
   // Init serial comunication for the OLED display
   Serial.begin(OLED_BAUDRATE);
  
  // Initialise display
  OLED_Init();
  
  //Clear screen
  OLED_Clear();
    
}

void beep (unsigned char speakerPin, int frequencyInHertz, long timeInMilliseconds)     // the sound producing function
{ 	 
          int x; 	 
          long delayAmount = (long)(1000000/frequencyInHertz);
          long loopTime = (long)((timeInMilliseconds*1000)/(delayAmount*2));
          for (x=0;x<loopTime;x++) 	 
          { 	 
              digitalWrite(speakerPin,HIGH);
              delayMicroseconds(delayAmount);
              digitalWrite(speakerPin,LOW);
              delayMicroseconds(delayAmount);
          } 	 
} 

// Usefull function to get distance from two 2D points
int getDistance(int x1, int y1, int x2, int y2)
{
  return sqrt(pow(x2-x1, 2) + pow(y2-y1, 2));
}

void InitGame()
{

  int i;
  
  for (i=0 ; i<MAX_PUNTOS ; i++)
  {
    
      mPuntos_x[i] = 0;
      mPuntos_y[i] = 0;      
      mPuntos_val[i] = 0;    
    
      // Set random values here
      mPuntos_x[i] = random(5, 105);
      mPuntos_y[i] = random(5, 105);      
      mPuntos_val[i] = random(0, 10);
  }
  
  ball_x = 64;
  ball_y = 64;
  ball_size = 5;
  acc_x = 0;
  acc_y = 0;
  
  game_time = 20-(game_level-1);  
  
}

void DrawmPuntos()
{
  
   int color=0;
   int i;
  
  for (i=0 ; i<MAX_PUNTOS ; i++)
  {      
      if (mPuntos_val[i] != 0)
      {
        if (mPuntos_val[i] < 5) { color = GetRGB(255, 0, 0); }
        if (mPuntos_val[i] == 5) { color = GetRGB(255, 0, 255); }
        if (mPuntos_val[i] > 5) { color = GetRGB(0, 255, 0); }
      
        OLED_DrawCircle(mPuntos_x[i], mPuntos_y[i], mPuntos_val[i], 0, color);
      }
  }
}

void DrawToolbar()
{
  
   char strText[30];
   
   // Erase first! (black)
   OLED_DrawRectangle(0, 110, 127, 16, 1, GetRGB(64, 64, 64));   
   
   sprintf(strText,"Time:   %d", game_time);     
   OLED_DrawText(0, 14, 0, strText, 0xFFFF);
      
   sprintf(strText,"Points: %d", game_totalpt);     
   OLED_DrawText(0, 15, 0, strText, 0xFFFF);  
}

void DrawMainMenu()
{
  
  char exit_loop=0;
   
  //Clear screen
  OLED_Clear();
  
  OLED_DrawText(1, 3, 2, "ArduGame v1.0", 0xFFFF);
  OLED_DrawText(1, 6, 0, "www.BricoGeek.com", 0xFFFF);  
  
  OLED_DrawText(1, 10, 0, "Play Motion    A", GetRGB(254, 0, 254));  
  OLED_DrawText(1, 11, 0, "Play Joystick  B", GetRGB(254, 254, 0));  
  
  while (exit_loop == 0)
  {
    // Select (A)
    if (digitalRead(BUTTON_A) == HIGH) { use_adxl = 1; while (digitalRead(BUTTON_A) == HIGH) {  }; exit_loop=1; }
    if (digitalRead(BUTTON_B) == HIGH) { use_adxl = 0; while (digitalRead(BUTTON_A) == HIGH) {  }; exit_loop=1; }
  }
  
  // Some sound here!
  beep(SOUND_PIN,12500,7); 
  
}

void loop()                     // run over and over again
{  
  
  int i;
  int x, y;
  char game_over=0;
  int remaining_points=0;
  int last_x, last_y, last_size;
  int val_x=0;
  int val_y=0;  
  
  OLED_Clear();
  DrawMainMenu();
  OLED_Clear();
    
  OLED_DrawCircle(ball_x, ball_y, ball_size, 0, GetRGB(0, 0, 254));
  
  InitGame();
  DrawmPuntos();
  DrawToolbar();
  
  game_level=1;  
  last_time = millis();
  
  while (!game_over)
  {        
    
    // Read acelerometer values (0-1024)
    if (use_adxl == 1)
    {
      val_x = analogRead(SENSOR_PIN_X)-512;
      val_y = analogRead(SENSOR_PIN_Y)-512;    
    }
    
    last_x = ball_x;
    last_y = ball_y;  
    last_size = ball_size;  
    
    if (use_adxl == 0)
    {
      if (digitalRead(BUTTON_A) == HIGH) { ball_size--; }    
      if (digitalRead(BUTTON_B) == HIGH) { ball_size++; }    
      if (digitalRead(BUTTON_UP) == HIGH) { acc_y-=BALL_ACCEL; }    
      if (digitalRead(BUTTON_DOWN) == HIGH) { acc_y+=BALL_DECEL;}    
      if (digitalRead(BUTTON_LEFT) == HIGH) { acc_x-=BALL_ACCEL; }
      if (digitalRead(BUTTON_RIGHT) == HIGH) { acc_x+=BALL_DECEL; }
    }
    else
    {    
      acc_x -= (val_x/130);
      acc_y -= (val_y/130);        
    }
    
    // Increase/decrease acceleration
    ball_x += acc_x;
    ball_y += acc_y;
    
    if (ball_size < 1) { ball_size=1; }
    if (ball_size > 10) { ball_size=10; }    
    
    if ( (ball_x != last_x) || (ball_y != last_y) || (ball_size != last_size) )
    {       
      OLED_DrawCircle(last_x, last_y, last_size, 0, 0);
      OLED_DrawCircle(ball_x, ball_y, ball_size, 0, GetRGB(0, 0, 254));
    }
    
    // Collision detection on corners
    if ( ((ball_x-ball_size) <= 0) || 
         ((ball_x+ball_size) >= 127) ||             
         ((ball_y-ball_size) <= 0) || 
         ((ball_y+ball_size) >= 110) )
    {
        OLED_DrawCircle(ball_x, ball_y, ball_size, 0, GetRGB(254, 254, 0));
        // We die! Oh noooo!!! BUUUUU!!
        beep(SOUND_PIN,2093,15);
        OLED_DrawCircle(ball_x, ball_y, ball_size, 0, 0);
        // Erase and reset de ball        
        ball_x = 64; ball_y = 64;
        acc_x = 0; acc_y = 0;
        OLED_DrawCircle(ball_x, ball_y, ball_size, 0, GetRGB(0, 0, 254));
    }    
    
    // Collision detection on points
    remaining_points=0;
    
    for (i=0 ; i<MAX_PUNTOS ; i++)
    {
      if (mPuntos_val[i] > 0)
      {
        if (getDistance(ball_x, ball_y, mPuntos_x[i], mPuntos_y[i]) <= (ball_size+mPuntos_val[i]))
        {          
            if (mPuntos_val[i] < 5)
            {
                 // Bad point   
               beep(SOUND_PIN,2000,10);  
               game_totalpt -= mPuntos_val[i];
               DrawToolbar();
            }
            else
            {
              // Good ball!
              OLED_DrawCircle(mPuntos_x[i], mPuntos_y[i], mPuntos_val[i], 0, GetRGB(0, 0, 254)); // Make point blue!
              beep(SOUND_PIN,11100,5);
              game_totalpt += mPuntos_val[i];
              DrawToolbar();
            }        
         
           // Erase point
           OLED_DrawCircle(mPuntos_x[i], mPuntos_y[i], mPuntos_val[i], 0, 0);         
           mPuntos_val[i] = 0; // Point is now obsolete
           
           // Redraw all points
           DrawmPuntos();
        }
      }
      // Green remaining points counter
      if (mPuntos_val[i] >= 5) { remaining_points++; }      
     
    }    
    
      // Detect next level
      if (remaining_points == 0)
      {
          char strText[30];
          game_level++; // Increase level!
          sprintf(strText, "LEVEL %d", game_level);
          OLED_DrawText(1, 3, 2, strText, GetRGB(254, 254, 254));
          sprintf(strText, "Total: %d pt", game_totalpt);
          OLED_DrawText(1, 7, 0, strText, GetRGB(254, 0, 0));
          for (i=5000 ; i<20000 ; i+=600)
          {
            beep(SOUND_PIN,i,1);
            //delay(1);
          }
          
          delay(100);
          
          OLED_Clear();
          
          InitGame();
          DrawmPuntos();       
          DrawToolbar();
       
         OLED_DrawCircle(ball_x, ball_y, ball_size, 0, GetRGB(0, 0, 254));   
          
      }    
      
      // Detect Game Over
      if (game_time == 0)
      {
          char strText[30];
          game_level++; // Increase level!
          OLED_DrawText(1, 3, 2, "* GAME OVER *", GetRGB(254, 0, 0));
          delay(1);
          
          for (i=20000 ; i>=5000 ; i-=200)
          {
            beep(SOUND_PIN,i,1);
            //delay(1);
          }          
          
          // Wait for button press
          while (digitalRead(BUTTON_A) == LOW) { }    
          
          game_over=1; // Game over!!!!
      }
      
      // Time counter
      if ((millis()-last_time) > 100)
      {
        last_time = millis();
        game_time--;
        DrawToolbar();
      }
    
  }
}

Comentarios:


gizer20
Enviado el 05/08/2008 (00:57)
Impresionante el proyecto , pero la critica es el aspecto del proyecto. Un trabajo bien hecho a parte de electronica y programacion es la carcasa, una recomendacion que te hago es utilizar unos tornillos mas pequeños, unos m3 bastan, despues en vez de poner bricogeek asi en permanente, pon una etiqueta que ocupe la parte delantera , queda mucho mas profesional y luego , esconde el puerto serie, o mejor aun, utiliza un chip ftdi para poder ponerle usb al projecto

Oscar Gonzalez Oscar Gonzalez es un colaborador!
Enviado el 05/08/2008 (01:11)
Tomo buena nota, aver si mejoro el aspecto en la siguiente version! :)

Alejandro Mallo Alejandro Mallo es un colaborador!
Enviado el 05/08/2008 (08:42)
Y digo yo no seria mejor poner un giroscopio en ligar de un acelerometro...

Na lo digo por las ostias que le arreas para que se mueva la pelotita...

tcltwins
Enviado el 05/08/2008 (10:31)
um~well ,that's a good machine,Music is so good too,i want to know the name of Background Music ...tcltwins@gmail.com

jaime
Enviado el 06/08/2008 (01:28)
hola,me agradaria saber los planos para poder hacerla a i email lla q me gustaria muxo poder hacer una ,es mi sueño...


xemiahx@gmail.com

rez
Enviado el 06/08/2008 (19:18)
Enhorabuena por el proyecto! Me he quedado boquiabierto. Llevo un tiempo trasteando con Arduino y me encantaría dar un paso más intentando hacer algo parecido a tu sistema. ¿Sería posible tener acceso más detallado a la construcción del mismo?, ¿teneis pensado publicar documentación al respecto?

Un gran saludo desde Pekin y enhorabuena de nuevo!

Rez
DELACREW team
www.delacrew.net

Bruce
Enviado el 07/08/2008 (13:34)
Hei que tal!!

Cojonu...... el proyecto. Por cierto que te quería comentar, me compré una videocámara asi que si la necesitas para los proyectos, pégame un toque.

Bye

Oscar Gonzalez Oscar Gonzalez es un colaborador!
Enviado el 07/08/2008 (13:51)
Hey! Genial, ya hablaremos entonces para gravar la v2! Gracias! :)

WG
Enviado el 24/09/2008 (14:33)
Oscar, me parece un proyecto muy currado.
El caso es que me estoy pegando con una plataforma similar (Arduino OLED128 varios sensores que compré en tu tienda) y me estoy volviendo un poco loco para hacer funcionar el display. Me gustaría comentar contigo los problemas que he sufrido a ver si ya has pasado tú por ellos.

Por cierto he intentado compilar el codigo que publicas y me da varios errores, que versión estas utilizando, la última? (v012 Alpha). Igual es que has actualizado la libreria OLED160drv y la que tengo esta en version anterior.



nicolas campiño pineda.
Enviado el 05/01/2009 (20:38)
soy nicolas y me gusta el juego de wall.e

cube
Enviado el 28/02/2009 (16:22)
Hola Óscar, he estado mirando el código del ArduGame y no acabo de ver porqué divides el valor del sensor entre 130.

Estoy haciendo un proyectillo con el adxl3xx y no le acabo de coger el truquillo.

Gracias!


Enviar comentario

Debes disponer de una cuenta de usuario para publicar un comentario.

Si aún no dispones de una cuenta, únete a la comunidad de BricoGeek.com y registrate ahora, te llevará tan solo un minuto y es gratis. También puedes identificate si ya dispones de una cuenta de usuario.
rss

Conecta con BricoGeek.com

Canales sociales

Nos gusta mucho estar en contacto con nuestros lectores y saber más acerca de sus proyecto e inventos.

BricoGeek en Facebook
BricoGeek en Twitter
BricoGeek en Flickr
BricoGeek en YouTUBE
BricoGeek en LinkedIn
Añadelo a Technorati

También puedes sugerir una noticia en el blog
Tienda de Electrónica DIY y Arduino

Lo que se cuece...

Comentarios recientes Más comentado Top noticias
Feed de novedades

Tienda BricoGeek.com

Electrónica para tus proyectos!

Estás preparando un proyecto o quieres iniciarte en la electrónica? Encuentra todo lo que necesitas en la tienda.

Visita la tienda BricoGeek!

La familia de BricoGeek.com

Conoce toda la gente que hace que BricoGeek.com sea posible cada día:

Alejandro Mallo
Colaborador
Manuel Jiménez
Colaborador
Pablo Toimil
Colaborador
Puedes hacer click sobre un autor para ver todas sus noticias


Etiquetas más populares:

Descubre todo el contenido de bricogeek.com con las etiquetas más populares:

Páginas amigas:

Aqui tienes una selección de las páginas que más nos gusta visitar


© BricoGeek.com 2005-2010

Demuestra lo que sabes hacer!