Zalo DS Blog

Sunday, July 31, 2016

Game Boy Development - Tips and Tricks (II)

5. The game loop

Most of the examples I've seen contain a main loop similar to this

void main() {
<game initializations>
while(1) {
wait_vbl_done();
...
<process input>
<your frame update>
}
}

this is a good starting point. Waiting for the vblank interruption prevents your game from running faster than it  should. If you start with this and keep coding you'll eventually notice a frame rate slowdown. A high slowdown actually, and that is because your frame update at some point takes more time than what is available between 2 vblanks, but you are still waiting an extra vblank.

1. Skipping vblank wait when we don't need it

One way to improve the previous code will be:

void vbl_update() {
vbl_count ++;
}

void main() {

//Subscribe vbl_update to vblank interruption
disable_interrupts();
add_VBL(vbl_update);
set_interrupts(VBL_IFLAG);
enable_interrupts();


while(1) {
if(vbl_count == 0)
wait_vbl_done();
vbl_count = 0;
...


}
}

in this new code we have declared an interruption that will store in a var the number of vblanks that have happened during the current frame. If there are more than one we no longer need to wait to the vblank and we can continue processing. Bear in mind this can duplicate your framerate up to x2 specially in situations where your processing takes more than one vblank just for a few instructions

2. Frame skipping

This is a good improvement, but it can be improved even more by using frame skipping. As its name says it consists of skipping the painting of some frames to save processing time and this way keeping the game logic running without suffering an slow down. So let's say your game is running at 60Hz meaning your fixed update time is 0.016 secs aprox, in normal conditions you will update your logic 0.016 secs, paint, update 0.016 secs, paint... and so on. Then at some point you realize that updating your logic takes more than 0.16 secs and in order to prevent an slow down  you start dropping frames like: update 0.016 secs, update 0.016 secs, paint, update 0.016 secs, update 0.016 secs...

In the Game Boy the painting happens automatically and it takes 0 processing time from your main loop. The only thing you need to do is prepare the OAM table and update the scroll coordinates and it will just happen. So just doing two consecutive updates during one frame iteration will not help up us at all

Once thing we can do though is processing several updates together. Instead of updating 0.016 secs and then update 0.016 secs we can do one single update of 0.032 secs which basically means multiplying all your updating calculations by 2 (which is a very simple operation as we saw), like this:

...
while(1) {
if(vbl_count == 0)
wait_vbl_done();
delta_time = vbl_count ? 1 : 0;
vbl_count = 0;
...
}
...

so now we have a var called delta_time that is 1 or 0 depending on having to do frame skipping or not. If for example in some of your updates you were doing
 sprite.x += 1;
you just need to change it to:
 sprite.x += 1 << delta_time;
x <<  0 is the same as multiplying by 1 and x << 1 is the same as multiplying by 2

On this implementation we are just skipping one frame, doing 2 will be complicated because that cannot be done with a shift operator. Skipping 3 will be posible but If you need to do that you should probably take a look at your calculations and try to optimize a little bit (I mean your update is taking 4 times what it should...)

3. Keeping the scroll movement smooth

Even though your game is not running at 60Hz you can still do your scroll being updated at 60Hz if you update  the scroll position registers during the vblank interruption. In order to do this you should store your scroll position in a variable during your update process and then refresh the scroll position registers during vblank. But instead of doing

SCX_REG = scroll_x;

update them like this:

if(old_scroll_x < scroll_x)
old_scroll_x ++;
else if(old_scroll_x > scroll_x)
old_scroll_x --;
SCX_REG = old_scroll_x;

where scroll_x is the current x position of the scroll and old_scroll_x is the position in the previous frame. If your scroll was moving faster than one pixel per frame sometimes you can improve this with an small spring interpolation

if(old_scroll_x < scroll_x)
old_scroll_x += (scroll_x - old_scroll_x + 1) >> 1;
else if(old_scroll_x > scroll_x)
old_scroll_x -= (old_scroll_x - scroll_x + 1) >> 1;
SCX_REG = old_scroll_x;

And you should do the same for the y axis. You can check the full implementation here

6. Funny things in the world of 8 bits

Here is a list of some of the things that made me waste some time because I am not very used to work with these kind of limitations

- printf implementation only works with 16bits values. Funny when this is the only method you have to get some debug info and you realize you are printing all that info incorrectly. If you want to print the values of 8bits vars ensure you do a casting:
printf("signed:%d - unsigend %u", (INT16)signed_8_x, (UINT16)unsigned_8_x)

