Wednesday, October 29, 2008

A humble little GamePack game :)

I've never gotten so many emails in my life! Wow... well, I really like all the emails I've read, but of course I also like the funny comments some folks posted over on digg (and I know the comment about inventing fire was meant to be a joke, but how cool would it be to have a fire / pyrotechnic shield controller?). Oh well, I suppose you can't please all the people all the time. :) Anyway, I'm just finishing up the spec sheet and I'll post the schematics and wiring diagrams over at liquidware.org. A handful of emails asked about how to read values off the joystick, and it's fairly easy (but only because Arduino environment is so straightforward):

  • The InputShield connects the joystick x and y axis to the analog input pins
  • I use the Arduino command analogRead(4) and analogRead(5) to get x and y respectively
  • Those values come in as integers, between 0 and 1024, and the joystick is a resistive voltage divider, so the middle state means the value back from analogRead(4) is about 500, give or take. Same thing with the other axis.
  • I typically do a small calculation on the value, to set it in the right range, like check to see if it's bigger than 600 or smaller than 400, and then I know the joystick has been moved
  • The buttons are a little more straightforward, since they're either on or off
  • The buttons normally keep the digital line they're on high
  • Button A is wired to digital pin 5, and button B is wired to digital pin 4
  • I use the Arduino command digitalRead(4) and digitalRead(5) to read the value of those pins
  • Then, in my loop() function, I write code like if(!digitalRead(4)), which means if the value is not not high, then do something

I put all that together in a little piece of source code, to make a really simple game. The game has little orbs that fall down towards the bottom of the screen, and you have to avoid them. If you hit one, then the little ball you control gets dimmer. To make your little ball brighter, you have to run into the little blue orbs, which are life points. If you hit button b, it pauses and unpauses the game, and button a is a "cheat" which resets you to full life. Next stop, tetris! Just kidding :)



Of course, here's the source code - I tried to comment it (which I'm not too good about, but I tried):


COLOR green = { 0, 255, 0 };
COLOR blue = {0,0,255};
COLOR yellow = {255,255,0};
COLOR black = {0,0,0};
COLOR white = {255,255,255};
COLOR grey = {0x77,0x77,0x77};
COLOR red = {255,0,0};
COLOR me = {0,255,0};

POINT my_point;//i'm not using the touchscreen for this, but maybe i will next time

unsigned int analogValues[6];
unsigned char digitalValues[10];

int pos = 64; //my position at the bottom of the screen - 64 is the middle of the 128 screen

#define NUMBLOCKS 10 //i can make more orbs by making the numblocks bigger
int xs[NUMBLOCKS];//x positions of the orbs
int ys[NUMBLOCKS];//y positions
int ds[NUMBLOCKS];//d stands for delta, or how much the orb should move
int t[NUMBLOCKS];//type of orb (good or bad)

#define DIFFICULTY 4 //the bigger the number, the faster the starting velocity of blocks...

int timer,go;
unsigned char lives=10;

void setup()
{
lcd_setBrightness(5);

//initiatlize the orbs
for(int i=0;i<NUMBLOCKS;i++){
xs[i]=(int)(90+(rand()%100));
ys[i]=(int)(10+(rand()%80));
ds[i]=2+(int)((rand()%10));
t[i]=1;
}

timer = 0;
go = 0;

Serial.begin(9600);
delay(3000);

/* The sync character */
Serial.print('U');
}



void loop()
{
//digitalValues[0] - digital pin 4, button B MODEA
//digitalValues[1] - digital pin 5, button A MODEA
//analogValues[5] - joystick y, MODEA
//analogValues[4] - joystick x, MODEA

//Read analog values - for x and y position of the joystick
analogValues[4] = (Serial.read() << 8) + Serial.read();
analogValues[5] = (Serial.read() << 8) + Serial.read();

//Read digital values for buttons a and b state
digitalValues[0] = Serial.read();
digitalValues[1] = Serial.read();

if (!digitalValues[1]) {//if button a is pressed, give me more life
me.green=255;
}
if (!digitalValues[0]) {//if button b is pressed, pause the game
go=!go;//go has to be 1 in order for the orbs to fall down
}

eraseCar();//erase car

//update coordinates of the car based on the joystick position
//the further over the joystick is leaned, the more the car moves
if (analogValues[4] < 400) pos += 2;
if (analogValues[4] > 600) pos -= 2;

if (analogValues[4] < 200) pos += 2;
if (analogValues[4] > 800) pos -= 2;
if (analogValues[4] < 100) pos += 2;
if (analogValues[4] > 900) pos -= 2;

if (pos < 10) pos = 10;//keep the car in bounds
if (pos > 118) pos = 118;

drawCar();//draw car

if (go && timer++ >= 0) {//if i wanted to debug and slow things down, i make 0 a bigger number, like 5
timer = 0;
eraseCascade();//erase the orbs
cascadeDown();//make them go down
drawCascade();//draw them again
}

}


void drawCar(void) {
//draw me, me is a global variable for the rgb color
lcd_circle(5,pos,5, me, me);
}

void eraseCar(void) {
//erase me
lcd_circle(5,pos,5, black, black);
}

void cascadeDown(void){
for(int i = 0; i<NUMBLOCKS; i++) {
//xs[i]-=5; //orbs cascade down at fixed speed
xs[i]-=ds[i]; //orbs cascade down at variable speed

if(xs[i]<5) {//are the orbs at the bottom of the screen>
if ((ys[i]>(pos-6)) && (ys[i]<(pos+6))){//is the orb on top of the car?
if (t[i]) {//is it a 'bad' orb? if so, dim the color
me.green-=40;
if(me.green<20) me.green = 20;
} else {//is it a good orb? if so, make the car brighter
me.green+=40;
if(me.green>255) me.green = 255;
}

}
//assign new random coordinates and speed to the orb
xs[i] = (unsigned int)130+(rand()%20);ys[i] = (unsigned int)10+(rand()%100);ds[i]=DIFFICULTY+(int)((rand()%10));
if ((rand()%20)>18) {//a few of them will be good orbs
t[i] = 0;
} else {
t[i] = 1;
}
};
}

}

void drawCascade(void){
//repeat for number of blocks
for(int i = 0; i<NUMBLOCKS; i++) {
if (xs[i]<115 && xs[i]>5) {//only draw the orb if it's inbounds on the screen
if (t[i]) {
lcd_circle(xs[i],ys[i],2, white, white); //bad orbs are white
} else {
lcd_circle(xs[i],ys[i],2, blue, blue); //good orbs are blue
}
}
}

}

void eraseCascade(void){
//i know, i know, not the most elegant, but this just erases where the orbs where by painting over in black
for(int i = 0; i<NUMBLOCKS; i++) {
if (xs[i]<115) {
lcd_circle(xs[i],ys[i],2, black, black);
}
}

}

1 comment:

Chris said...

Very cool foundation for multiple games!

I can almost see the road for the race car game!

For the sides of the road, I would generate a random list of circle radii ) x ) Where "x" is the car location.

Again, cool!