#176491 - MisterLogan - Sun Aug 07, 2011 8:38 am
Is there any way to choose randomly from a changing set of functions based on a random number? I've seen a mention or two of putting function names without the "()" in arrays but i don't know how that works. Googling keeps getting me results for various "rand" functions :/.
The reason I ask is I am loading a set of sprites, the number of sprites and what sprites I'm loading depend on the situation (the map changing), and I need to be able to move a random sprite at a regular interval. Each sprite is moved by calling a function though, and I don't know how to randomly choose between a changing set of functions.
Any ideas on this/something in c++ that does this that I don't know about? (I'm quite new to programming in general)
Thanks
#176492 - ant512 - Sun Aug 07, 2011 8:50 am
What you need is an array of function pointers. Pointers can point to anything in memory, and since functions exist in memory, you can point at them too. Store them in an array and call an array element as if it were a function. There's a Stack Overflow question about this here:
http://stackoverflow.com/questions/252748/how-to-use-array-of-function-pointers
This is the basic idea:
Code: |
#include <stdio.h>
#include <stdlib.h>
// These are all of the functions we're going to choose from randomly
int sum(int a, int b) { return a + b; }
int subtract(int a, int b) { return a - b; }
int mul(int a, int b) { return a * b; }
int divide(int a, int b) { return a / b; }
// This is our array of function pointers
int (*p[4]) (int x, int y);
int main(void)
{
int result;
int index;
// Insert pointers to functions into our array
p[0] = sum; /* address of sum() */
p[1] = subtract; /* address of subtract() */
p[2] = mul; /* address of mul() */
p[3] = divide; /* address of divide() */
// Choose a random index
srand(50);
index = rand() % 4;
// Call the function
result = (*p[index])(1, 2);
printf("Result: %d\n", result);
printf("Index: %d\n", index);
return 0;
}
|
This will work in C and C++, but it will only work for functions, not methods of C++ classes. If you need to do that you're better off investigating ways of declaring the function signature in an abstract base class, then encapsulating the different functionality within different implementations of the base class, and storing instances of the objects in an array. C++ does allow pointers to member functions, but they're very limited and kind of crap.
You'll probably find that every time you run this program you get the same result out. The reason for that is the way rand() works in C. Try changing the value passed to srand() from 50 to something else and you'll find that the program's result changes. For an explanation of this, you'll need to look up random number seeding in C.
#176493 - MisterLogan - Sun Aug 07, 2011 9:28 am
Thanks for the quick reply.
I tried putting this in tonight but i think my brain is a bit too tired, i ended up somehow jumping back to the start of main every time i access the array.
I'm sure it will all make sense when I wake up though.
#176494 - vuurrobin - Sun Aug 07, 2011 1:20 pm
ant512 wrote: |
You'll probably find that every time you run this program you get the same result out. The reason for that is the way rand() works in C. Try changing the value passed to srand() from 50 to something else and you'll find that the program's result changes. For an explanation of this, you'll need to look up random number seeding in C. |
I'm pretty sure the accepted way of doing that is to use the time function.
_________________
my blog:
http://vuurrobin.100webcustomers.com/
#176496 - MisterLogan - Sun Aug 07, 2011 11:20 pm
If you aren't passing any variables, do you access it with a "()" after the "(*name[option])"? And the same question for declaring the array.
As far as random number seeding goes, I just start a timer at the start of main and get it's value when start is pressed at the title screen before turning it back off.
EDIT: I've been messing with this for a while and I can't seem to stop it from looping back into main. Either my syntax here is wrong or I messed something else up without noticing.
I create the array as such
Code: |
void (*pNPCFuncs[128])(); |
Then add the functions to the array later on like so
Code: |
pNPCFuncs[OAMCounter] = updatesadghostTiles; |
Then call the functions like this
Code: |
(*pNPCFuncs[(Dice2+NPCOAMStart)])(); |
I've been messing with syntax for a while but this was the only way it would compile.
#176497 - sverx - Mon Aug 08, 2011 9:05 am
Some weeks ago it also happened to me to need to use function pointers, and here's where I learnt how to do it: http://www.newty.de/fpt/index.html :)
#176499 - ant512 - Mon Aug 08, 2011 6:09 pm
MisterLogan wrote: |
I've been messing with syntax for a while but this was the only way it would compile. |
Yep, that's the way to do it.
#176500 - Dwedit - Mon Aug 08, 2011 8:14 pm
Function pointers are just begging to be typedef'd to something more reasonable.
Something like this:
Code: |
typedef int (*FuncIntInt)(int); //function that takes in an int, and returns an int
FuncIntInt functions[10];
otherInt = functions[5](someInt);
|
Not sure if that syntax is exactly correct or not, but it sure simplifies function pointers.
_________________
"We are merely sprites that dance at the beck and call of our button pressing overlord."
#176501 - MisterLogan - Mon Aug 08, 2011 11:07 pm
Well, I can't seem to get it working at all, as far as I can tell I've implemented it correctly, but every time I call a function it just starts main from the top. Is there any reason anyone knows of that would cause this? Does using a function like this not return control to the function I call it from normally?
#176507 - ant512 - Tue Aug 09, 2011 9:24 am
MisterLogan wrote: |
Is there any reason anyone knows of that would cause this? |
The only reason I can think of is that you're clobbering the stack somehow and overwriting the return address. The chances of you managing to overwrite that with the exact address of the start of main() is tiny, though.
Quote: |
Does using a function like this not return control to the function I call it from normally? |
It should work like any other function call. Can you post some of your code?
#176512 - Azenris - Tue Aug 09, 2011 3:32 pm
Any source you could show?
_________________
<Robert Morley>
My Homebrew Games
#176515 - elhobbs - Tue Aug 09, 2011 6:07 pm
mixed calling conventions could cause some weird behavior - stack vs register parameters - expecting one and using another ... source code is probably the best option though, as Azenris already said.
#176517 - MisterLogan - Tue Aug 09, 2011 8:38 pm
I don't know what else i can post exactly, I'll put up the functions that get called and such though. Beware though, I am a huge newbie to C++ and programming in general, so I'm sure I'm doing silly things somewhere here. (try to not make too much fun if I'm doing silly things)
Alright, this is pretty much the same, it's global.
Code: |
void (*pNPCFuncs[128])() = {NULL}; |
The first function is the one I'm putting in the pointer array, the other functions are exact copies with different names due to not having drawn more NPCs. When I load a new map I can add as many initTiles functions as I need and they take up a place in OBJ mem and OAM and mark an index (Isadghost) for both the OAM and OBJ mem placement so I can easily change them later. The indexes are also global (I normally pass variables for pretty much everything but I got pretty lazy last night). So that's that, to edit a sprite I just change AnimStage to whatever I need and then run whatever updateTiles function I need.
Code: |
void updatesadghostTiles()
{ //kspritesize is 128
for(loop = 0; loop < (kSpriteSize*2); loop++)
OAM_Data[IsadghostOBJ+loop] = sadghostTiles[loop+(AnimStage*kSpriteSize*2)];
}
void initsadghostTiles(u16 &OBJMEMCounter, u16 &OAMCounter)
{
IsadghostOBJ = OBJMEMCounter;
IsadghostOAM = OAMCounter;
sprites[OAMCounter].attribute0 = COLOR_256 | SQUARE | SprPosY[0];
sprites[OAMCounter].attribute1 = SIZE_16 | SprPosX[0];
sprites[OAMCounter].attribute2 = PRIORITY(1) |OAMCounter*8;
OAMCounter += 1;
sprites[OAMCounter].attribute0 = COLOR_256 | SQUARE | (SprPosY[0] + 16);
sprites[OAMCounter].attribute1 = SIZE_16 | SprPosX[0];
sprites[OAMCounter].attribute2 = PRIORITY(3) | OAMCounter*8;
OAMCounter += 1;
//+= kspritesize*2 because I load two sprites here (for priority). Why 1 and 3 instead of 2 and 3? More problems!
OBJMEMCounter += kSpriteSize*2;
pNPCFuncs[OAMCounter] = &updatesadghostTiles;
updatesadghostTiles();
} |
I use the pointer array in my NPCAnim function (which is almost entirely commented out right now)
Code: |
void NPCAnim(u16 &Dice1, u16 &Dice2, s16 &SpriteHoldX, s16 &SpriteHoldY, u8 &NPCLoop, u8 &Nothing)
if(NPCLoop == 0)
NPCLoop = 16;
//dice1 is a random between 1 and 8 or something like that. 1-4 correspond with a movement direction and the rest makes nothing happen.
if(Dice1 > 4)
Nothing = 1;
if(NPCLoop == 16)
if(Nothing == 0)
{
AnimStage = (Dice1*2)-1;
(*pNPCFuncs[(Dice2+NPCOAMStart)])();
}
if(NPCLoop == 8)
if(Nothing == 0)
{
AnimStage = (Dice1-1)*2;
(*pNPCFuncs[(Dice2+NPCOAMStart)])();
}
NPCLoop -= 1;
if(NPCLoop == 0){
Dice1 = 0;
Nothing = 0; |
}
[/code]
I took out all the commented stuff, it's all just NPC movement and object collision. For some reason when I wrote this code originally I made the loop start at 16 and go down? Anyway, it's not too hard to understand what's going on here.
And finally, here's where I add the sprites to OAM and OBJ mem. This section is in main.
Code: |
///********
///general template for map loading.
///********
setSprites();
AnimStage=0;
///main
initGhostlyTiles(OBJMEMCounter, OAMCounter);
initwalkfxTiles(OBJMEMCounter, OAMCounter);
///npcs
NPCOAMStart = OAMCounter;
NPCCount = 2;
initsadghostTiles(OBJMEMCounter, OAMCounter);
initsadghostTiles2(OBJMEMCounter, OAMCounter);
///bg sprites
initflowerTiles(OBJMEMCounter, OAMCounter);
OBJMEMCounter=0;
OAMCounter=0;
copyOAM();
///******* |
Last bit probably doesn't matter but it shows what NPCOAMStart is.
Anyway, thanks for any help, feel free to tell me if I'm doing unrelated things wrong.[/code]
#176520 - ant512 - Wed Aug 10, 2011 10:50 am
MisterLogan wrote: |
Code: | pNPCFuncs[OAMCounter] = &updatesadghostTiles; |
|
There's one problem - get rid of the ampersand.
#176528 - MisterLogan - Wed Aug 10, 2011 7:56 pm
Oh, that's from messing around with syntax from that tutorial someone else posted, it has the same problem both ways, but I'll remove it.
#176529 - Azenris - Wed Aug 10, 2011 8:17 pm
I think you're supposed to use the ampersand, not using it is just a short-form. Another short-form (I think) is you don't have to specify the dereference, when used like a function.
_________________
My Homebrew Games
#176535 - ant512 - Thu Aug 11, 2011 10:06 am
This looks a bit suspect. Here's initsadghostTiles() with sections snipped out:
void initsadghostTiles(u16 &OBJMEMCounter, u16 &OAMCounter)
{
...
OAMCounter += 1;
...
OAMCounter += 1;
pNPCFuncs[OAMCounter] = &updatesadghostTiles;
...
}
You end up with an array that looks like this:
pNPCFuncs[0] = NULL
pNPCFuncs[1] = NULL
pNPCFuncs[2] = &updatesadghostTiles
pNPCFuncs[3] = NULL
pNPCFuncs[4] = NULL
pNPCFuncs[5] = &updatesadghostTiles
Is that what you intended? If you aren't expecting to have an array that's 2/3rds empty, you're calling NULL() like a function and executing data/code/who knows what.
#176538 - MisterLogan - Thu Aug 11, 2011 10:01 pm
Arghhhh, how did I not notice that?! No, I didn't mean for that to happen, I don't know what I was thinking... They're supposed to be right next to each other and I guess I forgot OAMCounter was going up by two for the larger sprites.
Should be loaded like Code: |
pNPCFuncs[((OAMCounter-NPCOAMStart) >> 1)-1] = &updatesadghostTiles2; |
and just called as whatever dice2 is.
Thanks a lot, it works fine now!
#176544 - MisterLogan - Sat Aug 13, 2011 7:31 am
Alright, I got one more syntax question in this vein.
It's pretty much the same thing but for arrays, the situation being an array of array pointers (pointing to various consted map arrays). The problem being, how to call an individual element of an array, through that array of array pointers.
Quick example here.
I would guess the syntax is something like this. (bgMap is a const [256][256] array.)
Code: |
u16 *MapIndex[1] ={
&bgMap
}; |
But I can't find any helpful information for this particular situation through google, there's some info on pointing to arrays but it doesn't really fit.
So here's where I'm really just throwing guesses.
Code: |
for(loop = 0; loop < 32; loop++){ // I need to access this element from bgMap through an array that is pointing to bgMap.
MapBuffer[lots of numbers here] = /*what to do here*/[(BGYTiles+30)+MapUpdateSwitch][BGXTiles+loop];
} |
I don't even know if that's a possible thing to do, but I don't know how I would accomplish this yet without something similar. Do I need to copy the pointer from the pointer array to a temp pointer so as to not be accessing an array element through an array element? If this is a thing, do I need to dereference it?
Feel free to tell me if this is a silly thing that is impossible. Thanks.
#176547 - Miked0801 - Sun Aug 14, 2011 5:29 am
Play with parenthesis. When you get into these situations, * and [] don't always bind in the order you expect them to.
#176548 - MisterLogan - Sun Aug 14, 2011 7:58 am
I will play with some parenthesis for now, but I think I'm missing some syntax somewhere here.
I ditched the array of array pointers, I realized it was entirely superfluous. So, from what I understand about pointers, there's a pointer, CurrentMap, pointing to the address of the first element in bgMap. So it would seem like I need to add the number of the element I need to access on to that memory address, then dereference to get the value I need. That makes perfect sense in my head. But I don't see how to do that with a two dimensional array. Does using currentmap like a two dimensional array with a * somewhere just work that out for you? I've been trying to get it to work like that with different parentheses like you said, but I just get an "invalid types 'const unsigned int[int]' for array subscript" error. I guess I could just use a y*xsize + x and switch to a 1d array but that seems kind of silly at this point.
Another thing I my brain skimmed past in a sleepy haze was that i'm making the pointer a const unsigned int. That, of course, defeats its purpose completely. But I can't unconst the pointer or I get a compile error about the conversion. Is there a way around this or should I just give up and find another way to do it?
I will keep playing with these parentheses though.
#176549 - Miked0801 - Sun Aug 14, 2011 3:08 pm
There's nothing silly about y*xsize+x, that's exactly what the compiler's doing internally anyways. If using a 1D array is what it takes to move forward, then do it. More game making, less C compiler fighting :)