Help a Newbie?

Store Forums Hackvision Game Development Help a Newbie?

Viewing 15 posts - 1 through 15 (of 18 total)
  • Author
    Posts
  • #484
    moe
    Member

    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 😉

    #1133
    Michael
    Keymaster

    Moe,

    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.html

    Why 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…

    #1134
    trodoss
    Member

    Moe,
    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
    }

    #1135
    moe
    Member

    First 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;i strcpy_P(s, (char *)pgm_read_word(&(strings[choices])));
    tv.print(32, 30+(i*8), s);
    }
    for(byte i=0;i y = 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;i tv.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:

    #1128
    moe
    Member

    Sorry, 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;i strcpy_P(s, (char *)pgm_read_word(&(strings[choices])));
    tv.print(32, 30+(i*8), s);
    }
    for(byte i=0;i y = 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;i tv.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;
    }
    }
    }
    #1121
    Michael
    Keymaster

    Moe,
    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?

    #1122
    Michael
    Keymaster

    Regarding 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.

    #1123
    moe
    Member

    Wow 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!

    #1124
    Michael
    Keymaster

    No, 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)

    #1125
    trodoss
    Member

    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.

    My recommendation would be to start with some examples on the Arduino site:
    http://arduino.cc/en/Tutorial/HomePage

    That 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.

    #1126
    moe
    Member

    Sooooo 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…

    #1127
    moe
    Member

    #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;i strcpy_P(s, (char *)pgm_read_word(&(strings[choices])));
    TV.print(32, 30+(i*8), s);
    }
    for(byte i=0;i y = 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;i TV.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;i TV.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);
    }
    #956
    trodoss
    Member

    @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).

    #957
    Michael
    Keymaster

    moe, 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.

    #1841

    Hello ,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

Viewing 15 posts - 1 through 15 (of 18 total)
  • You must be logged in to reply to this topic.