Gameboy Development Forum

Discussion about software development for the old-school Gameboys, ranging from the "Gray brick" to Gameboy Color
(Launched in 2008)

You are not logged in.

Ads

#1 2022-01-01 01:52:14

Multifaceted.Abnormal
Member
Registered: 2021-02-21
Posts: 10

[GBDK-2020] Do an animation in a loop until a button is pressed, maybe

I’m trying to write a bit of text to the screen with a border and a blinking arrow to prompt the player to press a button to go to the next screen.  I can do all of this just fine except for the fact that the blinking bit isn’t playing nice.  Right now, I have the background that has the alphabet and arrow along with the border.  I clear off the alphabet and then use those tiles to write the needed text on screen.  I then place the blinking arrow at the bottom of the text.  The arrow consists of three tiles and a blank one.  I set the first tile of the arrow over a blank one then perform a wait function, then I change the arrow to the second then wait then to the last then wait and then walk that back to blank.  This gives me a blinking arrow.

Unfortunately, when a button is pressed, it may or may not go through.  I know that it’s because the button press isn’t interrupting the wait function because when I remove the wait, it works like it should only the blinking is so fast that it might as well not be blinking.  I’ve tried getting this to work many different ways and none have worked.  I also went looking for examples that have an arrow like I’m trying to make so I can try to learn from the code they have.  None of the SCR games I can find have arrows.  I know it’s possible because pokemon and Zelda have exactly what I’m trying to do.  HOW?  I tried finding it in assembly and just including that inline for now until I can figure out how in C but I don’t know enough ASM to even locate the code I’d need to copy.  Plus I’d really love to figure out how to do it in C.

Finally: I’m gonna dump the code I’m currently using.  Look, I’m writing this cause I think it’s neat that I can write this in a notepad document type a few commands and “play” it on my gameboy.  I know my code is horrible and with no comments.  There's also portions that I'm already planning to rewrite after I finally get it working.  It’s just for fun and I’m a bit of a moron so please don’t be too harsh.  BUT! Well, if you can show me how to do what I’m asking awesome I’d love to learn.  BUT ALSO: if you know how I can code any bit of it better, please don’t hesitate to tell me.  I suck now but with your help, I hope to get a little better.  Someday.  Eventually.

My code follows.
File: new.c
This shows the games short back story over a few slides written in text on the background, sets up a new game state and then "continues" a new game.  CONTINUE will load a specific state (from the menu, the last saved) and allows play from that point (in this case a new game).  NOTE: Any function not defined here is referenced from a different file.
Goals: The blinking arrow waiting for J_A and the ability to skip the text slowly appearing with J_B

Code:

#include "./imgs/new.h"

