I'm working on a basic 2d game framework with Sdl, but I'm not sure how to make instances and game assets available to the game objects. any help would be appreciated. I've followed lazyfoo's tutorials but he keeps most of the code in main.cpp and uses global variables which gets messy.
for example; in a game object class I need to access the tilesheet, the renderer, sounds, etc.
I have a ImageManager class that handles loading, storing and drawing images.
if i create an instance for it in main, how can all of my classes use it?
1 2 3 4 5 6 7 8 9 10 11
some example code of the problem
//in main.cpp
ImageManager imageMgr = ImageManager();
System system = System();
SDL_Renderer * renderer = system.GetRenderer();
//in Player.cpp
void Player::Draw()
{
imageMgr.Draw(renderer,x,y,GAME_SPRITES_ENUM,frame);
}
The best solution i've found so far has been to create a singleton with the imagemanager instance defined in it or to make the things i need global. is there a correct way of doing this? should i just use singletons or make things like the renderer and assets global?
I have considered that, but I feel like the number of things I would need to pass through parameters would quickly become overwhelming and tedious. Passing in tilemap collision data, tile properties, sounds, music, spritesheets, etc. or passing in the manager for each. If you were making a game about the complexity of Nes Zelda is that how you would do it?
Ideally I'd like to make it really user friendly when writing game objects and be able to access what i need from within the class and not have to pass everything in. I've frequently read it's bad to make much of anything global though and other than using singletons i can't think of another way.
struct parameters
{
int x,y,z;
double h,p,r;
vector<unsignedchar> texture;
vector<polygons> mesh;
... etc
};
void foo( parameters &p);
or, if the items exist in main as variables, bundle up references to pass:
struct refpars
{
int &x;
int &y;
int &z;
refpars(int rx, int ry, int rz) : x(rx), y(ry), z(rz) {}
};
I would prefer the first idea, get rid of loose variables in main, bundle them up, make an object of the type in main, and then if you need a second set of them its easy to do that. The reference idea might work if you had a LOT of code already written and it was hard to fix, but you moved the problem, now you have a ginormous constructor for the wrapper object, no way around that. The second idea works ok if you only need a few of them sent to various functions. You can use a tuple or other ideas to avoid manually making the structs, if you want.
oh, I see how that can work.
So let's say the player object needs the object manager, sound manager, image manager, event handler, etc. i could bundle those all into a engine struct or class and pass that in the parameters so that the player can use the Draw(), PlaySound(),etc. and that would be a better design than making the managers global or using a singleton? also is that something I would pass in the constructor every time I create an instance?
yes, grouping them up and avoiding globals is the way to go.
um, if you are creating a bunch of copies of the same data, that isn't what I was trying to do at all.
lets do an example, then:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
struct managers
{
object_manager om;
sound_manager sm;
event_handler eh;
};
void foo(managers &mgrs)
{
mgrs.om.somethingorother();
}
int main()
{
managers m; //this creates one copy of each of the objects.
foo(m); //this updates or uses some of the variables and their data etc
}
you can set up managers to use an initializer list or constructor as needed.
but do you need more than one managers object, ever?
or were you asking about the reference version?
I am not a big fan of singletons; as soon as you go down that road you wll find out within a week you needed 2 of the darn thing.
Seems like a good way of doing it and might actually help prevent some of the inheritance loop errors i've encountered. I don't need a bunch of copies of the same data. I was only planning on having 1 manager ever for each which is used specifically for interfacing with the SDL api in a convenient way from anywhere in my project and preventing things like loading the same image more than once. A good compromise could be only having 1 singleton in which you define the managers rather than making them all singletons.
with my current set up it works something like
1 2 3 4 5 6 7 8
Player::Player()
{
e = Engine::GetInstance();
}
Player::Update()
{
e->Draw(x,y,PLAYER,frame);
}
I know globals are considered bad practice. some ppl don't like singletons. I'm just trying to figure out if there's a "right way" or if i'm on the right track lol.
singletons are fine. I don't like using them, but that is my personal style an approach to things. Globals are not a good idea, they cause literally a half dozen or more potential problems in larger programs. They work fine in tiny programs, I suppose, but the big thing about globals is that you do not need them: they solve no real problems, and create serious problems, making them a double no-go for most experienced programmers.
there are some tricks you can play to make quasi globals that are safer than the raw thing ... put them in a namespace, put them as static members of a class, and similar ideas. But these tricks only solve some of the problems (like name conflicting) and leave some of the others (who changed it last?! or threading issues (its being changed by 2 things at the same time!)