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.
Hello!
I am starting into Gameboy development with GBDK and have run into a bit of an issue using structs. I'm not sure if I'm missing something simple or if there is an underlying problem with GBDK / SDCC.
The issue is that when I have three structs, the third struct behaves very strangely. Here is the most straight forward example I could put together:
#include <gb.h> unsigned char face[] = { 0x00,0x2B,0x00,0x7E,0x7E,0x42,0xFF,0xA5, 0xFF,0x81,0xFF,0x99,0x7E,0x42,0x3C,0x3C }; struct Face { int xCoordinate; int yCoordinate; int spriteNumber; }; struct Face upperFace, straightFace, lowerFace; void moveFaces() { // Test if this face has gone too far right or up // If not, keep moving it in an upward angle if ((upperFace.xCoordinate >= 160) || upperFace.yCoordinate <= 0) { upperFace.xCoordinate = 200; upperFace.yCoordinate = 200; } else { upperFace.xCoordinate += 2; upperFace.yCoordinate -= 1; } move_sprite(upperFace.spriteNumber, upperFace.xCoordinate, upperFace.yCoordinate); // Test if this face has gone too far right // If not, keep moving it in a straight line if (straightFace.xCoordinate >= 160) { straightFace.xCoordinate = 200; straightFace.yCoordinate = 200; } else { straightFace.xCoordinate += 2; } move_sprite(straightFace.spriteNumber, straightFace.xCoordinate, straightFace.yCoordinate); // Test if this face has gone too far right or down // If not, keep moving it in a downward angle if ((lowerFace.xCoordinate >= 160) || lowerFace.yCoordinate >= 150) { lowerFace.xCoordinate = 200; lowerFace.yCoordinate = 200; } else { lowerFace.xCoordinate += 2; lowerFace.yCoordinate += 1; } move_sprite(lowerFace.spriteNumber, lowerFace.xCoordinate, lowerFace.yCoordinate); } void resetFaces() { upperFace.xCoordinate = 25; upperFace.yCoordinate = 75; upperFace.spriteNumber = 0; straightFace.xCoordinate = 25; straightFace.yCoordinate = 75; straightFace.spriteNumber = 1; lowerFace.xCoordinate = 25; lowerFace.yCoordinate = 75; lowerFace.spriteNumber = 2; } void main() { int key; set_sprite_data(0, 1, face); set_sprite_tile(0, 0); set_sprite_tile(1, 0); set_sprite_tile(2, 0); SHOW_BKG; SHOW_SPRITES; while (!0) { wait_vbl_done(); key = joypad(); if (key & J_A) { resetFaces(); } moveFaces(); } }
What should be happening is that there are three faces moving in an upward angle, straight line, and downward angle. However, the third struct (downward angle) is moving in a straight line. This can be seen by commenting out the code within "moveFaces" that correspond with the upper and straight faces.
Perhaps the strangest part is changing the lower face's code to be the following:
if ((lowerFace.xCoordinate >= 160) || lowerFace.yCoordinate >= 150) { lowerFace.xCoordinate = 200; lowerFace.yCoordinate = 200; move_sprite(lowerFace.spriteNumber, lowerFace.xCoordinate, lowerFace.yCoordinate); } else { lowerFace.xCoordinate += 2; lowerFace.yCoordinate += 1; move_sprite(lowerFace.spriteNumber, lowerFace.xCoordinate, lowerFace.yCoordinate); }
If the "move_sprite" is moved within the if and else statements, then the lower face moves in the downward angle as expected.
My end goal is to have an array of structs so that I can iterate over each struct, check it against the boundaries of the screen, and move its corresponding sprite accordingly. If there's a simpler way to achieve this sort of behavior rather than using structs, I'm open to that as well. I wanted to do something a bit more intelligent than an array for X coordinates, an array for Y coordinates, an array for sprite number, etc, but it is proving to be a bit of a headache.
Like I said, I'm not sure if there's something inherently wrong with this code or if it is something out of my control. Any advice would be greatly appreciated.
Offline
Hi there!
So I don't know 100% what's causing your issues here, but there's a few things that stand out to me as potentially problematic that probably are causing or will cause issues.
First, it looks like the `Face` instances are only initialized in `resetFaces`, but `resetFaces` is only called when the user presses the A button. In particular, that's the only place in your code that the `spriteNumber` property is written to. Before you call `resetFaces`, the `spriteNumber` property of your instances is uninitialized, and therefore effectively random. It's even possible that before you call `resetFaces` for the first time some of your `Face` instances might have the same `spriteNumber`. You should probably add a call to `resetFaces` in `main` before you enter your loop. Otherwise, your `moveFaces` function is calling `move_sprite` with values for `spriteNumber` that are unknown.
Second, from what I understand, GBDK defines `int` as a 8-bit signed integer, with a range of -128 to 127. Your code includes logic outside those bounds (comparisons to 160, assignments to 200, etc). This could be causing all sorts of fun chaos, like temporary promotion to 16-bit and then truncating back to 8-bit for storage. I'm not sure the exact effects, but it's almost certainly not what you intend. You may want to change your coordinate variables to `long` values, which I think is 16-bit in GBDK. To encourage yourself to think carefully about integer ranges, you may also want to consider using GBDK's more explicitly-named types, like UINT8, INT16, etc. Programming with small integer types makes overflows really easy, so it's important to constantly ask yourself, "can this overflow? What happens if it overflows?"
Sorry that neither of these detail exactly what is definitely causing your issue, but I hope at least one of them helps!
Last edited by DonaldHays (2016-08-29 19:23:20)
Offline
Thank you for the reply, I really appreciate it!
The first part makes a lot of sense. In my actual game, I'm doing something similar to that, but I forgot to do it here in the example.
The second part is exactly what I needed though! I am used to having 'int' be a lot larger than 128 so I'll need to look into types to make sure what I'm using is correct. I changed 'xCoordinate' and 'yCoordinate' to be 'UBYTE' and now everything is working properly. Looking at 'types.h' in GBDK, 'UBYTE' also seems to be the same as 'UINT8' which is an 'unsigned char' and should give me access to 0 to 255. Hopefully I'm understanding this right and this will be the end of my woes, at least for now.
Thanks again for the reply and helping me resolve this.
Offline
DonaldHays wrote:
Hi there!
So I don't know 100% what's causing your issues here, but there's a few things that stand out to me as potentially problematic that probably are causing or will cause issues.
First, it looks like the `Face` instances are only initialized in `resetFaces`, but `resetFaces` is only called when the user presses the A button. In particular, that's the only place in your code that the `spriteNumber` property is written to. Before you call `resetFaces`, the `spriteNumber` property of your instances is uninitialized, and therefore effectively random. It's even possible that before you call `resetFaces` for the first time some of your `Face` instances might have the same `spriteNumber`. You should probably add a call to `resetFaces` in `main` before you enter your loop. Otherwise, your `moveFaces` function is calling `move_sprite` with values for `spriteNumber` that are unknown.
Second, from what I understand, GBDK defines `int` as a 8-bit signed integer, with a range of -128 to 127. Your code includes logic outside those bounds (comparisons to 160, assignments to 200, etc). This could be causing all sorts of fun chaos, like temporary promotion to 16-bit and then truncating back to 8-bit for storage. I'm not sure the exact effects, but it's almost certainly not what you intend. You may want to change your coordinate variables to `long` values, which I think is 16-bit in GBDK. To encourage yourself to think carefully about integer ranges, you may also want to consider using GBDK's more explicitly-named types, like UINT8, INT16, etc. Programming with small integer types makes overflows really easy, so it's important to constantly ask yourself, "can this overflow? What happens if it overflows?"
Sorry that neither of these detail exactly what is definitely causing your issue, but I hope at least one of them helps!
int is 16bit, not 8 bit
Offline
bbsfoo wrote:
int is 16bit, not 8 bit
Oh? I don't use GBDK much myself, so I consulted documentation when finding my answer, and the first thing I stumbled upon was this, which says on page 10 that `int` is an 8-bit signed type. However, looking closer, I see that the document applies to GBDK 2.0b13, and the most recent release appears to be quite a bit newer, and this suggests that `int` is a 16-bit signed type. From looking at a changelog, it looks like 2.0b1 has a note saying that the size of basic types changed such that `int` would now be 8-bit signed and `long` would be 16-bit signed, and it looks like 2.0.14 added `-int8` and `-int16` flags to change the size, with the default remaining 8.
Did `int` change to 16-bit at some point, and I'm just not seeing the release note where it happened? And if `int` is 16-bit, I don't see how rokushadows changing the `xCoordinate` and `yCoordinate` values to `UBYTE` could have fixed problems, and yet from their last post it did. Since it appears the size of the type has changed over time, is it possible folks are using different versions of GBDK with different `int` sizes?
Offline
DonaldHays wrote:
bbsfoo wrote:
int is 16bit, not 8 bit
Oh? I don't use GBDK much myself, so I consulted documentation when finding my answer, and the first thing I stumbled upon was this, which says on page 10 that `int` is an 8-bit signed type. However, looking closer, I see that the document applies to GBDK 2.0b13, and the most recent release appears to be quite a bit newer, and this suggests that `int` is a 16-bit signed type. From looking at a changelog, it looks like 2.0b1 has a note saying that the size of basic types changed such that `int` would now be 8-bit signed and `long` would be 16-bit signed, and it looks like 2.0.14 added `-int8` and `-int16` flags to change the size, with the default remaining 8.
Did `int` change to 16-bit at some point, and I'm just not seeing the release note where it happened? And if `int` is 16-bit, I don't see how rokushadows changing the `xCoordinate` and `yCoordinate` values to `UBYTE` could have fixed problems, and yet from their last post it did. Since it appears the size of the type has changed over time, is it possible folks are using different versions of GBDK with different `int` sizes?
printf("%d\n", (int)sizeof(int));
output is "2"
-----------------------------------------------
my gbdk is on the windows
sdcc -v
SDCC : gbz80/z80 2.2.1 ' (UNIX)
Last edited by bbsfoo (2016-09-01 21:44:04)
Offline
You should always use UBYTE or UINT8, since they have fixed length.
Take a look at this page if it can help you : http://gbdk.sourceforge.net/guidelines.html
Offline
DonaldHays wrote:
Did `int` change to 16-bit at some point, and I'm just not seeing the release note where it happened? And if `int` is 16-bit, I don't see how rokushadows changing the `xCoordinate` and `yCoordinate` values to `UBYTE` could have fixed problems, and yet from their last post it did.
I think the relevant change is signed -> unsigned rather than 16 bit -> 8 bit. Almost everything in the code in the OP fits in a UBYTE (0 - 255) but not in a signed 8 bit int (-128 - 127). The exception is upperFace.yCoordinate <= 0 which might fail in the case <0 when a UBYTE would have wrapped over from the subtraction, but it should still work relatively better.
Offline