void writeThis(char * saying, int startTile) {
    int *answer;
    const int len = strlen(saying);
    answer = malloc(len * sizeof(int));
    
    for (i=0; i<len; i++) {
        if (saying[i]=='A') {
            answer[i]=21;
        } else if (saying[i]=='B') {
            answer[i]=22;
        } else if (saying[i]=='C') {
            answer[i]=23;
        } else if (saying[i]=='D') {
            answer[i]=24;
        } else if (saying[i]=='E') {
            answer[i]=25;
        } else if (saying[i]=='F') {
            answer[i]=26;
        } else if (saying[i]=='G') {
            answer[i]=27;
        } else if (saying[i]=='H') {
            answer[i]=28;
        } else if (saying[i]=='I') {
            answer[i]=29;
        } else if (saying[i]=='J') {
            answer[i]=30;
        } else if (saying[i]=='K') {
            answer[i]=31;
        } else if (saying[i]=='L') {
            answer[i]=32;
        } else if (saying[i]=='M') {
            answer[i]=33;
        } else if (saying[i]=='N') {
            answer[i]=34;
        } else if (saying[i]=='O') {
            answer[i]=35;
        } else if (saying[i]=='P') {
            answer[i]=36;
        } else if (saying[i]=='Q') {
            answer[i]=37;
        } else if (saying[i]=='R') {
            answer[i]=38;
        } else if (saying[i]=='S') {
            answer[i]=41;
        } else if (saying[i]=='T') {
            answer[i]=42;
        } else if (saying[i]=='U') {
            answer[i]=43;
        } else if (saying[i]=='V') {
            answer[i]=44;
        } else if (saying[i]=='W') {
            answer[i]=45;
        } else if (saying[i]=='X') {
            answer[i]=46;
        } else if (saying[i]=='Y') {
            answer[i]=47;
        } else if (saying[i]=='Z') {
            answer[i]=48;
        } else if (saying[i]=='a') {
            answer[i]=49;
        } else if (saying[i]=='b') {
            answer[i]=50;
        } else if (saying[i]=='c') {
            answer[i]=51;
        } else if (saying[i]=='d') {
            answer[i]=52;
        } else if (saying[i]=='e') {
            answer[i]=53;
        } else if (saying[i]=='f') {
            answer[i]=54;
        } else if (saying[i]=='g') {
            answer[i]=55;
        } else if (saying[i]=='h') {
            answer[i]=56;
        } else if (saying[i]=='i') {
            answer[i]=57;
        } else if (saying[i]=='j') {
            answer[i]=58;
        } else if (saying[i]=='k') {
            answer[i]=61;
        } else if (saying[i]=='l') {
            answer[i]=62;
        } else if (saying[i]=='m') {
            answer[i]=63;
        } else if (saying[i]=='n') {
            answer[i]=64;
        } else if (saying[i]=='o') {
            answer[i]=65;
        } else if (saying[i]=='p') {
            answer[i]=66;
        } else if (saying[i]=='q') {
            answer[i]=67;
        } else if (saying[i]=='r') {
            answer[i]=68;
        } else if (saying[i]=='s') {
            answer[i]=69;
        } else if (saying[i]=='t') {
            answer[i]=70;
        } else if (saying[i]=='u') {
            answer[i]=71;
        } else if (saying[i]=='v') {
            answer[i]=72;
        } else if (saying[i]=='w') {
            answer[i]=73;
        } else if (saying[i]=='x') {
            answer[i]=74;
        } else if (saying[i]=='y') {
            answer[i]=75;
        } else if (saying[i]=='z') {
            answer[i]=76;
        } else if (saying[i]=='!') {
            answer[i]=91;
        } else if (saying[i]==',') {
            answer[i]=111;
        } else if (saying[i]=='.') {
            answer[i]=112;
        } else if (saying[i]==' ') {
            answer[i]=125;
        } else {
            answer[i]=0;
        }
        set_bkg_tiles(getXfromTileID(startTile+i),getYfromTileID(startTile+i),1,1,&new_map_data[answer[i]]);
        setWait(3);
    }
    free(answer);
}

