Saturday, January 30, 2010

Shortly after I wrote a post about Zombie Showdown at the Arduino forum, Bart had a suggestion on how the game could evolve with graphics. My original idea wasn't really to make a game with graphics but rather with physical input and feedback, but I admit I got a bit tempted to try graphics out. But before I got around to, Bart did. Here's the result of my game with Barts graphics.

Tuesday, January 12, 2010

I recently bought a 16x2 LCD from Adafruit and decided to make a small Zombie survival game as my first project.
The hardware is just the LCD hooked up according to instructions on the LCD product page and a push-button with a pull-down resistor like the one in this tutorial.

/*
Zombie Showdown
By Markus Ulfberg 2010-01-12
More Arduino stuff and future updates of Zombie Showdown at:
http://genericnerd.blogspot.com
Description:
A small game using an 16x2 LCD and a pushbutton.
The game presents a start screen and awaits player input.
If the player presses the button the game starts.
After a random number of milliseconds a Zombie will
appear. It will have a random strenght of 1-5.
The strenght of the Zombie is equal to how many
buttonpresses it takes to kill it.
If the player fails to kill the Zombie within a random
timeframe the Zombie will kill the player.
If the player manages to kill the Zombie the game will
continue and a new Zombie will soon attack.
When the player dies a Game Over screen is presented
also displaying the score of the player.
There is no "beating" this game since in the Zombie apocalypse
eventually everyone will die.
Planned expansion:
1. Use a vibrator to signal Zombie attack.
2. Use randomizer to make
Credits:
This game uses example code from
Debounce and the LiquidCrystal library.
*/
// include the library code:
#include <LiquidCrystal.h>
// initialize the library with the numbers of the interface pins
LiquidCrystal lcd(7, 8, 9, 10, 11, 12);
// Set the text strings used in the game
char* gameText[]={"Zombie Showdown",// 0 Title
"Kill to Start", // 1
"Let's go!", // 2
"Zombie!!!", // 3
"You killed it.", // 4
"You are dead.", // 5
"Game Over", // 6
"Score: " // 7
};
// Button variables
const int buttonPin = 2;
// Two values for debounceing
int buttonState;
int lastButtonState = LOW;
// the following variables are long's because the time, measured in miliseconds,
// will quickly become a bigger number than can be stored in an int.
long lastDebounceTime = 0; // the last time the output pin was toggled
long debounceDelay = 50; // the debounce time; increase if the output flickers
// Stores value of button 1 = pressed 0 = released
int buttonPress = 0;
// Title screen blink variables
long lastBlinkTime = 0;
long blinkDelay = 500;
int blinkState = 0;
// Gameplay variables
// Zombie health a random number between 1 and 5
int zombie;
// Time since last zombie apperance
long lastZombieTime = 0;
// Randomizes between each zombie
// Timer for next zombie apperance
// a random number between 200 ms and 20 000 ms
long zombieDelay;
// Randomizes between each zombie
// Countdown timer for zombie bite
// a random number between 2 000 ms and 7 000 ms
long zombieBite;
// Status of zombie apperance 1 = zombie 0 = no zombie
int zombieState = 0;
// Status of player 1 = alive 0 = dead
int playerAlive = 1;
// gameRun sets start screen or runs the game
int gameRun = 0;
// Score
int score = 0;
void setup() {
// set up the LCD's number of rows and columns:
lcd.begin(16, 2);
// DEBUG ONLY
Serial.begin(9600);
// Set up the button
pinMode(buttonPin, INPUT);
buttonState = digitalRead(buttonPin);
// Use unconnected analog input pin
// to generate a random seed for the random generator
randomSeed(analogRead(0));
// The timeframe where a new Zombie appears
// this first Zombie will have a shorter random time frame
zombieDelay = random(200,10000);
// The timeframe for the first zombie bite
zombieBite = random(2000,7000);
}
void loop() {
// Start sequence
gameStart();
// Gameplay
gameExecute();
// GameOver
gameOver();
} // loop end
void gameStart() {
while (gameRun == 0) {
// Set the cursor
lcd.setCursor(0,0);
// print the name of the game
lcd.print(gameText[0]);
// Pause for blink
if ((millis() - lastBlinkTime) > blinkDelay) {
// If blinkState is OFF turn it ON
if (blinkState == 0) {
// Clear the LCD and set the cursor
lcd.clear();
// move the cursor down
lcd.setCursor(0,1);
// print the blinking text
lcd.print(gameText[1]);
// Set the cursor for non blinking text
lcd.setCursor(0,0);
// since blink cleared the whole LCD
// we need print the name of the game again
lcd.print(gameText[0]);
// set blinkState to ON
blinkState = 1;
// If blinkState is OFF turn it ON
} else {
// Clear the LCD and set the cursor
lcd.clear();
// Set the cursor for non blinking text
lcd.setCursor(0,0);
// print the name of the game
lcd.print(gameText[0]);
// Set blink to OFF
blinkState = 0;
} // END: else
// store the new blink time
lastBlinkTime = millis();
} // END: if ((millis() - lastBlinkTime) > blinkDelay)
// Check if button is pressed
if (button() == 1) {
// If so start the game
gameRun = 1;
playerAlive = 1;
// reset score
score = 0;
} // END: if (button() == 1)
} // END: while game start
} // END: void gameStart
void gameExecute() {
// check wether button is pushed to run game
while (gameRun == 1) {
lcd.clear();
lcd.print(gameText[2]);
// small delay to get ready
delay(2000);
// Check if a zombie is here
if (zombieState == 1) {
// tell the player that a zombie is here
lcd.clear();
lcd.print(gameText[3]);
// Set the strenght and speed of of the zombie
zombie = random(1,5);
// Resets the zombie timer
// this one is for the speed of which the zombie attacks
lastZombieTime = millis();
// The action part
while (zombie > 0) {
/* // DEBUG
Serial.print("Str: ");
Serial.print(zombie, DEC);
Serial.print(" Time: ");
Serial.print(zombieBite);
Serial.print(" Bite: ");
Serial.println((millis() - lastZombieTime));
*/ // END: DEBUG
// Check if button is pressed
if (button() == 1) {
// If so remove 1 health from zombie
zombie--;
}
// check if the zombie bites
if ((millis() - lastZombieTime) > zombieBite) {
// player is bitten and killed
// Print information on death
lcd.clear();
lcd.print(gameText[5]);
delay(2000);
// Make sure all loops are exited
gameRun = 0;
zombie = 0;
}
} // END: while zombie > 0
// Add score, first check if the zombie was killed or
// it was the player who died and zombie was just reset
if (gameRun == 1) {
// The zombie was killed
zombieState = 0;
// Add to the players score
score++;
lcd.clear();
// Print information of kill
lcd.print(gameText[4]);
// Pause for a while
delay(1000);
}
} else { // END: if Zombie State and Start else
// zombieState is 0 check if enough time
// has passed since last zombie
if ((millis() - lastZombieTime) > zombieDelay) {
// A new zombie is here
zombieState = 1;
// reset random zombieDelay
zombieDelay = random(200,2000);
// reset random zombieBite time;
zombieBite = random(2000,7000);
} // END: if ((millis() - lastZombieTime) > zombieDelay)
} // END: else
} // END: while game run
} // END: gameExecute
int button() {
// read the state of the switch into a local variable:
int reading = digitalRead(buttonPin);
// check to see if you just pressed the button
// (i.e. the input went from LOW to HIGH), and you've waited
// long enough since the last press to ignore any noise:
// If the switch changed, due to noise or pressing:
if (reading != lastButtonState) {
// reset the debouncing timer
lastDebounceTime = millis();
}
if ((millis() - lastDebounceTime) > debounceDelay) {
// whatever the reading is at, it's been there for longer
// than the debounce delay, so take it as the actual current state:
buttonState = reading;
// If button is pressed down
if (buttonState == HIGH) {
// store the state of the button
buttonPress = 1;
}
// If button is up ...
if (buttonState == LOW) {
// ... and was just down i.e released
if (buttonPress == 1) {
// Store the new state of the button
buttonPress = 0;
// also save the reading since we terminate in the next step
lastButtonState = reading;
// Terminate the button() function and return
// a positive button press
return 1;
}
} // END: If buttonState == LOW
} // END: If millis...
// save the reading. Next time through the loop,
// it'll be the lastButtonState:
lastButtonState = reading;
} // END: void button()
void gameOver() {
lcd.clear();
lcd.setCursor(0,0);
// Print Game Over
lcd.print(gameText[6]);
lcd.setCursor(0,1);
// Print Score
lcd.print(gameText[7]);
lcd.print(score);
// Pause for a while
delay(4000);
}

My browser of choice is Kazehakase, a small foot print browser that can use either the Webkit or the Gecko rendering engine. It can handle alot more than Dillo but uses way less resources than Firefox. I found out about Kazehakase while reading the blog of K. Mandla.

However there is a problem. Kazehakase is not a very well documented browser. In it's manual there is a reference to using "Actions" with the browser, but the actions themselves aren't documented. Luckily enough I found out how to start Kazehakase in full screen mode by some trial and error.