gbadev.org forum archive

This is a read-only mirror of the content originally found on forum.gbadev.org (now offline), salvaged from Wayback machine copies. A new forum can be found here.

Beginners > Why won't it work?

#53657 - biubid_boy - Sat Sep 10, 2005 10:23 am

I'm using this code for my pacman clone but I keep getting heaps of errors. They're mainly parse errors and something about conflicting types for CopyOAM. Does anyone know how to fix it?

Code:

#include "gba.h"
#include "bg.h"
#include "enemy.h"

void InitializeSprites(void);
void CopyOAM(void);
void MoveSprite(OAMEntry* sp, int x, int y);
void MoveEnemy(void);

OAMEntry sprites[128];

u16 xenemy = 30;
u16 yenemy = 50;
u16 randomDirection = rand();
u16 timer

u8 enemyDirection = 0;   

u8 xMax = 240;      
u8 yMax = 160;      
u8 xMin = 1;      
u8 yMin = 1;      

int main() {
   
   u16 loop;

   SetMode(MODE_4 | BG2_ENABLE | OBJ_ENABLE | OBJ_MAP_1D);

   for(loop = 0; loop < 256; loop++) {
   OBJ_PaletteMem[loop] = enemyPalette[loop];
   }

   for (loop = 0; loop < 256; loop++) {
      BG_PaletteMem[loop]=bgPalette[loop];
   }

   for (loop = 0; loop < (120*160); loop++) {
      FrontBuffer[loop] = bgData[loop] ;
   }

   memcpy( (u16 *)0x06014000, &enemyData, sizeof(enemyData) );

   sprites[0].attribute0 = COLOR_256 | SQUARE | yenemy;
   sprites[0].attribute1 = SIZE_8 | xenemy;
   sprites[0].attribute2 = 512;
 
while(1)
   {
      MoveEnemy();
      MoveSprite(&sprites[0],xenemy,yenemy);
      for (timer = 0; timer < 60; timer++)
        {
        if(RandomDirection <= 25) { 
        enemyDirection == 0;
        } else (RandomDirection <= 50) {
        enemyDirection == 1;
        } else (RandomDirection <= 75) {
        enemyDirection == 2;
        } else (RandomDirection <= 100) {
        enemyDirection == 3;
        } else (RandomDirection == 100) {
        enemyDirection == 0; }
        {
        WaitForVsync();
      CopyOAM();
   }
   
    return 0;

}

void InitializeSprites(void)
{
   u16 loop;
   for(loop = 0; loop < 128; loop++) {
      sprites[loop].attribute0 = 160;
      sprites[loop].attribute1 = 240;
   }
}

void CopyOAM(void)
{
   u16 loop;
   u16* temp;
   temp = (u16*)sprites;
   for(loop = 0; loop < 128*4; loop++)   {
      OAM_Mem[loop] = temp[loop];
   }
}

void MoveSprite(OAMEntry* sp, int x, int y)
{
   sp->attribute1 = sp->attribute1 & 0xFE00;
   sp->attribute1 = sp->attribute1 | x;

   sp->attribute0 = sp->attribute0 & 0xFF00;
   sp->attribute0 = sp->attribute0 | y;
}


void MoveEnemy(void) {
   if (enemyDirection == 0 && xenemy < xMax && yenemy < yMax) {
      xenemy++;
   }
   if (enemyDirection == 0 && (xenemy >= xMax || yenemy >= yMax )) {
      if (xenemy >= xMax) {
         enemyDirection = 3;
      } else {
         enemyDirection = 1;
      }
   }

   if (enemyDirection == 1 && xenemy < xMax && yenemy > yMin) {
      xenemy++;
   }
   if (enemyDirection == 1 && (xenemy >= xMax || yenemy <= yMin )) {
      if (xenemy >= xMax) {
         enemyDirection = 2;
      } else {
         enemyDirection = 0;
      }
   }

   if (enemyDirection == 2 && xenemy > xMin && yenemy > yMin) {
      xenemy--;
   }
   if (enemyDirection == 2 && (xenemy <= xMin || yenemy <= yMin )) {
      if (xenemy <= xMin) {
         enemyDirection = 1;
      } else {
         enemyDirection = 3;
      }
   }

   if (enemyDirection == 3 && xenemy > xMin && yenemy < yMax) {
      xenemy--;
   }
   if (enemyDirection == 3 && (xenemy <= xMin  || yenemy >= yMax)) {
      if (xenemy <= xMin) {
         enemyDirection = 0;
      } else {
         enemyDirection = 2;
      }
   }

}



If i use
Code:

while(1)
   {
      MoveBall();
      MoveSprite(&sprites[0],xball,yball);
      WaitForVsync();
      CopyOAM();
   }
Instead of the one in my code, it compiles perfectly.
Thanks for any help,
Biubid_boy.

#53660 - Fatnickc - Sat Sep 10, 2005 11:20 am

The for loop is your problem, obviously. In my first game, I used while loops for automated character loops, meaning that it 'paused' the rest of the game whilst doing that. Think about it.

#53666 - biubid_boy - Sat Sep 10, 2005 12:46 pm

Nup, don't get it. Apparently the logic center of my brain isn't developed yet. I need things spelled out for me.

#53669 - Mighty Max - Sat Sep 10, 2005 1:40 pm

Code:

  while(1) {
      MoveEnemy();
      MoveSprite(&sprites[0],xenemy,yenemy);
      for (timer = 0; timer < 60; timer++)  {
        if(RandomDirection <= 25) { 
            enemyDirection == 0;
        } else (RandomDirection <= 50) {
            enemyDirection == 1;
        } else (RandomDirection <= 75) {
            enemyDirection == 2;
        } else (RandomDirection <= 100) {
            enemyDirection == 3;
        } else (RandomDirection == 100) {
            enemyDirection == 0;
        }
        {
            WaitForVsync();
            CopyOAM();
   }


I moved the text a bit around to look where it opens/closes blocks ... guess you cut something out here, or ? At least there is one { to much (just before WaitForVSync();) and one } to less. (Closing the for-loop)

The code this way does not make much sense tbh.

There are missing some if too
_________________
GBAMP Multiboot

#53673 - Fatnickc - Sat Sep 10, 2005 2:26 pm

Maybe put the for loop inside a function, and make the equivalant with if statements.

#53731 - biubid_boy - Sun Sep 11, 2005 7:49 am

Instead of my for loop, I'm using this
Code:

while(1)
   {
         MoveEnemy();
         MoveSprite(&sprites[0],xenemy,yenemy);
         while (1)
        {
         if (timer < 60) {
         timer + 1; }
         else if (timer >= 60) {               
               if(randomDirection <= 25) { 
               enemyDirection == 0;
               } else if (randomDirection <= 50) {
               enemyDirection == 1;
               } else if(randomDirection <= 75) {
               enemyDirection == 2;
               } else if(randomDirection <= 100) {
               enemyDirection == 3;
               } else if (randomDirection >= 100) {
               enemyDirection == 0; }
          }
        }

which seems to be working OK. Now I get an error about the decleration of MoveEnemy. Can't see why though.

#53732 - headspin - Sun Sep 11, 2005 8:07 am

Because you are calling the MoveEnemy() function before it is defined. Place a function prototype above your main() function. It looks like this..

Code:
void MoveSprite(OAMEntry* sp, int x, int y);

_________________
Warhawk DS | Manic Miner: The Lost Levels | The Detective Game

#53736 - biubid_boy - Sun Sep 11, 2005 9:07 am

What the... Now my sprite isn't showing up at all! It was before...

#53740 - Fatnickc - Sun Sep 11, 2005 11:08 am

That's because in your post where you said what you are now using, you forgot to add
Code:
WaitForVsync();
copyOAM();

#53825 - biubid_boy - Mon Sep 12, 2005 9:48 am

Ok. Compiled without errors, but my random direction system for the enemy isn't working. This is my code:



Code:
#include "gba.h"
#include "bg.h"
#include "enemy.h"

void InitializeSprites(void);
void CopyOAM(void);
void MoveSprite(OAMEntry* sp, int x, int y);
void MoveEnemy(void);

OAMEntry sprites[128];

u16 xenemy = 30;
u16 yenemy = 50;

u8 enemyDirection = 0;   

u8 xMax = 240;      
u8 yMax = 160;      
u8 xMin = 1;      
u8 yMin = 1;      

int main() {
   
    u16 randomDirection = rand();
    u16 timer = 0;
   u16 loop;

   SetMode(MODE_4 | BG2_ENABLE | OBJ_ENABLE | OBJ_MAP_1D);

   for(loop = 0; loop < 256; loop++) {
   OBJ_PaletteMem[loop] = enemyPalette[loop];
   }

   for (loop = 0; loop < 256; loop++) {
      BG_PaletteMem[loop]=bgPalette[loop];
   }

   for (loop = 0; loop < (120*160); loop++) {
      FrontBuffer[loop] = bgData[loop] ;
   }

   memcpy( (u16 *)0x06014000, &enemyData, sizeof(enemyData) );

   sprites[0].attribute0 = COLOR_256 | SQUARE | yenemy;
   sprites[0].attribute1 = SIZE_8 | xenemy;
   sprites[0].attribute2 = 512;
 
while(1)
   {
      MoveEnemy();
      MoveSprite(&sprites[0],xenemy,yenemy);
         if (timer < 60) {
         timer + 1; }
         else if (timer >= 60) {               
               if(randomDirection <= 25) { 
               enemyDirection == 0;
               } else if (randomDirection <= 50) {
               enemyDirection == 1;
               } else if(randomDirection <= 75) {
               enemyDirection == 2;
               } else if(randomDirection <= 100) {
               enemyDirection == 3;
               } else if (randomDirection >= 100) {
               enemyDirection == 0; }     }
        WaitForVsync();
      CopyOAM();
 }   
   
    return 0;

}


void InitializeSprites(void)
{
   u16 loop;
   for(loop = 0; loop < 128; loop++) {
      sprites[loop].attribute0 = 160;
      sprites[loop].attribute1 = 240;
   }
}

void CopyOAM(void)
{
   u16 loop;
   u16* temp;
   temp = (u16*)sprites;
   for(loop = 0; loop < 128*4; loop++)   {
      OAM_Mem[loop] = temp[loop];
   }
}

void MoveSprite(OAMEntry* sp, int x, int y)
{
   sp->attribute1 = sp->attribute1 & 0xFE00;
   sp->attribute1 = sp->attribute1 | x;

   sp->attribute0 = sp->attribute0 & 0xFF00;
   sp->attribute0 = sp->attribute0 | y;
}


void MoveEnemy(void) {
   if (enemyDirection == 0 && xenemy < xMax && yenemy < yMax) {
      xenemy++;
   }
   if (enemyDirection == 0 && (xenemy >= xMax || yenemy >= yMax )) {
      if (xenemy >= xMax) {
         enemyDirection = 3;
      } else {
         enemyDirection = 1;
      }
   }

   if (enemyDirection == 1 && xenemy < xMax && yenemy > yMin) {
      xenemy++;
   }
   if (enemyDirection == 1 && (xenemy >= xMax || yenemy <= yMin )) {
      if (xenemy >= xMax) {
         enemyDirection = 2;
      } else {
         enemyDirection = 0;
      }
   }

   if (enemyDirection == 2 && xenemy > xMin && yenemy > yMin) {
      xenemy--;
   }
   if (enemyDirection == 2 && (xenemy <= xMin || yenemy <= yMin )) {
      if (xenemy <= xMin) {
         enemyDirection = 1;
      } else {
         enemyDirection = 3;
      }
   }

   if (enemyDirection == 3 && xenemy > xMin && yenemy < yMax) {
      xenemy--;
   }
   if (enemyDirection == 3 && (xenemy <= xMin  || yenemy >= yMax)) {
      if (xenemy <= xMin) {
         enemyDirection = 0;
      } else {
         enemyDirection = 2;
      }
   }

}

#53844 - Fatnickc - Mon Sep 12, 2005 5:07 pm

Looks very much like code from Webbesen.dk/gba's tutorial.
If it is based on that, read the original stuff over and check how yours differs.

#53904 - biubid_boy - Tue Sep 13, 2005 6:03 am

It's not based off it, but I'll check anyway.

#53907 - Fatnickc - Tue Sep 13, 2005 6:52 am

Code:
         if (timer < 60) {
         timer + 1; }


Maybe you should change that to timer += 1; or timer++; ?

#54022 - Miked0801 - Wed Sep 14, 2005 7:48 pm

Quote:

Code:

         if (timer < 60) {
         timer + 1; }
         else if (timer >= 60) {               
               if(randomDirection <= 25) { 
               enemyDirection == 0;
               } else if (randomDirection <= 50) {
               enemyDirection == 1;
               } else if(randomDirection <= 75) {
               enemyDirection == 2;
               } else if(randomDirection <= 100) {
               enemyDirection == 3;
               } else if (randomDirection >= 100) {
               enemyDirection == 0; }     }




This is very broken. Your main loop never updates your timer and your randomDirection is only called once and will never change. I've also cleaned up some other things here and there to make the code easier to read and maintain. Your MoveEnemy() function also needs some help, but that I leave to you. Here:
Code:

int main(void)
{
    u16 randomDirection;
    u16 timer = 0;
    u16 loop;

    // Set game to mode 4, sprites on, 1D VRAM
    SetMode(MODE_4 | BG2_ENABLE | OBJ_ENABLE | OBJ_MAP_1D);

    // Copy both sprite and OBJ palettes into VRAM
    for(loop = 0; loop < 256; loop++)
    {
        OBJ_PaletteMem[loop] = enemyPalette[loop];
        BG_PaletteMem[loop]=bgPalette[loop];
    }

    // Paint first screen
    for (loop = 0; loop < (120*160); loop++)
    {
        FrontBuffer[loop] = bgData[loop] ;
    }

    // Copy sprite VRAM data
    memcpy( (u16 *)0x06014000, &enemyData, sizeof(enemyData) );

    // Setup OAM for sprite
    sprites[0].attribute0 = COLOR_256 | SQUARE | yenemy;
    sprites[0].attribute1 = SIZE_8 | xenemy;
    sprites[0].attribute2 = 512;
 
    while(1)
    {
        // Update enemy and sprite positions
        MoveEnemy();
        MoveSprite(&sprites[0],xenemy,yenemy);

        // Don't change direction for a few seconds       
        if (timer >= 60)
        {
            // Get a number from 0 to 255 I guess???
            randomDirection = rand();

            // Choose a new direction based on random value
            enemyDirection = randomDirection / 25;
            if(enemyDirection >= 4)
            {
                 enemyDirection = 0;
            }
        }

        // Wait for vblank and copy data
        WaitForVsync();
        CopyOAM();

        // Next tic.
        timer++;
    }   

    return 0;
}


Notice the comments and code spacing/indenting. This tells in English what is going on and why. Some people like dense code, I prefer mine spread out just a little bit so I'm reading only 1 idea at a time. Makes it easier for me to debug and understand. Good luck!

#54246 - biubid_boy - Sat Sep 17, 2005 2:18 am

Thanks Mike! Your code is way more efficient than my one, but it's still not working. I guess it may have something to do with moveEnemy, so I'll check it out.

#54330 - biubid_boy - Sun Sep 18, 2005 11:09 am

Umm..... I'm still using the same code, but my sprite keeps getting stuck when it hits the end of the screen.....

#54331 - Fatnickc - Sun Sep 18, 2005 11:13 am

Well it would. In your MoveEnemy function, it checks that xenemy is less than xMin. If it did go off screen totally, you might get some 'chopping' appearing, and a messed up sprite appearing at the other end.

#54453 - Miked0801 - Mon Sep 19, 2005 5:16 pm

Your "Logic" for changing sprite direction that I emulated will cause the AI to tend to bounce against the top of the screen forever due to the weight you've given it. I noticed it originally, but wanted to move your code ideas over as is and let you take a crack at it. For random movement, you either neeed to bias towards a goal, or weight it completely evenly.

#54558 - biubid_boy - Tue Sep 20, 2005 12:07 pm

Weight? Do you mean when I do the sizeof thingy?

#54600 - Miked0801 - Tue Sep 20, 2005 7:11 pm

Code:

               if(randomDirection <= 25) { 
               enemyDirection == 0;
               } else if (randomDirection <= 50) {
               enemyDirection == 1;
               } else if(randomDirection <= 75) {
               enemyDirection == 2;
               } else if(randomDirection <= 100) {
               enemyDirection == 3;
               } else if (randomDirection >= 100) {
               enemyDirection == 0; }     }
 


This code is setup such that 25 values are assigned to 1, 2, and 3, and 181 values are assigned to direction 0. That means there is a 7 to 1 bias for your random routine to go direction 0 over any other direction.

#54663 - biubid_boy - Wed Sep 21, 2005 12:22 pm

So if I change what I divide by, then my code shouldn't be biased anymore, right?

#54714 - Miked0801 - Wed Sep 21, 2005 10:01 pm

I'm done, next tutor please. (Yes, dividing my 64 will fix this problem. Your task is to tell me why 64 will work.)

#54786 - tepples - Thu Sep 22, 2005 3:55 pm

There are 256 possible values. Divide by 64 (rounding toward 0, as operator/(int, int) always does) produces four possible values.
_________________
-- Where is he?
-- Who?
-- You know, the human.
-- I think he moved to Tilwick.

#54993 - biubid_boy - Sat Sep 24, 2005 9:09 am

I think Mike wanted me to figure that out. Anyway, how come every time I reset VisualBoyAdvance, the enemy moves in the same pattern as before?

#55015 - tepples - Sat Sep 24, 2005 5:36 pm

The enemy moves in the same pattern because pseudorandom number generators always produce the same sequence. You have to specify a "seed", or a starting position for the sequence. This is usually generated by calculating the time (in vblanks since power on, and possibly the scanline as well) at which the player presses the start button.
_________________
-- Where is he?
-- Who?
-- You know, the human.
-- I think he moved to Tilwick.

#55064 - biubid_boy - Sun Sep 25, 2005 3:51 am

Wait. So you want me to make a 'seed' that counts the vblacks since the start of the game.... How does that make the enemy change its pattern?

#55067 - tepples - Sun Sep 25, 2005 4:03 am

The sequence of enemy patterns is usually at least billions of steps long. If you change the seed, it starts the sequence of enemy patterns from a different point in this sequence.

If you're using a GBA flash card with a documented RTC, or you are on the Nintendo DS, you can use the traditional seed of the current date and time. If you have a savegame, you can checksum the savegame data to get a seed. Otherwise, you need to wait for press start and then use the elapsed time as a seed.
_________________
-- Where is he?
-- Who?
-- You know, the human.
-- I think he moved to Tilwick.

#55075 - biubid_boy - Sun Sep 25, 2005 5:31 am

I'll be using this on the M3 (hopefully), but I still need to know how to implement the seed thing and what registers the date and time are.

BTW: In case you didn't know, M3 has a clock/calendar built in.

#55191 - biubid_boy - Mon Sep 26, 2005 5:06 am

Anyone?

#55192 - tepples - Mon Sep 26, 2005 5:10 am

I believe the question was "what does the seed do?" which I answered. If not, what was the question again?
_________________
-- Where is he?
-- Who?
-- You know, the human.
-- I think he moved to Tilwick.

#55194 - biubid_boy - Mon Sep 26, 2005 5:15 am

I still need to know how to implement the seed thing and what registers the date and time are.

#55198 - poslundc - Mon Sep 26, 2005 6:04 am

Since you are using the ANSI rand() function, you should be able to set the seed to any value using the srand() function before the first time you call rand().

This means you'll have to take the call to rand() out of the variable's declaration in the file scope and declare it in code that you know will be executed after the call to srand().

It also means that if you want to produce a different set of random numbers each time your program executes, you'll have to come up with a way of passing a different number into srand() each time the program executes. A number of ways have already been suggested.

Sorry, but I can't help you with the RTC, except to say that there's a good chance that your emulator won't support that kind of custom feature, so it might be a good idea to find a different way.

Dan.

#55199 - biubid_boy - Mon Sep 26, 2005 6:23 am

Every time I try to load a value into srand (eg. srand() = 7) I get an invalid lvalue assignment error.

#55200 - Fatnickc - Mon Sep 26, 2005 6:54 am

I think you're meant to use
Code:
srand(7);

However, you will probably want to use a different number each time. So, use, for example, and integer that you've been incrementing by one every tic, until Start is pressed, in order to seed the rand function 'randomly'. However, as has been mentioned, you'll have to make sure that this is done before using rand().

#55201 - tepples - Mon Sep 26, 2005 6:57 am

Most flash carts' RTCs have the same register level interface as the one used in Pokemon games. Google GBA RTC demo gives a demo by AmPz.
_________________
-- Where is he?
-- Who?
-- You know, the human.
-- I think he moved to Tilwick.

#55205 - biubid_boy - Mon Sep 26, 2005 9:32 am

@Fatnickc: I'd tried that before but I thought it must have been wrong because my sprite doesn't move at all when I do it.

#55214 - Fatnickc - Mon Sep 26, 2005 1:28 pm

Are you sure?
Perhaps that's because you were just lucky with the random number you used to get, as it wouldn't change. However, now that we're in the big wide world of seeding our rands, you may, and probably will, get very big numbers sometimes. So, for this, you could use modulus.
For example, to get a random number below 160, you could use :
Code:
...
srand(s);
y=rand()%160;
...

#55222 - poslundc - Mon Sep 26, 2005 5:05 pm

biubid_boy wrote:
Every time I try to load a value into srand (eg. srand() = 7) I get an invalid lvalue assignment error.


Please read a book or tutorial on C. A large part of the reason you are having difficulty is because you are stumbling on very basic elements of the language.

Dan.

#55248 - biubid_boy - Tue Sep 27, 2005 1:17 am

Sorry. I've been meaning to do some pc programming for a while but never got round to it. Thank you all for your help so far and putting up with my newby questions.

#55274 - Fatnickc - Tue Sep 27, 2005 9:07 am

It is very helpful to have a background of some programming. Not only will it enable you to code a few lines without having to look up how to do something, but it will mean that you have ideas of how you could solve problems, and which solution would work the best.