void clearBG() {
    for (i=21; i!=39; i++) {
        set_bkg_tiles(getXfromTileID(i), getYfromTileID(i),1,1,&new_map_data[125]);
    }
    for (i=41; i!=59; i++) {
        set_bkg_tiles(getXfromTileID(i), getYfromTileID(i),1,1,&new_map_data[125]);
    }
    for (i=61; i!=79; i++) {
        set_bkg_tiles(getXfromTileID(i), getYfromTileID(i),1,1,&new_map_data[125]);
    }
    for (i=81; i!=99; i++) {
        set_bkg_tiles(getXfromTileID(i), getYfromTileID(i),1,1,&new_map_data[125]);
    }
    for (i=101; i!=119; i++) {
        set_bkg_tiles(getXfromTileID(i), getYfromTileID(i),1,1,&new_map_data[125]);
    }
    for (i=121; i!=139; i++) {
        set_bkg_tiles(getXfromTileID(i), getYfromTileID(i),1,1,&new_map_data[125]);
    }
    for (i=141; i!=159; i++) {
        set_bkg_tiles(getXfromTileID(i), getYfromTileID(i),1,1,&new_map_data[125]);
    }
    for (i=161; i!=179; i++) {
        set_bkg_tiles(getXfromTileID(i), getYfromTileID(i),1,1,&new_map_data[125]);
    }
    for (i=181; i!=199; i++) {
        set_bkg_tiles(getXfromTileID(i), getYfromTileID(i),1,1,&new_map_data[125]);
    }
    for (i=201; i!=219; i++) {
        set_bkg_tiles(getXfromTileID(i), getYfromTileID(i),1,1,&new_map_data[125]);
    }
    for (i=221; i!=239; i++) {
        set_bkg_tiles(getXfromTileID(i), getYfromTileID(i),1,1,&new_map_data[125]);
    }
    for (i=241; i!=259; i++) {
        set_bkg_tiles(getXfromTileID(i), getYfromTileID(i),1,1,&new_map_data[125]);
    }
    for (i=261; i!=279; i++) {
        set_bkg_tiles(getXfromTileID(i), getYfromTileID(i),1,1,&new_map_data[125]);
    }
    for (i=281; i!=299; i++) {
        set_bkg_tiles(getXfromTileID(i), getYfromTileID(i),1,1,&new_map_data[125]);
    }
    for (i=301; i!=319; i++) {
        set_bkg_tiles(getXfromTileID(i), getYfromTileID(i),1,1,&new_map_data[125]);
    }
    for (i=321; i!=339; i++) {
        set_bkg_tiles(getXfromTileID(i), getYfromTileID(i),1,1,&new_map_data[125]);
    }
}
void prompt(int location) {
        set_bkg_tiles(getXfromTileID(location), getYfromTileID(location),1,1,&new_map_data[128]);
        //setWait(8);
        set_bkg_tiles(getXfromTileID(location), getYfromTileID(location),1,1,&new_map_data[127]);
        //setWait(8);
        set_bkg_tiles(getXfromTileID(location), getYfromTileID(location),1,1,&new_map_data[126]);
        //setWait(8);
        set_bkg_tiles(getXfromTileID(location), getYfromTileID(location),1,1,&new_map_data[127]);
        //setWait(8);
        set_bkg_tiles(getXfromTileID(location), getYfromTileID(location),1,1,&new_map_data[128]);
        //setWait(8);
        set_bkg_tiles(getXfromTileID(location), getYfromTileID(location),1,1,&new_map_data[125]);
        //setWait(8);
}
void startNewGame() {
    loop=true;
    set_bkg_data(0, new_tile_count, new_tile_data);
    set_bkg_tiles(0, 0, 20, 18, new_map_data);
    clearBG();
    SHOW_BKG;
    ffb(15);
    
    //18 tiles for framed 20 unframed
    
    writeThis("Trained and", 104);
    writeThis("launched into", 123);
    writeThis("space from earth,", 142);
    writeThis("you lay suspended", 161);
    writeThis("in a cryogenic", 183);
    writeThis("stasis chamber", 203);
    writeThis("aboard the", 225);
    writeThis("starship,", 246);
    writeThis("Diplomacy.", 265);
    while (joypad() != J_A) {
    prompt(338);
    }
    clearBG();
}

Any and all help would be greatly appreciated.  Thank you in advance.  (I’ll also thank you later, too.)

M.A

Offline

 

#2 2022-01-02 18:01:26

bbbbbr
Member
Registered: 2019-03-04
Posts: 124

Re: [GBDK-2020] Do an animation in a loop until a button is pressed, maybe

If you are only calling set_bkg_tile() with one tile to print, it's better to use set_bkg_tile_xy()
https://gbdk-2020.github.io/gbdk-2020/d … 98be7668f8

It's not clear what setWait() does, but usually it's best to use something like wait_vbl_done()
https://gbdk-2020.github.io/gbdk-2020/d … 19b7131514

You can also move the while loop and input checking to be inside the prompt function, it might make it easier. I didn't try to compile this, so it might have some typos. But it should give the basic idea.

Code:

// Useful macro for calculating array size at compile time
#define ARRAY_LEN(A)  sizeof(A) / sizeof(A[0])

// List of the tiles you want to use for the prompt. 
// Make it a const array as long as the contents never change, that saves RAM and init code size
const uint8_t prompt_tile_ids[] = {128, 127, 126, 127, 128, 125};

