ASnogarD
Level 1
|
 |
« on: November 21, 2010, 09:00:46 AM » |
|
Ok, I can finish my game if I carry on but the code will be a mess and I am not happy with that... hacking a pile of objects together to create a simple game may work for a simple game, but teaches me nothing.
Had loads of text, mostly rants because I am frustrated but rather than waste bandwith and your time with walls of text I'll just ask.
Is there some resources online for C++ and SDL source code with good examples on how to set up a framework suitable for a 2D scrolling game with a decent number of objects ( ie enough objects to require some for of array, vector , stack or list to control )? Reccommend me some source code I can examine ?
I tend to grasp concepts more easily if I can see how other use them, and then expand from that.
|
|
|
|
|
Logged
|
Somethings are painfully obvious, others must be made obvious... painfully.
|
|
|
|
Netsu
|
 |
« Reply #1 on: November 21, 2010, 09:24:51 AM » |
|
Unfortunately I don't know any resources, but maybe I'll try to show how my frameworks are built: The main loop (this one goes on and on until it is time to close the game) - Check the time elapsed from the last loop
- For each 1/100th of a second that elapsed execute the function:
- - logic function (here I put all the logic of the game, movement, collision, input)
- Now execute this function once:
- - render function (here I just draw everything that's supposed to be drawn)
- Repeat
Now for the objects, I keep a container for all the objects (an STL list to be precise) and iterate through all the objects in the logic function. Each object has an enum type that describes the type of the object. Now depending on the type of the object different actions are taken. For example if it has type ENEMY I move it towards the player, if it has type BULLET I check for collision with enemies etc. The same container is used to draw everything that is in it on the screen. Now there is an additional hack that I use, but I don't know if it's good programming (it is rather not beginner stuff though). All the objects in the container are of the same class (it's called particle) but aside from the enum type, the class has a void pointer. This pointer is used to point to a "parent" object. So for example... I have a player class and an enemy class. Both those classes have a pointer to an object of class particle inside them. Only the particle object is stored inside the STL list. So I only iterate through the particles, but if the enum type is ENEMY I cast the void pointer to the parent and this way I can access functions of it's parent object (the object of class enemy). This way I have a uniform class that shares the functions for collision detection, physics, movement etc. and I can put additional functionality in them by packaging them in other classes (like the enemy class) and pointing to this parent object upon creation. I hope this is of some help to you.
|
|
|
|
|
Logged
|
|
|
|
ASnogarD
Level 1
|
 |
« Reply #2 on: November 21, 2010, 10:50:09 AM » |
|
Thanks for the reply.
The issue I am having is jumping from instantiating objects , or making basic naked pointer to my classes to those... lists of pointers, and those message systems I read about where objects pass messages to each other for access.
Currently I can declare a object and use that , or declare an object and send a pointer to other classes which use that pointer in that classes constructor. I believe this is called using 'naked' ( or bad ) pointers ? I have issue though when it comes to a array of objects, and then multiple arrays of objects all instatiated through a all purpose class.
My game at the moment has a world class that instantiates a array of tile objects, stick objects and coin objects... it also controls the move function of the player ( need tile data ), the camera ( the tiles take in the camera bounding box to determine if they should be drawn or not ), it also checks for collisions between objects...
... in short its a huge mess of a class, ideally I would love the tile , stick and coin classes to manage thier own drawing and collision but as they need to be instantiated as a array, and then use a loop to go through the set of tiles/sticks/coins I cant put that in the tile or stick or coin class.
I seem to bump into cases where I cant move that function because it relies on data from another class/function ... a lot. I done a lot of reading in theory on game development but a lot of material neatly sidesteps the issue by merely stating to use a list, or a message system but dont really say how.
|
|
|
|
|
Logged
|
Somethings are painfully obvious, others must be made obvious... painfully.
|
|
|
|
Netsu
|
 |
« Reply #3 on: November 21, 2010, 11:13:48 AM » |
|
I don't know if I understood correctly what your problem is, but here is how you can dynamically create objects: Let's say you have your coin class. Create a container for pointers to objects of this class. For example using an STL vector (because it's the simplest I guess): vector <coin*> vector_of_coins; Now you can create a new coin and put it in the container every time something happens, for example: if (shit_happened) { coin* pointer = new coin; vector_of_coins.push_back(pointer); } Now in your logic function, world class, or whatever you are using you can iterate through the container and execute methods of your coin class, for example a function that draws your coin on the screen: for (unsigned int i = 0; i < vector_of_coins.size(); i++) vector_of_coins[i]->draw();
The same way you can create a container for your sticks and tiles. Is this what you had problem with?
|
|
|
|
|
Logged
|
|
|
|
|
mcc
|
 |
« Reply #4 on: November 21, 2010, 11:27:27 AM » |
|
I'm not sure I understand the problem you're having.
What do you mean by "naked pointer"? Do you mean you are using "void *"?
Are you using polymorphism? That is, do "tile", "stick" and "coin" share a common superclass?
For what reason do you believe "tile", "stick", and "coin" need to be different classes in the first place?
The normal use of subclasses is when you have a set of objects which will act the same under some situations, but different under other situations, you declare the common functionality in a parent class, and put the functionality which differs inside of "virtual" functions. Each subclass can have a different implementation of that virtual function; when you need your different objects to "act different", you call one of the virtual functions, and the version of the virtual function defined for that particular object will be called. The object knows which function to call because it has something called a "vtable" it lugs around with itself; if you say pointertoobject->function(), as long as the type of pointertoobject is typed as the class or one of the class's parent classes, C++ will know which method to "really" call by using the vtable.
For most people, I think it requires some degree of training and practice before you master "thinking in virtual functions". If you are not familiar with OO design it may be smarter for now to just create a single class/struct, something like "entity", and then have there be like an enum entitytype { entity_tile, entity_stick, entity_coin };. Later, when you write code which operates on the entity, you can say switch (entity->etype) or something.
|
|
|
|
|
Logged
|
|
|
|
ASnogarD
Level 1
|
 |
« Reply #5 on: November 21, 2010, 01:36:36 PM » |
|
I'll try explain a bit better...
in World.cpp before the constructor I make a pointer to an array of tile objects:
Tile *tiles[ TOTAL_TILES ];
now the tile class is merely a SDL_rect ( for x and y offsets) , a type and a state members.
in the World class constructor I have a bit of code that does this :
set_tiles();
which calls a function in the class that does:
{ int x = 0, y = 0;
std::ifstream map( "GFX/my.map" );
if ( map == NULL ) { return false; }
for ( int t = 0; t < TOTAL_TILES; t++ ) { int tileType = -1; map >> tileType;
if( map.fail() == true ) { map.close(); return false; }
if( ( tileType >=0 ) && ( tileType < TILE_SPRITES ) ) { tiles[ t ] = new Tile( x, y, tileType ); } else { map.close(); return false; }
x += TILE_WIDTH;
if( x >= LEVEL_WIDTH ) { x = 0; y += TILE_HEIGHT; }
if( ( tileType >=4 ) && ( tileType < TILE_SPRITES ) ) {
tiles[ t ]->set_state( TILE_COLLIDE );
} else tiles[ t ]->set_state( TILE_AVAILABLE );
}
map.close();
return true; }
I could probably do the same thing with a vector , but I still have the same issue:
I will always need to use the World class to run through the list / array of tile objects to check things like collision , or state , or type. Which means other classes that need to know a tiles condition will rely on World to check the tile, and as my stick and coin classes are nearly the same as the tile ( just the array is not seeded by a external file) , each coin and stick check is also going through world. This has made my World class a huge mess of functions that other classes depend on to work, and a mess as its grown large as a result.
I am not using ANY inheritance at all, nor any friend classes ... just basic classes. The stick , tiles and coins are pretty different to each other. The tiles are the playing field while the sticks and coins are object on the field.
note: a lot of the code is based or copied from Lazy Foos tutorials , I adjusted it to work in seperate files and to suit my own requirements.
|
|
|
|
|
Logged
|
Somethings are painfully obvious, others must be made obvious... painfully.
|
|
|
|
Netsu
|
 |
« Reply #6 on: November 21, 2010, 03:17:27 PM » |
|
You could create a static class that would act as an interface between your containers (be it vector or plain arrays) and the rest of the program. That's not that different from your World class, only you don't need to put all your function in this class. You could do it like this: class World { private: Tile *tiles[ TOTAL_TILES ]; public: Tile* get_tile(int number); }
Tile* World::get_tile(int number) { return tiles[number]; } Still not sure if this is what you're looking for 
|
|
|
|
|
Logged
|
|
|
|
ASnogarD
Level 1
|
 |
« Reply #7 on: November 21, 2010, 05:01:29 PM » |
|
Thats something to think about at least, basically a class that uses another class for its data member types... a composite class to make 1 'object'. Thanks for your replies 
|
|
|
|
|
Logged
|
Somethings are painfully obvious, others must be made obvious... painfully.
|
|
|
|
Average Software
|
 |
« Reply #8 on: November 21, 2010, 06:07:02 PM » |
|
I'll try explain a bit better...
in World.cpp before the constructor I make a pointer to an array of tile objects:
Tile *tiles[ TOTAL_TILES ];
That's not a pointer to an array of tile objects, it's an array of pointers to tile objects. There's a significant difference between the two.
|
|
|
|
|
Logged
|
|
|
|
|
mcc
|
 |
« Reply #9 on: November 21, 2010, 09:40:46 PM » |
|
I guess I'm still confused what it is you're unsatisfied with here.
Also what did you mean by "naked pointer"?
|
|
|
|
|
Logged
|
|
|
|
|
Zaphos
Guest
|
 |
« Reply #10 on: November 21, 2010, 10:09:29 PM » |
|
Also what did you mean by "naked pointer"?
I think it means the pointer is not wrapped in some smart pointer-esque class (eg *not* using any of boost's smart pointer stuff).
|
|
|
|
|
Logged
|
|
|
|
ASnogarD
Level 1
|
 |
« Reply #11 on: November 22, 2010, 05:11:14 AM » |
|
I am dissatisfied by the way my World class code has lots of functions that belong in other classes but cant be moved due to dependancies, due to my inability to pass data about efficiently. The class is nearly 1200 lines by itself.
I read about how some designs use a list of pointers to control the objects? How ? I have read of other systems that use a message system , ie when you press the left arrow instead of setting the xVel directly it generates a message ' PLAYER_LEFT_REQ ' and the functions 'registered' to that message 'id' ... respond. How ?
By naked pointer I mean its just a pointer to a object declared, nothing else. Example: in main I declare a World object...
World mWorld( &mIO, &mPlayer, &mAli );
I pass to its constructor refrences to the other objects it needs to know about. In World I have a equal number of private pointers ( *mIO, *mPlayer, *mAli ) and I set the pointer to passed refrences so they point to the correct location. It works but is very clumsy, adding a new object means changing the constructor and everything.
Its hard for me to explain because I dont know exactly what it is I am looking for, its more a case I am sure theres a better way / method to pass objects about to various classes to access functions.
If it helps you to understand my issue , my programming level is basically a beginner ( I am still reading about inheritance , polymorphism , friend functions / classes ) that got sick of samples which output only to a console so I started learning about SDL via Lazy Foo's tutorials. Neither my C++ Primer Plus book nor Lazy Foo tutorials deal with multi file classes, so there isnt much to see regarding how you deal with complex class interation.
|
|
|
|
|
Logged
|
Somethings are painfully obvious, others must be made obvious... painfully.
|
|
|
|
Netsu
|
 |
« Reply #12 on: November 22, 2010, 05:24:36 AM » |
|
I read about how some designs use a list of pointers to control the objects? How ?
This is basically what I posted before. World mWorld( &mIO, &mPlayer, &mAli );
I pass to its constructor refrences to the other objects it needs to know about. In World I have a equal number of private pointers ( *mIO, *mPlayer, *mAli ) and I set the pointer to passed refrences so they point to the correct location. It works but is very clumsy, adding a new object means changing the constructor and everything.
You might want to create functions designed to add a certain object to the World class. For example a separate class that creates a player object and adds a pointer to it to the World. In general, don't add functions to the World class, add them to the objects they belong to and then run them from the pointer the World class has.
|
|
|
|
|
Logged
|
|
|
|
ASnogarD
Level 1
|
 |
« Reply #13 on: November 22, 2010, 06:07:54 AM » |
|
Thats what I want to do... have the objects class contain the functions but due to dependancies its difficult.
My player move function is in World because it relies on detecting a collision with a collidable tile, to find out which tile may collide with the player box the collision function runs through the list of tile object pointers and checks each for a collsion with the player box.
In the move function there is a bit that goes basically " If ( touches_wall == true ) then take away the velocity ", and touches wall basically calls a world function to check for collision between 2 SDL_Rects ... one is the player box and the other being a tile box.
If I put the move function to the player class it doesnt know what a tile is, tiles is intantiated in World, as a array ( of pointers ), adding a world refrence to Player would cause a cyclic dependancy between world and player and I dont understand forward declaration enough to use that to get away from that issue.
Additionally you SAY you use a STL list of pointers to objects to access your objects, how does this work ? Do you have a class that includes all the classes of the objects and it created a object that is a List of pointers ? Would this allow you to access a function in a given class by calling its element name in the list and -> to the desired function ? Could this list deal with a large number of similiar objects... like your mentioned bullets, in a bullet hell game? Do all the objects go into 1 list and using your ENUM type to control its action ?
I mean I have read about lists and message systems but no idea how to implement one.
I am sorry if I am being a bit slow here, I am trying to ensure I learn from each game to the next and feel I need to understand how to use pointers and refrences to controll access better. I intend to use 3 games to build my way to the game I started learning developement for, though none of the games will be just plain ' it works so there '.
|
|
|
|
|
Logged
|
Somethings are painfully obvious, others must be made obvious... painfully.
|
|
|
|
Netsu
|
 |
« Reply #14 on: November 22, 2010, 06:16:05 AM » |
|
Forward declaration isn't hard but I think there is a better solution to all your problems. Declare World as a static class. Then you don't need a pointer to it, you just reference the world by it's name and have easy acces to all it's functions from anyhwere in the code.
So you can put a STL vector of tiles in the World class exactly the way I described earlier. Then create a player method for detecting collision with tiles that uses the World::get_tile(int) function to get information about tiles.
You could either create one big list/vector with all the objects and use enum OR create separate lists/vectors for bullets/tiles/whatever and put them all in the static World class, with each list having it's own access function (like the one I described earlier).
|
|
|
|
|
Logged
|
|
|
|
|