- lcc optimizes array indexing using ints, which in 8bits limits array access to 128 as stated in the documentation. It also says that for statically allocated arrays this doesn't apply. At some point of the implementation of the SpriteManager I started seeing some weird behaviour and it took me a while to discover that actually when accessing sprite[10] the 10 was calculated as sizeof(struct sprite) * 10 instead of sizeof(struct sprite) * (UINT16)10 giving me te wrong address of sprite 10 and causing some weird behaviour

- As seen above it is actually a very bad idea to randomly acess an element of an array of structs because it contains a multiplication. Instead it is a good idea to create an array of pointers to the elements of the array on startup and use this array instead

- I've read in lots of places that structs have a lot of problems and you shouldn't use them... as far as I have seen there are no problems using them (except for the array accessing issue which actually makes sense for optimization purposes)

- Also, I've read in lots of places that you cannot use pointers to functions. I have successfully used pointers to functions of 0 and 1 parameters. And the GDBK uses this for interruption handler!

- There are a lot of tutorials telling you how to do things... you'll be surprised to see some of them contain errors and you should use them as reference but never as the absolute truth. Once again check this post from AntonioND

- As a final conclussion... is the GBDK a mess like lots of people are saying on lots of forums and blogs? I have been using it for a month and I can tell you that is a lie. I am not gonna say working with it is easy but that's not the GBDK's fault alone. First there is the SDCC which is the compiler the GBDK is using and probably the one people should be blaming. There is a new version of the GDBK using the latest version of SDCC here, I didnt' have the time to test it. But most of the things people seem to complain about are more related to a lack of C knowledge (and again, this is not a good enviroment for learning it) and the limitations of having to work with and 8-bit machine. The creators of the GBDk are perfectly aware of this as you can see here

Thursday, July 28, 2016

Game Boy Development - Tips and Tricks (I)


As promised here is the first post with some advices based on my experience programming for the Game Boy during the Bit Bit Jam 3.

1. Choosing your SDK

The Gameboy has some different SDks you can choose to work with. The two main ones are the GBDK and the RGBDS. Choosing between any of them depends on what you want to accomplish and what skills you have. Check the next table with pros and cons of each one


GBDK RGBDS
C and asm asm (better than asm in GBDK*)
Slower if you use C Asm is always faster but requires experience
Faster to write programs in C Assembly is very low level, requires more time
Reliable for a jam If you are not an skilled programmer you might not finish your jam entry
Horrible SDK for learning C because of the 8bits limitations, no debugger.... Perfect for learning asm. You can even use a debugger with the BGB emulator 


With all these in mind, I chose GBDK because I have a lot of experience with C and C++ and I was feelling more confident to finish the game with it.

*so I've heard, because I didn't have the chance to try it

2. Getting your environment ready

I will suggest spending some time doing this, it can save you from lot of problems that will happen that last hour before sending the game. Code completion, launching the emu after compiling and the ability to add or delete files from your project easily are the things you should focus to get before starting your project.

A couple of weeks before the jam I started playing with GBDK and getting my development environment ready. Same as I did for the 3ds, Ds and the Wii I started configuring visual studio and some makefiles (see this post for more info). Most of the tutorials I followed along the internet were using some dos .bat files but I was looking for something more general. I created a makefile that:

- Compiles any source found under the folder src, so you don't have to specify the list of files to compile

- Parse the extension of the file to select the bank (anything.c will be on bank0, anything.b1.c will be on bank1, anything.b2.c on bank 2 and so on...)

- Automatically changes the unsigned char of the resources exported by GameBoy Map Builder and GameBoy Tile designed to const unsigned char

And some other features that you can check here. The important thing is having a confortable environment so you only have to worry about coding the videogame

If you are under Windows you will need a way to run makefiles. Since I have been using devkitpro for a long time I already had msys installed. One funny thing that I noticed is that the version of make that comes with devkitpro doesn't display any compilation errors so I guess the stderr is not properly configured. If you have the same problem you just need to download the last version of make for windows here

3. Banks