void wait_prompt(int location) {
    
    uint8_t counter = 0;

    while (joypad() != J_A) {

        // Display a given prompt tile_id based on the counter
        set_bkg_tiles(getXfromTileID(location), getYfromTileID(location),
                      &new_map_data[ prompt_tile_ids[counter] ]);

        counter++;
        if (counter >= ARRAY_LEN(prompt_tile_ids))
            counter = 0;

        wait_vbl_done(); // Wait for next frame
    }
}

...
    wait_prompt(338);
...

Also:

Here is an example of a more compact way to calculate offsets for font tiles and print characters:
https://github.com/Zal0/ZGB/blob/master … rint.c#L59

An example of how to skip text printing delay if a button is pressed. UPDATE_KEYS is just a macro you can look up elsewhere in the code.
https://github.com/bbbbbr/Petris/blob/r … int.c#L142

Offline

 

#3 2022-01-13 16:32:40

Multifaceted.Abnormal
Member
Registered: 2021-02-21
Posts: 10

Re: [GBDK-2020] Do an animation in a loop until a button is pressed, maybe

Esh!  Looking at your code made me feel stupid.  I took a few months off from my project because an SDCC update broke my workflow.  So I didn't really remember the code but when I saw your code it reminded me of similar code I'd written for my main menu and sure enough they were extremely similar.  I used your implementation to sure up my own similar code.  Thanks.  I felt like a right knob.  I'm truly sorry to have bothered you with it.

And the way that I do the string to tile id conversion bit was the part I was talking about when I said there are bits that I planned on fixing.  You included a way that makes since as a way to do it.  It seems like it's allowing the system to determine what character there is in the string by virtue of there being 26 known letters of the alphabet.  However, when it comes down to it, I simply don't understand the math that's being employed.  I'm wondering if you could shed some light on it.  I figure I could just copy and paste the code and play with it until it works but I'd really love to understand what it's doing instead of just blindly using some code.  The line I don't fully understand is this:

Code:

c = font_idx +  1 + *txt - 'A';

.  I get where font_idx is coming from and I understand the need for the string but I don't know why one has to be added to font_idx and more importantly I super don't understand the (- 'A') bit.  Is the 'A' being converted into ASCII (65) by the system and subtracting that value from the total?  If so, why doesn't it stay a constant 'A' instead of the current character?  I bet it's super simple and I feel like an idiot for asking but I reckon you've done this longer than me and for me it's nothing more than a passing hobby for which I took a few low level C classes in college.  I did try to look it up on google but not exactly knowing what to search for left the results lacking.  Again, I hate asking for what is undoubtedly simple, still, any insights to alleviate my ignorance of how and why this works would be most appreciated.

Thanks for all the assistance thus far.  And again, sorry.

EDIT: Is plus one being added to the index because the null character is tile zero?

Last edited by Multifaceted.Abnormal (2022-01-13 16:36:15)

Offline

 

#4 2022-01-14 01:20:24

bbbbbr
Member
Registered: 2019-03-04
Posts: 124

Re: [GBDK-2020] Do an animation in a loop until a button is pressed, maybe

No need to apologize, asking questions and seeing better ways to do things is how we all learn.

Code:

c = font_idx +  1 + *txt - 'A';

In the case of ZGB's print function above:

* The "+1" is because the first character in ZGB's font tileset is a Blank Space character, which you can see how it uses that at Lines 56 and 57. ZGB's font tiles start like " ", "A", "B", C", etc and only have upper case if I recall.

* Yes, you're correct that " - 'A' " gets converted by the compiler to " - 65 ". So if the value of the current string character at " *txt " is also "A" (which is again the same as "65"), then when it's subtracted you get " 0 ". Then that "+1" offset for the Blank Space font tile is added, and finally the font_idx offset is added which points to where the font tiles start in VRAM. And then the pointer " txt " is incremented to the next character and so on.

Offline

 

Board footer

Powered by PunBB
© Copyright 2002–2005 Rickard Andersson