Store › Forums › Hackvision › Game Development › Help a Newbie?
- This topic has 17 replies, 4 voices, and was last updated 10 years, 11 months ago by steeve_madison.
-
AuthorPosts
-
January 8, 2012 at 8:57 pm #484moeMember
Hi @ Everybody
I recently got Hackvision from “Santa” 😉 and because I’m interested in Software and Hardware I built it just on the next day. Thank you for the good instructions on your site. Hackvision worked great on the first try (I was surprised) and I played all day long. My dad ordered this USB to UART cable so I can program my own games, although we got a 5-pin cable he got it to work.
So currently I’m checking out the TVOut Library and I’m studying the code of other games. And here comes my first question:
The games are pretty good, but I would need a “blank” file to start with, only the necessary functions like menu and so and with a function to work with, cause the whole thing is a bit confusing for the first time (normally I work with Python).
Second One:
So yeah I got some little things to work, but there’s a strange failure with the menu. So look at my code:
In menu call function draw()
.
.
.
void draw() {
tv.print(10,10,”TEST”);
tv.delay(3600);
setup();
}But after I call the setup() function the menu looks weird and the font displaced
Before:
-> TEST
After:
-TE ST
>
(I Attached a Picture)
Excuse my mistakes, I’m not a native speaker… So I hope you guys could help me, thanks a lot! And yeah Hackvision ROCKS 😉January 9, 2012 at 2:52 pm #1133MichaelKeymasterMoe,
Glad you are trying to make your own game! Starting with the existing games is a good way to start. There are some simple ones on the games page:
http://nootropicdesign.com/hackvision/games.htmlWhy are you calling setup()? That is for initialization of the Arduino program and is only called when the program starts.
Maybe you should post your whole program, and enclose in code formatting so it is easy to read. Use the button “Code” when you post…
January 9, 2012 at 4:32 pm #1134trodossMemberMoe,
Here is code that might help a little. It is code from one of my sketches that I tried to make a little more “generic”
/*
Game Template (?) -- At least maybe a start
*/
#include
#include
#include
#include
#define SCREENWIDTH 128
#define SCREENHEIGHT 96
boolean palJumper;
#define PALpin 12
#define PAUSE_BETWEEN_ACTIONS 100
#define GAME_TITLE 0
#define GAME_PLAYING 1
#define GAME_WON 3
#define GAME_OVER 4
TVout TV;
char game_state;
void setup()
{
palJumper = digitalRead( PALpin );
TV.begin( palJumper ? NTSC : PAL, SCREENWIDTH, SCREENHEIGHT );
TV.select_font(font4x6);
//generate random number (ouside of standard Arduino random, because
//it needs to be 'read' in code outside the main sketch
randomSeed(analogRead(0));
start_title();
}
void loop()
{
static long next_action = 0;
switch (game_state)
{
case GAME_TITLE:
case GAME_OVER:
case GAME_WON:
if (TV.millis() >= next_action)
{
//wait for a button to be pressed to continue
if (Controller.firePressed())
{
//check to see what we are doing when we start the game
if (game_state > GAME_TITLE)
{
if (game_state == GAME_WON)
{
//if the game has been won, handle action here
} else {
//if the game was just over (died) then reset game elements
}
}
game_state = GAME_PLAYING;
start_game();
}
next_action = TV.millis() + PAUSE_BETWEEN_ACTIONS;
}
update_sound();
break;
case GAME_PLAYING:
if (TV.millis() >= next_action)
{
if (Controller.upPressed())
{
//handle 'up' being pressed
}
if (Controller.downPressed()){
{
//handle 'down' being pressed
}
if (Controller.rightPressed())
{
//handle 'right' being pressed
}
if (Controller.leftPressed())
{
//handle 'left' being pressed
}
if (Controller.firePressed())
{
//handle 'fire' being pressed
}
//build in a delay between actions (optional)
next_action = TV.millis() + PAUSE_BETWEEN_ACTIONS;
//handle game elements here
}
break;
}
}
void start_game()
{
//clear the contents of the screen
TV.clear_screen();
//start the game
game_state = GAME_PLAYING;
}
void start_title()
{
//clear the contents of the screen
TV.clear_screen();
//set the state to show the title screen
game_state = GAME_TITLE;
//to do: show title here
}
void start_game_over()
{
//clear the contents of the screen
TV.clear_screen();
game_state = GAME_OVER;
//to do: show game over message here
}
void start_game_won()
{
//clear the contents of the screen
TV.clear_screen();
game_state = GAME_WON;
//to do: show game won message here
}
January 9, 2012 at 9:06 pm #1135moeMemberFirst of all thanks for the quick replies!
Thanks for the example File, this is nearly exactly what I looked for but could you please add this “choice-menu” like the hackvision firmware? Thanks.
This is my first problem (strange menu):
#include
#include
#include
#include
#include
#define TV_W 136
#define TV_H 98
#define FRAME_X0 8
#define FRAME_X1 128
#define FRAME_Y0 9
#define FRAME_Y1 89
#define LEFT 3
#define RIGHT 2
#define UP 4
#define DOWN 5
#define FIRE 10
struct Segment
{
boolean active;
byte x;
byte y;
};
prog_char s0[] PROGMEM = "DRAW";
PROGMEM const char *strings[] = {s0};
char s[16]; // general string buffer
char scoreString[11];
byte currentTonePriority = 0;
void (*game)();
TVout tv;
int count = 3;
void setup() {
// If pin 12 is pulled LOW, then the PAL jumper is shorted.
pinMode(12, INPUT);
digitalWrite(12, HIGH);
if (digitalRead(12) == LOW) {
tv.begin(_PAL, TV_W, TV_H);
}
else {
tv.begin(_NTSC, TV_W, TV_H);
}
tv.select_font(font6x8);
randomSeed(analogRead(0));
byte m[1] = {0};
byte choice = menu(1, m);
if (choice == 0) {
tv.delay(10);
draw();
}
}
byte menu(byte nChoices, byte *choices) {
char choice = 0;
tv.fill(0);
byte x = 24;
byte y;
while (true) {
for(byte i=0;istrcpy_P(s, (char *)pgm_read_word(&(strings[choices])));
tv.print(32, 30+(i*8), s);
}
for(byte i=0;iy = 30+(i*8);
if (i == choice) {
// draw arrow next to selected game
tv.set_pixel(x+4, y, 1);
tv.set_pixel(x+5, y+1, 1);
tv.draw_line(x, y+2, x+6, y+2, 1);
tv.set_pixel(x+5, y+3, 1);
tv.set_pixel(x+4, y+4, 1);
}
else {
for(byte j=0;j<8;j++) {
tv.draw_line(x, y+j, x+7, y+j, 0);
}
}
}
// get input
if (pollFireButton(10)) {
playTone(1046, 20);
return choice;
}
if (Controller.upPressed()) {
choice--;
if (choice == -1) {
choice = 0;
}
else {
playTone(1046, 20);
}
}
if (Controller.downPressed()) {
choice++;
if (choice == nChoices) {
choice = nChoices-1;
}
else {
playTone(1046, 20);
}
}
}
}
void draw() {
while (Controller.firePressed()) {
tv.clear_screen();
tv.draw_line(0,0,99,99,1);
tv.delay(3600);
tv.clear_screen();
tv.delay(200);
tv.print(10,10,"TEST");
tv.delay(3600);
tv.draw_circle(tv.hres()/2,tv.vres()/2,tv.vres()/3,WHITE);
tv.delay(3600);
tv.clear_screen();
tv.print(count);
count = count-1;
}
setup();
}
boolean pollFireButton(int n) {
for(int i=0;itv.delay_frame(1);
if (Controller.firePressed()) {
return true;
}
}
return false;
}
void playTone(unsigned int frequency, unsigned long duration_ms) {
// Default is to play tone with highest priority.
playTone(frequency, duration_ms, 9);
}
void playTone(unsigned int frequency, unsigned long duration_ms, byte priority) {
// priority is value 0-9, 9 being highest priority
if (TCCR2B > 0) {
// If a tone is currently playing, check priority
if (priority < currentTonePriority) {
return;
}
}
currentTonePriority = priority;
tv.tone(frequency, duration_ms);
}
void loop() {}
Please don’t think what a noob and such things… I know programming languages and I understand much but C or C++ are a bit difficult to learn and really voluminous… So you don’t have to explain it like you would to a total newbie.
And here comes my second (I tried to combine Worms & Breakout) -> Copy&Paste doesn’t work great… I tried it on my hackvision but it only gives an annoying beep all the time… No menu, no games…. So if anyone could help me fixing this? I know maybe the sketch size is too big but if you delete all the same parts and put these two together they should probably work… Am I right? I would really like this to have on my hackvision… It should look like the original firmware of HV. Thanks and here’s the code:
January 9, 2012 at 9:09 pm #1128moeMemberSorry, more than 20000 chars…
Removed #include too much chars
#define TV_W 136
#define TV_H 98
#define FRAME_X0 8
#define FRAME_X1 128
#define FRAME_Y0 9
#define FRAME_Y1 89
#define LEFT 3
#define RIGHT 2
#define UP 4
#define DOWN 5
#define FIRE 10
const byte width = 120; //Width of screen
const byte height = 96; //Hight of screen
int dx = -1; //Initial movement of ball
int dy = -1; //Initial movement of ball
int xb; //Balls starting possition
int yb; //Balls starting possition
boolean released; //If the ball has been released by the player
boolean paused = false; //If the game has been paused
byte xPaddle; //X position of paddle
boolean isHit[5][12]; //Array of if bricks are hit or not
boolean bounced=false; //Used to fix double bounce glitch
byte lives = 3; //Amount of lives
byte level = 1; //Current level
unsigned int score=0; //Score for the game
unsigned int brickCount; //Amount of bricks hit
byte pad,pad2,pad3; //Button press buffer used to stop pause repeating
byte oldpad,oldpad2,oldpad3;
char text[16]; //General string buffer
boolean start=false; //If in menu or in game
boolean initialDraw=false;//If the inital draw has happened
char initials[3]; //Initials used in high score
//Ball Bounds used in collision detection
byte leftBall;
byte rightBall;
byte topBall;
byte bottomBall;
//Brick Bounds used in collision detection
byte leftBrick;
byte rightBrick;
byte topBrick;
byte bottomBrick;
struct Segment
{
boolean active;
byte x;
byte y;
};
prog_char s0[] PROGMEM = "ARDUINO WORM";
prog_char s1[] PROGMEM = "GAME";
prog_char s2[] PROGMEM = "OVER";
prog_char s3[] PROGMEM = "SCORE: ";
prog_char s4[] PROGMEM = "BREAKOUT";
PROGMEM const char *strings[] = {
s0,s1,s2,s3,s4};
char s[16]; // general string buffer
char scoreString[11];
byte currentTonePriority = 0;
byte ballX;
byte ballY;
byte movingDirection;
boolean ballCollected = false;
int currentScore;
int choice = true;
Segment worm[50];
void (*game)();
TVout tv;
// Allow the overall speed of the game to be adjusted.
// Higher number (like 1.5) slow the game down. Lower numbers (like 0.6) speed it up.
float speedAdjust = 1.0;
void setup() {
// If pin 12 is pulled LOW, then the PAL jumper is shorted.
pinMode(12, INPUT);
digitalWrite(12, HIGH);
if (digitalRead(12) == LOW) {
tv.begin(_PAL, TV_W, TV_H);
// Since PAL processing is faster, we need to slow the game play down.
speedAdjust = 1.4;
}
else {
tv.begin(_NTSC, TV_W, TV_H);
}
tv.select_font(font6x8);
randomSeed(analogRead(0));
playTone(1046, 20);
tv.delay(1);
playTone(1318, 20);
tv.delay(1);
playTone(1568, 20);
tv.delay(1);
playTone(2093, 20);
byte m[2] = {0,4};
byte choice = menu(2, m);
if (choice == 0) {
game = &worms;
tv.delay(10);
initWorms();
}
if (choice == 1) {
choice = false;
tv.delay(10);
initBreakout();
}
}
byte menu(byte nChoices, byte *choices) {
char choice = 0;
tv.fill(0);
byte x = 24;
byte y;
while (true) {
for(byte i=0;istrcpy_P(s, (char *)pgm_read_word(&(strings[choices])));
tv.print(32, 30+(i*8), s);
}
for(byte i=0;iy = 30+(i*8);
if (i == choice) {
// draw arrow next to selected game
tv.set_pixel(x+4, y, 1);
tv.set_pixel(x+5, y+1, 1);
tv.draw_line(x, y+2, x+6, y+2, 1);
tv.set_pixel(x+5, y+3, 1);
tv.set_pixel(x+4, y+4, 1);
}
else {
for(byte j=0;j<8;j++) {
tv.draw_line(x, y+j, x+7, y+j, 0);
}
}
}
// get input
if (pollFireButton(10)) {
playTone(1046, 20);
return choice;
}
if (Controller.upPressed()) {
choice--;
if (choice == -1) {
choice = 0;
}
else {
playTone(1046, 20);
}
}
if (Controller.downPressed()) {
choice++;
if (choice == nChoices) {
choice = nChoices-1;
}
else {
playTone(1046, 20);
}
}
}
}
void initWorms() {
for(int x = 0; x < 5; x++) {
worm[x].active = true;
worm[x].x = 30-x;
worm[x].y = 30;
}
for(int x = 5; x < 50; x++) {
worm[x].active = false;
worm[x].x = 0;
worm[x].y = 0;
}
movingDirection = RIGHT;
ballCollected = true;
currentScore = 0;
updateScore();
}
void initBreakout() {
loop();
}
void loop() {
if (choice = true){
game();
} else {
//Title screen loop switches from title screen and high scores utill FIRE is pressed
while (!start) {
start = titleScreen();
if (!start) {
start = displayHighScores(2);
}
}
//Inital level draw
if (!initialDraw){
//Clears the screen
tv.fill(0);
//Selects Font
tv.select_font(font4x6);
//Draws the new level
newLevel();
initialDraw=true;
}
if (lives>0){
drawPaddle();
//Pause game if FIRE pressed
pad = Controller.firePressed();
if(pad ==1 && oldpad==0 && released){
oldpad2=1; //Forces pad loop 2 to run once
pause();
}
oldpad=pad;
drawBall1();
if(brickCount==60){
level++;
newLevel();
}
}
else{
drawGameOver();
if (score > 0) {
tv.select_font(font6x8);
enterHighScore(2);
}
tv.select_font(font4x6);
drawGameOver();
initialDraw=false;
start=false;
lives=3;
score=0;
newLevel();
}
tv.delay_frame(1);
}
}
void worms() {
tv.fill(0);
drawFrame();
drawScore();
drawBall();
for(int x = 0; x < 50; x++) {
if(worm[x].active)
tv.set_pixel(worm[x].x,worm[x].y,1);
}
move();
tv.delay_frame(4);
}
void drawBall() {
if(ballCollected) {
ballX = random(FRAME_X0+1, FRAME_X1-1);
ballY = random(FRAME_Y0+1, FRAME_Y1-1);
ballCollected = false;
}
tv.set_pixel(ballX,ballY,1);
}
void drawFrame() {
tv.draw_line(FRAME_X0, FRAME_Y0, FRAME_X1, FRAME_Y0, 1);
tv.draw_line(FRAME_X0, FRAME_Y1, FRAME_X1, FRAME_Y1, 1);
tv.draw_line(FRAME_X0, FRAME_Y0, FRAME_X0, FRAME_Y1, 1);
tv.draw_line(FRAME_X1, FRAME_Y0, FRAME_X1, FRAME_Y1, 1);
}
void move() {
if (Controller.upPressed()) {
if(movingDirection != DOWN) {
movingDirection = UP;
}
}
else if(Controller.downPressed()) {
if(movingDirection != UP) {
movingDirection = DOWN;
}
}
else if(Controller.leftPressed()) {
if(movingDirection != RIGHT) {
movingDirection = LEFT;
}
}
else if(Controller.rightPressed()) {
if(movingDirection != LEFT) {
movingDirection = RIGHT;
}
}
switch (movingDirection) {
case UP:
if(detectBallHit(worm[0].x, worm[0].y-1)) {
assignBallCoordinatesToFirstSegment();
}
else {
shiftSegments();
worm[0].y = worm[0].y-1 ;
}
break;
case DOWN:
if(detectBallHit(worm[0].x, worm[0].y+1)){
assignBallCoordinatesToFirstSegment();
}
else {
shiftSegments();
worm[0].y = worm[0].y+1 ;
}
break;
case LEFT:
if(detectBallHit(worm[0].x-1, worm[0].y)){
assignBallCoordinatesToFirstSegment();
}
else {
shiftSegments();
worm[0].x = worm[0].x-1 ;
}
break;
case RIGHT:
if(detectBallHit(worm[0].x+1, worm[0].y)){
assignBallCoordinatesToFirstSegment();
}
else {
shiftSegments();
worm[0].x = worm[0].x+1 ;
}
break;
}
if(worm[0].x > FRAME_X1-1 || worm[0].x < FRAME_X0+1 || worm[0].y > FRAME_Y1-1 || worm[0].y < FRAME_Y0+1 || checkForTailBite()) {
gameOver();
initWorms();
return;
}
}
boolean checkForTailBite() {
for(int x = 1; x < 50; x++) {
if(worm[0].x == worm[x].x && worm[0].y == worm[x].y) {
return true;
}
}
return false;
}
void shiftSegments() {
for(int x = 49; x > 0; x--) {
if(worm[x].active) {
worm[x].x = worm[x-1].x;
worm[x].y = worm[x-1].y;
}
}
}
void assignBallCoordinatesToFirstSegment() {
worm[0].x = ballX;
worm[0].y = ballY;
}
boolean detectBallHit(byte sx, byte sy) {
if(sx == ballX && sy == ballY) {
for(int x = 49; x > -1; x--) {
if(worm[x].active) {
worm[x+1].active = true;
worm[x+1].x = worm[x].x;
worm[x+1].y = worm[x].y;
}
}
ballCollected = true;
currentScore += 1;
updateScore();
playTone(1046, 10);
tv.delay(1);
playTone(1318, 10);
tv.delay(1);
playTone(1568, 10);
tv.delay(1);
playTone(2093, 10);
return true;
}
else {
return false;
}
}
void drawScore() {
tv.print(FRAME_X0, 0, scoreString);
}
void updateScore() {
strcpy_P(scoreString, (char *)pgm_read_word(&(strings[3])));
sprintf(s, "%d", currentScore);
strcat(scoreString, s);
}
void gameOver() {
tv.delay(1000);
tv.fill(0);
strcpy_P(s, (char *)pgm_read_word(&(strings[1])));
tv.print(44, 40, s);
strcpy_P(s, (char *)pgm_read_word(&(strings[2])));
tv.print(72, 40, s);
tv.delay(3000);
}
boolean pollFireButton(int n) {
for(int i=0;itv.delay_frame(1);
if (Controller.firePressed()) {
return true;
}
}
return false;
}
void playTone(unsigned int frequency, unsigned long duration_ms) {
// Default is to play tone with highest priority.
playTone(frequency, duration_ms, 9);
}
void playTone(unsigned int frequency, unsigned long duration_ms, byte priority) {
// priority is value 0-9, 9 being highest priority
if (TCCR2B > 0) {
// If a tone is currently playing, check priority
if (priority < currentTonePriority) {
return;
}
}
currentTonePriority = priority;
tv.tone(frequency, duration_ms);
}
void movePaddle()
{
//Move right
if(xPaddle < width-12)
{
if(Controller.rightPressed())
{
xPaddle++;
}
}
//Move left
if(xPaddle > 0)
{
if(Controller.leftPressed())
{
xPaddle--;
}
}
}
void moveBall()
{
if(released)
{
//Move ball
xb=xb + dx;
yb=yb + dy;
//Set bounds
leftBall = xb;
rightBall = xb+2;
topBall = yb;
bottomBall = yb+2;
//Bounce off top edge
if (yb<=0)
{
yb=2;
dy=-dy;
tv.tone(523, 20);
}
//Lose a life if bottom edge hit
if (yb>=89)
{
tv.draw_rect(xPaddle,87,11,1,0);
xPaddle = 54;
yb=84;
released = false;
lives--;
drawLives();
tv.tone(175, 20);
if (random(0, 2) == 0) {
dx = 1;
}
else {
dx = -1;
}
}
//Bounce off left side
if (xb<=0)
{
xb=2;
dx=-dx;
tv.tone(523, 20);
}
//Bounce off right side
if (xb>=width-2)
{
xb=width-4;
dx=-dx;
tv.tone(523, 20);
}
//Bounce off paddle
if (xb+1>=xPaddle && xb<=xPaddle+12 && yb+2>=87 && yb<=92){
dy=-dy;
dx=((xb-(xPaddle+6))/3); //Applies spin on the ball
tv.tone(200, 20);
}
//Bounce off Bricks
for (byte row = 0; row < 5; row++) {
for (byte column = 0; column < 12; column++) {
if (!isHit[row][column]){
//Sets Brick bounds
leftBrick = 10*column;
rightBrick = 10*column+10;
topBrick = 6*row+1;
bottomBrick = 6*row+7;
//If A collison has occured
if (topBall <= bottomBrick && bottomBall>=topBrick && leftBall<=rightBrick && rightBall >=leftBrick){
Score();
brickCount++;
isHit[row][column] = true;
tv.draw_rect(10*column ,2+6*row,8,4,0);
//Vertical collision
if (bottomBall>bottomBrick || topBall//Only bounce once each ball move
if(!bounced){
dy=-dy;
yb+=dy;
bounced=true;
tv.tone(261, 20);
}
}
//Hoizontal collision
if (leftBallrightBrick){
//Only bounce once brick each ball move
if(!bounced){
dx=-dx;
xb+=dx;
bounced=true;
tv.tone(261, 20);
}
}
}
}
}
}
//Reset Bounce
bounced=false;
}
else{
//Ball follows paddle
xb=xPaddle+5;
//Release ball if FIRE pressed
pad3 = Controller.firePressed();
if(pad3 ==1 && oldpad3==0){
released=true;
//Apply random direction to ball on release
if (random(0, 2) == 0) {
dx = 1;
}
else {
dx = -1;
}
//Makes sure the ball heads upwards
dy = -1;
}
oldpad3=pad3;
}
}
void drawBall1()
{
tv.set_pixel(xb,yb,0);
tv.set_pixel(xb+1,yb,0);
tv.set_pixel(xb,yb+1,0);
tv.set_pixel(xb+1,yb+1,0);
moveBall();
tv.set_pixel(xb,yb,1);
tv.set_pixel(xb+1,yb,1);
tv.set_pixel(xb,yb+1,1);
tv.set_pixel(xb+1,yb+1,1);
}
void drawPaddle()
{
tv.draw_rect(xPaddle,87,11,1,0);
movePaddle();
tv.draw_rect(xPaddle,87,11,1,1);
}
void drawLives()
{
sprintf(text, "LIVES:%u", lives);
tv.print(0, 90, text);
}
void drawGameOver()
{
tv.set_pixel(xb,yb,0);
tv.set_pixel(xb+1,yb,0);
tv.set_pixel(xb,yb+1,0);
tv.set_pixel(xb+1,yb+1,0);
tv.print(52, 42, "Game");
tv.print(52, 54, "Over");
delay(4000);
}
void pause()
{
paused = true;
//Draw pause to the screen
tv.print(52, 45, "PAUSE");
while(paused){
delay(15);
//Unpause if FIRE is pressed
pad2 = Controller.firePressed();
if(pad2 ==1 && oldpad2==0 && released){
tv.print(52, 45, " ");
paused=false;
}
oldpad2=pad2;
}
}
void Score()
{
score += (level*10);
sprintf(text, "SCORE:%u", score);
tv.print(80, 90, text);
}
void newLevel(){
//Undraw paddle
tv.draw_rect(xPaddle,87,11,1,0);
//Undraw ball
tv.set_pixel(xb,yb,0);
tv.set_pixel(xb+1,yb,0);
tv.set_pixel(xb,yb+1,0);
tv.set_pixel(xb+1,yb+1,0);
//Alter various variables to reset the game
xPaddle = 54;
yb=84;
brickCount=0;
released=false;
//Draws new bricks and resets their values
for (byte row = 0; row < 5; row++) {
for (byte column = 0; column < 12; column++) {
isHit[row][column] = false;
tv.draw_rect(10*column ,2+6*row,8,4,1);
}
}
//Draws the initial lives
drawLives();
//Draws the initial score
sprintf(text, "SCORE:%u", score);
tv.print(80, 90, text);
}
//Function by nootropic design to display highscores
boolean displayHighScores(byte file) {
byte y = 10;
byte x = 24;
// Each block of EEPROM has 10 high scores, and each high score entry
// is 5 bytes long: 3 bytes for initials and two bytes for score.
int address = file*10*5;
byte hi, lo;
tv.fill(0);
tv.select_font(font6x8);
tv.print(32, 0, "HIGH SCORES");
for(int i=0;i<10;i++) {
sprintf(text, "%2d", i+1);
tv.print(x, y+(i*8), text);
hi = EEPROM.read(address + (5*i));
lo = EEPROM.read(address + (5*i) + 1);
if ((hi == 0xFF) && (lo == 0xFF)) {
score = 0;
}
else {
score = (hi << 8) | lo;
}
initials[0] = (char)EEPROM.read(address + (5*i) + 2);
initials[1] = (char)EEPROM.read(address + (5*i) + 3);
initials[2] = (char)EEPROM.read(address + (5*i) + 4);
if (score > 0) {
sprintf(text, "%c%c%c %u", initials[0], initials[1], initials[2], score);
tv.print(x+24, y+(i*8), text);
}
}
if (pollFireButton(300)) {
return true;
}
return false;
}
boolean titleScreen() {
//Clears the screen
tv.fill(0);
//Draws the main title
tv.bitmap(0, 0, title);
if (pollFireButton(25)) {
return true;
}
//Flash "Press FIRE" 5 times
for(byte i=0;i<5;i++){
//Draws "Press FIRE"
tv.bitmap(31, 53, fire);
if (pollFireButton(50)) {
return true;
}
//Removes "Press FIRE"
tv.draw_rect(31,53,57,8,0,0);
if (pollFireButton(25)) {
return true;
}
}
return false;
}
//Function by nootropic design to add high scores
void enterInitials() {
char index = 0;
tv.fill(0);
tv.print(16, 0, "HIGH SCORE");
sprintf(text, "%u", score);
tv.print(88, 0, text);
initials[0] = ' ';
initials[1] = ' ';
initials[2] = ' ';
while (true) {
tv.print_char(56, 20, initials[0]);
tv.print_char(64, 20, initials[1]);
tv.print_char(72, 20, initials[2]);
for(byte i=0;i<3;i++) {
tv.draw_line(56 + (i*8), 27, 56 + (i*8) + 6, 27, 1);
}
tv.draw_line(56, 28, 88, 28, 0);
tv.draw_line(56 + (index*8), 28, 56 + (index*8) + 6, 28, 1);
tv.delay(150);
if (Controller.leftPressed()) {
index--;
if (index < 0) {
index = 0;
} else {
tv.tone(1046, 20);
}
}
if (Controller.rightPressed()) {
index++;
if (index > 2) {
index = 2;
} else {
tv.tone(1046, 20);
}
}
if (Controller.upPressed()) {
initials[index]++;
tv.tone(523, 20);
// A-Z 0-9 :-? !-/ ' '
if (initials[index] == '0') {
initials[index] = ' ';
}
if (initials[index] == '!') {
initials[index] = 'A';
}
if (initials[index] == '[') {
initials[index] = '0';
}
if (initials[index] == '@') {
initials[index] = '!';
}
}
if (Controller.downPressed()) {
initials[index]--;
tv.tone(523, 20);
if (initials[index] == ' ') {
initials[index] = '?';
}
if (initials[index] == '/') {
initials[index] = 'Z';
}
if (initials[index] == 31) {
initials[index] = '/';
}
if (initials[index] == '@') {
initials[index] = ' ';
}
}
if (Controller.firePressed()) {
if (index < 2) {
index++;
tv.tone(1046, 20);
} else {
tv.tone(1046, 20);
return;
}
}
}
}
void enterHighScore(byte file) {
// Each block of EEPROM has 10 high scores, and each high score entry
// is 5 bytes long: 3 bytes for initials and two bytes for score.
int address = file*10*5;
byte hi, lo;
char tmpInitials[3];
unsigned int tmpScore = 0;
// High score processing
for(byte i=0;i<10;i++) {
hi = EEPROM.read(address + (5*i));
lo = EEPROM.read(address + (5*i) + 1);
if ((hi == 0xFF) && (lo == 0xFF)) {
// The values are uninitialized, so treat this entry
// as a score of 0.
tmpScore = 0;
} else {
tmpScore = (hi << 8) | lo;
}
if (score > tmpScore) {
enterInitials();
for(byte j=i;j<10;j++) {
hi = EEPROM.read(address + (5*j));
lo = EEPROM.read(address + (5*j) + 1);
if ((hi == 0xFF) && (lo == 0xFF)) {
tmpScore = 0;
} else {
tmpScore = (hi << 8) | lo;
}
tmpInitials[0] = (char)EEPROM.read(address + (5*j) + 2);
tmpInitials[1] = (char)EEPROM.read(address + (5*j) + 3);
tmpInitials[2] = (char)EEPROM.read(address + (5*j) + 4);
// tmpScore and tmpInitials now hold what we want to write in the next slot.
// write score and initials to current slot
EEPROM.write(address + (5*j), ((score >> 8) & 0xFF));
EEPROM.write(address + (5*j) + 1, (score & 0xFF));
EEPROM.write(address + (5*j) + 2, initials[0]);
EEPROM.write(address + (5*j) + 3, initials[1]);
EEPROM.write(address + (5*j) + 4, initials[2]);
score = tmpScore;
initials[0] = tmpInitials[0];
initials[1] = tmpInitials[1];
initials[2] = tmpInitials[2];
}
score = 0;
initials[0] = ' ';
initials[1] = ' ';
initials[2] = ' ';
return;
}
}
}January 9, 2012 at 9:16 pm #1121MichaelKeymasterMoe,
The structure of this code is not right. The function setup() is automatically called once when your Arduino program starts to run. The purpose of setup() is to initialize things. Do not call draw() from setup().Your function draw() is calling setup() (which in turn calls draw(), which calls setup())….see what’s happening here? This is infinite recursion which will blow the call stack very quickly.
Your loop() function is what gets called over and over in Arduino programs. The general structure of an Arduino program is to have setup() for initialization, then loop() for continuous running of the code.
So take draw() out of setup() and move it to loop(). And take setup() out of draw().
Make sense?
January 9, 2012 at 9:41 pm #1122MichaelKeymasterRegarding your latest code post:
void initBreakout() {
loop();
}don’t call loop() in your code (ever). loop() is automatically called over and over again in Arduino programs.
You have a bug here where you use assignment (=) instead of comparison (==):
if (choice = true){
I think you’re not understanding the structure of Arduino programs in general. Your loop() method needs to call either worms() or breakout(), depending on which game was chosen. You need to move the breakout game logic out of loop() and into a function called breakout(). If breakout is chosen from the menu, set
game = &breakout;
just like was done with game = &worms;
Your loop() should invoke the function pointer “game” by calling it:
void loop() {
game();
}Does this make sense? Just follow the pattern in the Hackvision.pde firmware.
January 9, 2012 at 9:52 pm #1123moeMemberWow this makes totally sense to me. Yes you are completely right. Ok I’ll try to fix that! Thanks for the good explanation and yeah this is all really new to me… So can you say that loop is kinda something like a “main” function ? And yes I wasn’t sure with these == and = because this is different in some other p. Languages… And the
game = &breakout;
is calling the function breakout ? What’s the difference between void and byte ? Thank you!
January 9, 2012 at 10:13 pm #1124MichaelKeymasterNo, loop() is not like main(). Function main() is called once when a program starts. Function loop() is called over and over again (by main()). Just remember that Arduino programs are just C/C++ programs with the main() hidden. You should study Arduino and C/C++ before going much further with game development.
The code:
game = &breakout;
does not call the function breakout(). It makes the function pointer “game” point to the function “breakout”.
What’s the difference between void and byte ?
Byte is an 8-bit data type. void means “no type” or “does not return a value”. This is basic C stuff and I think you will need to learn more C, or you’ll really not get far. (sorry)
January 10, 2012 at 3:44 am #1125trodossMemberJust remember that Arduino programs are just C/C++ programs with the main() hidden. You should study Arduino and C/C++ before going much further with game development.
My recommendation would be to start with some examples on the Arduino site:
http://arduino.cc/en/Tutorial/HomePageThat should help you get a feel for how the Arduino “works.” Syntax is C/C++ as Michael has mentioned. It may answer for you some of the questions you have. If you are comfortable you are with the basic syntax, then certainly try some of the more advanced syntax. C++ is notorious for things like “short-circuit evaluation” that you don’t *have* to use, yet you are bound to see. Since you are already familiar with Python, you may be more used to whitespace indention rather than {} (curly braces) to indicate “scope.” Keeping track of when you have started/ended a code block, and tracing where you have missed a “}” might take a while to get used to, but you will get used to it. I would recommend learning C/C-like syntax regardless, as it will serve you well in the future 😉 The Arduino environment (IDE/syntax) is a great place to get started. Since you are not new to programming, you should pick it up fast.
I would avoid function pointers and interrupt handlers for now. Looking at the “wiring under the board” so to speak can be confusing to follow unless you have a solid understanding of how the underlying gcc compiler (avr-gcc) works. Frankly you can get by (and may be perfectly happy) not going to that low of a level.
If you feel comfortable with the syntax, you might want to investigate the examples in the TVOut library as well. Try a few simple things and build off of them (maybe .print(), .draw_circle(), .draw_line() …etc.) In doing so it will help you understand some of the syntax, and how to use the data types.
Hopefully that helps.
January 10, 2012 at 6:09 pm #1126moeMemberSooooo yes I finished my game fusion! I followed what Michael said and yes it works.. I moved the code out of loop and integrated the function and the menu. And yes the Breakout-Game works, the Worms also, but the problem is the frame doesn’t show up correctly. Maybe you can try this code on your on Hackvision to see it (or I can upload a pic). But at least it’s playable and my “creation” works. I also tried the other “project” and now I understand the “syntax” of Arduino (at least a little bit). I want to thank you Michael and Trodoss again you guys did great work and yeah thanks helping me. Oh and yes and Michael if you plan to make a Hackvision v2: If it’s possible, could you please add a on/off switch? But thats just an idea I wanted to share..
Code in next post…
January 10, 2012 at 6:10 pm #1127moeMember
#define TV_W 136
#define TV_H 98
#define FRAME_X0 8
#define FRAME_X1 128
#define FRAME_Y0 9
#define FRAME_Y1 89
#define LEFT 3
#define RIGHT 2
#define UP 4
#define DOWN 5
#define FIRE 10
struct Segment
{
boolean active;
byte x;
byte y;
};
prog_char s0[] PROGMEM = "ARDUINO WORM";
prog_char s1[] PROGMEM = "GAME";
prog_char s2[] PROGMEM = "OVER";
prog_char s3[] PROGMEM = "SCORE: ";
prog_char s4[] PROGMEM = "BREAKOUT";
PROGMEM const char *strings[] = {
s0,s1,s2,s3,s4};
char s[16]; // general string buffer
char scoreString[11];
byte currentTonePriority = 0;
byte ballX;
byte ballY;
byte movingDirection;
boolean ballCollected = false;
int currentScore;
Segment worm[50];
void (*game)();
TVout TV;
const byte width = 120; //Width of screen
const byte height = 96; //Hight of screen
int dx = -1; //Initial movement of ball
int dy = -1; //Initial movement of ball
int xb; //Balls starting possition
int yb; //Balls starting possition
boolean released; //If the ball has been released by the player
boolean paused = false; //If the game has been paused
byte xPaddle; //X position of paddle
boolean isHit[5][12]; //Array of if bricks are hit or not
boolean bounced=false; //Used to fix double bounce glitch
byte lives = 3; //Amount of lives
byte level = 1; //Current level
unsigned int score=0; //Score for the game
unsigned int brickCount; //Amount of bricks hit
byte pad,pad2,pad3; //Button press buffer used to stop pause repeating
byte oldpad,oldpad2,oldpad3;
char text[16]; //General string buffer
boolean start=false; //If in menu or in game
boolean initialDraw=false;//If the inital draw has happened
char initials[3]; //Initials used in high score
//Ball Bounds used in collision detection
byte leftBall;
byte rightBall;
byte topBall;
byte bottomBall;
//Brick Bounds used in collision detection
byte leftBrick;
byte rightBrick;
byte topBrick;
byte bottomBrick;
float speedAdjust = 1.0;
void setup()
{
// If pin 12 is pulled LOW, then the PAL jumper is shorted.
pinMode(12, INPUT);
digitalWrite(12, HIGH);
//Set video mode
if (digitalRead(12) == LOW) {
TV.begin(PAL,width,height);
}
else {
TV.begin(NTSC,width,height);
}
TV.select_font(font6x8);
randomSeed(analogRead(0));
playTone(1046, 20);
TV.delay(1);
playTone(1318, 20);
TV.delay(1);
playTone(1568, 20);
TV.delay(1);
playTone(2093, 20);
byte m[2] = {0, 4};
byte choice = menu(2, m);
if (choice == 0) {
game = &worms;
TV.delay(10);
initWorms();
}
if (choice == 1) {
game = &breakout;
TV.delay(10);
}
}
byte menu(byte nChoices, byte *choices) {
char choice = 0;
TV.fill(0);
byte x = 24;
byte y;
while (true) {
for(byte i=0;istrcpy_P(s, (char *)pgm_read_word(&(strings[choices])));
TV.print(32, 30+(i*8), s);
}
for(byte i=0;iy = 30+(i*8);
if (i == choice) {
// draw arrow next to selected game
TV.set_pixel(x+4, y, 1);
TV.set_pixel(x+5, y+1, 1);
TV.draw_line(x, y+2, x+6, y+2, 1);
TV.set_pixel(x+5, y+3, 1);
TV.set_pixel(x+4, y+4, 1);
}
else {
for(byte j=0;j<8;j++) {
TV.draw_line(x, y+j, x+7, y+j, 0);
}
}
}
// get input
if (pollFireButtonWorm(10)) {
playTone(1046, 20);
return choice;
}
if (Controller.upPressed()) {
choice--;
if (choice == -1) {
choice = 0;
}
else {
playTone(1046, 20);
}
}
if (Controller.downPressed()) {
choice++;
if (choice == nChoices) {
choice = nChoices-1;
}
else {
playTone(1046, 20);
}
}
}
}
void initWorms() {
for(int x = 0; x < 5; x++) {
worm[x].active = true;
worm[x].x = 30-x;
worm[x].y = 30;
}
for(int x = 5; x < 50; x++) {
worm[x].active = false;
worm[x].x = 0;
worm[x].y = 0;
}
movingDirection = RIGHT;
ballCollected = true;
currentScore = 0;
updateScore();
}
void loop() {
game();
}
void worms() {
TV.fill(0);
drawFrame();
drawScore();
drawBallWorm();
for(int x = 0; x < 50; x++) {
if(worm[x].active)
TV.set_pixel(worm[x].x,worm[x].y,1);
}
move();
TV.delay_frame(4);
}
void drawBallWorm() {
if(ballCollected) {
ballX = random(FRAME_X0+1, FRAME_X1-1);
ballY = random(FRAME_Y0+1, FRAME_Y1-1);
ballCollected = false;
}
TV.set_pixel(ballX,ballY,1);
}
void drawFrame() {
TV.draw_line(FRAME_X0, FRAME_Y0, FRAME_X1, FRAME_Y0, 1);
TV.draw_line(FRAME_X0, FRAME_Y1, FRAME_X1, FRAME_Y1, 1);
TV.draw_line(FRAME_X0, FRAME_Y0, FRAME_X0, FRAME_Y1, 1);
TV.draw_line(FRAME_X1, FRAME_Y0, FRAME_X1, FRAME_Y1, 1);
}
void move() {
if (Controller.upPressed()) {
if(movingDirection != DOWN) {
movingDirection = UP;
}
}
else if(Controller.downPressed()) {
if(movingDirection != UP) {
movingDirection = DOWN;
}
}
else if(Controller.leftPressed()) {
if(movingDirection != RIGHT) {
movingDirection = LEFT;
}
}
else if(Controller.rightPressed()) {
if(movingDirection != LEFT) {
movingDirection = RIGHT;
}
}
switch (movingDirection) {
case UP:
if(detectBallHit(worm[0].x, worm[0].y-1)) {
assignBallCoordinatesToFirstSegment();
}
else {
shiftSegments();
worm[0].y = worm[0].y-1 ;
}
break;
case DOWN:
if(detectBallHit(worm[0].x, worm[0].y+1)){
assignBallCoordinatesToFirstSegment();
}
else {
shiftSegments();
worm[0].y = worm[0].y+1 ;
}
break;
case LEFT:
if(detectBallHit(worm[0].x-1, worm[0].y)){
assignBallCoordinatesToFirstSegment();
}
else {
shiftSegments();
worm[0].x = worm[0].x-1 ;
}
break;
case RIGHT:
if(detectBallHit(worm[0].x+1, worm[0].y)){
assignBallCoordinatesToFirstSegment();
}
else {
shiftSegments();
worm[0].x = worm[0].x+1 ;
}
break;
}
if(worm[0].x > FRAME_X1-1 || worm[0].x < FRAME_X0+1 || worm[0].y > FRAME_Y1-1 || worm[0].y < FRAME_Y0+1 || checkForTailBite()) {
gameOver();
initWorms();
return;
}
}
boolean checkForTailBite() {
for(int x = 1; x < 50; x++) {
if(worm[0].x == worm[x].x && worm[0].y == worm[x].y) {
return true;
}
}
return false;
}
void shiftSegments() {
for(int x = 49; x > 0; x--) {
if(worm[x].active) {
worm[x].x = worm[x-1].x;
worm[x].y = worm[x-1].y;
}
}
}
void assignBallCoordinatesToFirstSegment() {
worm[0].x = ballX;
worm[0].y = ballY;
}
boolean detectBallHit(byte sx, byte sy) {
if(sx == ballX && sy == ballY) {
for(int x = 49; x > -1; x--) {
if(worm[x].active) {
worm[x+1].active = true;
worm[x+1].x = worm[x].x;
worm[x+1].y = worm[x].y;
}
}
ballCollected = true;
currentScore += 1;
updateScore();
playTone(1046, 10);
TV.delay(1);
playTone(1318, 10);
TV.delay(1);
playTone(1568, 10);
TV.delay(1);
playTone(2093, 10);
return true;
}
else {
return false;
}
}
void drawScore() {
TV.print(FRAME_X0, 0, scoreString);
}
void updateScore() {
strcpy_P(scoreString, (char *)pgm_read_word(&(strings[3])));
sprintf(s, "%d", currentScore);
strcat(scoreString, s);
}
void gameOver() {
TV.delay(1000);
TV.fill(0);
strcpy_P(s, (char *)pgm_read_word(&(strings[1])));
TV.print(44, 40, s);
strcpy_P(s, (char *)pgm_read_word(&(strings[2])));
TV.print(72, 40, s);
TV.delay(3000);
}
boolean pollFireButtonWorm(int n) {
for(int i=0;iTV.delay_frame(1);
if (Controller.firePressed()) {
return true;
}
}
return false;
}
void playTone(unsigned int frequency, unsigned long duration_ms) {
// Default is to play tone with highest priority.
playTone(frequency, duration_ms, 9);
}
void playTone(unsigned int frequency, unsigned long duration_ms, byte priority) {
// priority is value 0-9, 9 being highest priority
if (TCCR2B > 0) {
// If a tone is currently playing, check priority
if (priority < currentTonePriority) {
return;
}
}
currentTonePriority = priority;
TV.tone(frequency, duration_ms);
}
void movePaddle()
{
//Move right
if(xPaddle < width-12)
{
if(Controller.rightPressed())
{
xPaddle++;
}
}
//Move left
if(xPaddle > 0)
{
if(Controller.leftPressed())
{
xPaddle--;
}
}
}
void moveBall()
{
if(released)
{
//Move ball
xb=xb + dx;
yb=yb + dy;
//Set bounds
leftBall = xb;
rightBall = xb+2;
topBall = yb;
bottomBall = yb+2;
//Bounce off top edge
if (yb<=0)
{
yb=2;
dy=-dy;
TV.tone(523, 20);
}
//Lose a life if bottom edge hit
if (yb>=89)
{
TV.draw_rect(xPaddle,87,11,1,0);
xPaddle = 54;
yb=84;
released = false;
lives--;
drawLives();
TV.tone(175, 20);
if (random(0, 2) == 0) {
dx = 1;
}
else {
dx = -1;
}
}
//Bounce off left side
if (xb<=0)
{
xb=2;
dx=-dx;
TV.tone(523, 20);
}
//Bounce off right side
if (xb>=width-2)
{
xb=width-4;
dx=-dx;
TV.tone(523, 20);
}
//Bounce off paddle
if (xb+1>=xPaddle && xb<=xPaddle+12 && yb+2>=87 && yb<=92){
dy=-dy;
dx=((xb-(xPaddle+6))/3); //Applies spin on the ball
TV.tone(200, 20);
}
//Bounce off Bricks
for (byte row = 0; row < 5; row++) {
for (byte column = 0; column < 12; column++) {
if (!isHit[row][column]){
//Sets Brick bounds
leftBrick = 10*column;
rightBrick = 10*column+10;
topBrick = 6*row+1;
bottomBrick = 6*row+7;
//If A collison has occured
if (topBall <= bottomBrick && bottomBall>=topBrick && leftBall<=rightBrick && rightBall >=leftBrick){
Score();
brickCount++;
isHit[row][column] = true;
TV.draw_rect(10*column ,2+6*row,8,4,0);
//Vertical collision
if (bottomBall>bottomBrick || topBall//Only bounce once each ball move
if(!bounced){
dy=-dy;
yb+=dy;
bounced=true;
TV.tone(261, 20);
}
}
//Hoizontal collision
if (leftBallrightBrick){
//Only bounce once brick each ball move
if(!bounced){
dx=-dx;
xb+=dx;
bounced=true;
TV.tone(261, 20);
}
}
}
}
}
}
//Reset Bounce
bounced=false;
}
else{
//Ball follows paddle
xb=xPaddle+5;
//Release ball if FIRE pressed
pad3 = Controller.firePressed();
if(pad3 ==1 && oldpad3==0){
released=true;
//Apply random direction to ball on release
if (random(0, 2) == 0) {
dx = 1;
}
else {
dx = -1;
}
//Makes sure the ball heads upwards
dy = -1;
}
oldpad3=pad3;
}
}
void drawBall()
{
TV.set_pixel(xb,yb,0);
TV.set_pixel(xb+1,yb,0);
TV.set_pixel(xb,yb+1,0);
TV.set_pixel(xb+1,yb+1,0);
moveBall();
TV.set_pixel(xb,yb,1);
TV.set_pixel(xb+1,yb,1);
TV.set_pixel(xb,yb+1,1);
TV.set_pixel(xb+1,yb+1,1);
}
void drawPaddle()
{
TV.draw_rect(xPaddle,87,11,1,0);
movePaddle();
TV.draw_rect(xPaddle,87,11,1,1);
}
void drawLives()
{
sprintf(text, "LIVES:%u", lives);
TV.print(0, 90, text);
}
void drawGameOver()
{
TV.set_pixel(xb,yb,0);
TV.set_pixel(xb+1,yb,0);
TV.set_pixel(xb,yb+1,0);
TV.set_pixel(xb+1,yb+1,0);
TV.print(52, 42, "Game");
TV.print(52, 54, "Over");
delay(4000);
}
void pause()
{
paused = true;
//Draw pause to the screen
TV.print(52, 45, "PAUSE");
while(paused){
delay(15);
//Unpause if FIRE is pressed
pad2 = Controller.firePressed();
if(pad2 ==1 && oldpad2==0 && released){
TV.print(52, 45, " ");
paused=false;
}
oldpad2=pad2;
}
}
void Score()
{
score += (level*10);
sprintf(text, "SCORE:%u", score);
TV.print(80, 90, text);
}
void newLevel(){
//Undraw paddle
TV.draw_rect(xPaddle,87,11,1,0);
//Undraw ball
TV.set_pixel(xb,yb,0);
TV.set_pixel(xb+1,yb,0);
TV.set_pixel(xb,yb+1,0);
TV.set_pixel(xb+1,yb+1,0);
//Alter various variables to reset the game
xPaddle = 54;
yb=84;
brickCount=0;
released=false;
//Draws new bricks and resets their values
for (byte row = 0; row < 5; row++) {
for (byte column = 0; column < 12; column++) {
isHit[row][column] = false;
TV.draw_rect(10*column ,2+6*row,8,4,1);
}
}
//Draws the initial lives
drawLives();
//Draws the initial score
sprintf(text, "SCORE:%u", score);
TV.print(80, 90, text);
}
//Used to delay images while reading button input
boolean pollFireButton(int n) {
for(int i=0;iTV.delay(15);
pad = Controller.firePressed();
if(pad ==1 && oldpad==0){
oldpad3=1; //Forces pad loop 3 to run once
return true;
}
oldpad=pad;
}
return false;
}
//Function by nootropic design to display highscores
boolean displayHighScores(byte file) {
byte y = 10;
byte x = 24;
// Each block of EEPROM has 10 high scores, and each high score entry
// is 5 bytes long: 3 bytes for initials and two bytes for score.
int address = file*10*5;
byte hi, lo;
TV.fill(0);
TV.select_font(font6x8);
TV.print(32, 0, "HIGH SCORES");
for(int i=0;i<10;i++) {
sprintf(text, "%2d", i+1);
TV.print(x, y+(i*8), text);
hi = EEPROM.read(address + (5*i));
lo = EEPROM.read(address + (5*i) + 1);
if ((hi == 0xFF) && (lo == 0xFF)) {
score = 0;
}
else {
score = (hi << 8) | lo;
}
initials[0] = (char)EEPROM.read(address + (5*i) + 2);
initials[1] = (char)EEPROM.read(address + (5*i) + 3);
initials[2] = (char)EEPROM.read(address + (5*i) + 4);
if (score > 0) {
sprintf(text, "%c%c%c %u", initials[0], initials[1], initials[2], score);
TV.print(x+24, y+(i*8), text);
}
}
if (pollFireButton(300)) {
return true;
}
return false;
}
boolean titleScreen() {
//Clears the screen
TV.fill(0);
//Draws the main title
TV.bitmap(0, 0, title);
if (pollFireButton(25)) {
return true;
}
//Flash "Press FIRE" 5 times
for(byte i=0;i<5;i++){
//Draws "Press FIRE"
TV.bitmap(31, 53, fire);
if (pollFireButton(50)) {
return true;
}
//Removes "Press FIRE"
TV.draw_rect(31,53,57,8,0,0);
if (pollFireButton(25)) {
return true;
}
}
return false;
}
//Function by nootropic design to add high scores
void enterInitials() {
char index = 0;
TV.fill(0);
TV.print(16, 0, "HIGH SCORE");
sprintf(text, "%u", score);
TV.print(88, 0, text);
initials[0] = ' ';
initials[1] = ' ';
initials[2] = ' ';
while (true) {
TV.print_char(56, 20, initials[0]);
TV.print_char(64, 20, initials[1]);
TV.print_char(72, 20, initials[2]);
for(byte i=0;i<3;i++) {
TV.draw_line(56 + (i*8), 27, 56 + (i*8) + 6, 27, 1);
}
TV.draw_line(56, 28, 88, 28, 0);
TV.draw_line(56 + (index*8), 28, 56 + (index*8) + 6, 28, 1);
TV.delay(150);
if (Controller.leftPressed()) {
index--;
if (index < 0) {
index = 0;
} else {
TV.tone(1046, 20);
}
}
if (Controller.rightPressed()) {
index++;
if (index > 2) {
index = 2;
} else {
TV.tone(1046, 20);
}
}
if (Controller.upPressed()) {
initials[index]++;
TV.tone(523, 20);
// A-Z 0-9 :-? !-/ ' '
if (initials[index] == '0') {
initials[index] = ' ';
}
if (initials[index] == '!') {
initials[index] = 'A';
}
if (initials[index] == '[') {
initials[index] = '0';
}
if (initials[index] == '@') {
initials[index] = '!';
}
}
if (Controller.downPressed()) {
initials[index]--;
TV.tone(523, 20);
if (initials[index] == ' ') {
initials[index] = '?';
}
if (initials[index] == '/') {
initials[index] = 'Z';
}
if (initials[index] == 31) {
initials[index] = '/';
}
if (initials[index] == '@') {
initials[index] = ' ';
}
}
if (Controller.firePressed()) {
if (index < 2) {
index++;
TV.tone(1046, 20);
} else {
TV.tone(1046, 20);
return;
}
}
}
}
void enterHighScore(byte file) {
// Each block of EEPROM has 10 high scores, and each high score entry
// is 5 bytes long: 3 bytes for initials and two bytes for score.
int address = file*10*5;
byte hi, lo;
char tmpInitials[3];
unsigned int tmpScore = 0;
// High score processing
for(byte i=0;i<10;i++) {
hi = EEPROM.read(address + (5*i));
lo = EEPROM.read(address + (5*i) + 1);
if ((hi == 0xFF) && (lo == 0xFF)) {
// The values are uninitialized, so treat this entry
// as a score of 0.
tmpScore = 0;
} else {
tmpScore = (hi << 8) | lo;
}
if (score > tmpScore) {
enterInitials();
for(byte j=i;j<10;j++) {
hi = EEPROM.read(address + (5*j));
lo = EEPROM.read(address + (5*j) + 1);
if ((hi == 0xFF) && (lo == 0xFF)) {
tmpScore = 0;
} else {
tmpScore = (hi << 8) | lo;
}
tmpInitials[0] = (char)EEPROM.read(address + (5*j) + 2);
tmpInitials[1] = (char)EEPROM.read(address + (5*j) + 3);
tmpInitials[2] = (char)EEPROM.read(address + (5*j) + 4);
// tmpScore and tmpInitials now hold what we want to write in the next slot.
// write score and initials to current slot
EEPROM.write(address + (5*j), ((score >> 8) & 0xFF));
EEPROM.write(address + (5*j) + 1, (score & 0xFF));
EEPROM.write(address + (5*j) + 2, initials[0]);
EEPROM.write(address + (5*j) + 3, initials[1]);
EEPROM.write(address + (5*j) + 4, initials[2]);
score = tmpScore;
initials[0] = tmpInitials[0];
initials[1] = tmpInitials[1];
initials[2] = tmpInitials[2];
}
score = 0;
initials[0] = ' ';
initials[1] = ' ';
initials[2] = ' ';
return;
}
}
}
void breakout()
{
//Title screen loop switches from title screen and high scores utill FIRE is pressed
while (!start) {
start = titleScreen();
if (!start) {
start = displayHighScores(2);
}
}
//Inital level draw
if (!initialDraw){
//Clears the screen
TV.fill(0);
//Selects Font
TV.select_font(font4x6);
//Draws the new level
newLevel();
initialDraw=true;
}
if (lives>0){
drawPaddle();
//Pause game if FIRE pressed
pad = Controller.firePressed();
if(pad ==1 && oldpad==0 && released){
oldpad2=1; //Forces pad loop 2 to run once
pause();
}
oldpad=pad;
drawBall();
if(brickCount==60){
level++;
newLevel();
}
}
else{
drawGameOver();
if (score > 0) {
TV.select_font(font6x8);
enterHighScore(2);
}
TV.select_font(font4x6);
drawGameOver();
initialDraw=false;
start=false;
lives=3;
score=0;
newLevel();
}
TV.delay_frame(1);
}January 10, 2012 at 7:22 pm #956trodossMember@moe wrote:
Sooooo yes I finished my game fusion!
Congratulations!
I haven’t had a chance to see what your issue may be, as I don’t have my Hackvision available at the moment.Oh and yes and Michael if you plan to make a Hackvision v2: If it’s possible, could you please add a on/off switch? But thats just an idea I wanted to share..
That is a pretty common suggestion among a lot of Arduino board designs 😉 It is a fine balance between extra components (adding to cost) versus adding “features.” I think for Michael to make a significant revision to Hackvision there would have to be a lot of interest.
Since there are a number of pins brought out, you can always try your hand at “expanding” your Hackvision (soldering on some pin headers on the expansion areas).
January 10, 2012 at 7:39 pm #957MichaelKeymastermoe, glad you had success. Agree with trodoss that function pointers are more of an advanced concept in C, so not really something that everyone understands. I’m glad there’s enough SRAM for both the Worms and Breakout games.
A switch would be nice. When designing a product, it’s very important to keep costs down. Even a the cheapest of switches costs $.49 and the extra board space costs more, too. Most people don’t realize that for every $1 you add to the production cost, you add $2.77 to your retail price (so that there’s 40% margin for distributors and 40% margin for me). So being economical with the design is paramount. That’s why products are spartan in their design.
December 31, 2013 at 10:05 pm #1841steeve_madisonMemberHello ,is it possible to view the 2 in 1 sketch …
I tried all the game for the hackvision and the only one
I couldn’t make work is worm , what i get is a black screen
And an annoying beep sound….Thank you , and i wish you an happy new year
-
AuthorPosts
- You must be logged in to reply to this topic.