I would recommend reading the full documentation of the GBDK prior to any development. And specially trying to understand banks, what they are and how they work. AntonioND explains it pretty well on this post. The idea is very simple. If you take a look at the size of variables of the GBDK you'll see that the size of a pointer is 2 bytes. The maximum address you can point to with 2 bytes is 65535 (2^16). In these addresses the gameboy needs to get access to everything, not only the rom. Basically it needs 32kb for internal registers: sound, video, power, etc (you can find the complete list here if you are curious) and then there are other 32 kb for rom access(the cartdridge data). These 32kb are splitted into two: a fixed part that is always accesible (called bank0) and a swapable part (by default set to bank1). You can change the starting address of bank1 and make it point to addresses beyond 65535. Each group of 16386 (16kb) is known as a bank

So the idea is splitting your code along any of these banks and before excuting any part of it swapping the start address so the code can be reached. Take some time to understand this because it is extremely important. And also pay attention to the next things:

- You can only swap banks from code that is stored in bank0. Actually you can swap banks from any bank if you dare but your program will crash and it has an easy explanation. In any computer no matter how big or small it is there is a register (usually known as EIP) that contains the address of the next instruction to be executed. If no jump occurs then this register will automatically advance to the next register and execute the next instruction and basically that's how programs run. So, what will happen if you are in bank2 for example and swap to banks3? The EIP will execute the command to change the bank (in bank2) then it will advance to the next register and will try to execute the next command. The problem is that this comand that was supposed to belong to bank2 now belongs to bank3 and it can be literally anything. An easy solution to this is calling a function on bank0 (always accesible) that swaps banks, do the job on that new bank and then before returning swap banks back to bank2

- Lcc errors. You will eventually find a message like this from the compiler "warning : possibly wrote twice at addr 407a (21>21)". This will happen everytime you write code larger than 16kb and part of it ends in addresses that belong to the next bank. When this happens you should start moving your code to another bank

- Try to fill all banks with some info. If you have decided your game is gonna take 4 banks then the size of the game will be 64kb (16kb per bank) event if you don't fill them. Because of this it is a good idea to put some info in each bank from the beginning, otherwise you won't see the message I was telling you before when a bank gets full and your program will randomly crash at some point

- A stack can be very helpful to deal with bank swapping. Implementing a stack is very simple, you can check my implementation here. Once you have a stack implemented you can use it to move between banks like I do here. Everytime I want to change to a new bank I call PUSH_BANK and when I finish I just call POP_BANK so it automatically leaves me on the previous bank. Using this technique I was able to call gbt_update that automatically changes the bank to bank1 on my vblank interruption and then return to the bank I was previously working on calling REFRESH_BANK, fixing the issue AntonioND is stating on this post

4. Basic Maths

The Gameboy isn't very powerful and it can't even deal with multiplications or divisions, so you should avoid them at all cost (you can use them, but the performance will suffer). Most of these multiplications and divisions can be replaced by simpler operations, like:

- instead of multiplying by any power of 2 use the shift left operator (<<). Specially for multiplying by 2 use x << 1

- instead of dividing by any power of 2 use the shirft right operator (>>). Specially for dividing by 2 use x >> 1

- instead of the module operator (%) with any power of 2 you can do a mask. For example for getting the module 8 of a number do x & 0x7 (7 is 111 in binary so using & here turns everything except the last 3 digits into 0 which is what we want)

- the gbdk manual encourages you to use unsigned values. Take into account that these values are always bigger than 0. So doing something like 3 - 4 will result in an overflow giving you 255 (or 65535 if you are using 16bits) and checking if this is < 0 will unexpectecly return false. The best way I found to deal with this is masking against 0x8000 (1000 0000 0000 0000 in binary) and checking that the result is 0 (that 1 in the highest bit will only happen after an overflow)

Tuesday, July 26, 2016

BitBitJam 3 - Pretty Princess' Castle Escape

The bit bit jam is a retro jam based on old systems up to 16 bits. The jam lasts one week and you can use any technology you have developed before that week. The game must work in real hardware.

Before the jam starts a theme is randomly selected and this year it was "Red Hot Princess Carnage". A total of 20 games were presented. You can take a look at all of them here, and also you can check the winners on each different category: best theme, best platform usage and funniest game

This year I ended up joininig forces with sergeeo. He was in charge of the music and graphics and I was working on all the coding stuff. Together we created Pretty Princess' Castle Escape for the old nintendo Game Boy. Althought we finished the game on time we continued working on it after the jam and finally we have released a new version we consider final. Check it out here!




We'd like to thank all  the guys playing it and specially the ones who have uploaded some videos:



Also special thanks to David Colmenero for organizing the jam and for convince me to participate

During this week I'll be posting a few entries with my experience developing using the GBDK providing some info that I hope will be usefull for someone. Stay tuned!