A bunch of really stupid code-structure questions.

Started by
9 comments, last by Heelp 7 years, 6 months ago

Guys, I present you my longest and stupidest post ever. :lol:

Question No.1:

I've asked a similar question before, but I guess I didn't understand everything. It's concerned with the storage of all my game resources.

By resources I mean my vertex and fragment shaders and my models. I store all of them in a ResourceManager class (I don't know why I called it that way, it doesn't manage anything, it just stores all the stuff, but nevermind).

And when I need to create a player, I just do this: Player player = Player( myResourceManager.getSomeAnimations(), vec3 position );

And the animations I supplied as an argument go to a pointer in the Player class.

So what I do is just storing pointers to the animations that can be found in the ResourceManager.

But what I don't like is that every time that I need a shader or a model in a function, I have to supply ResourceManager as an argument to the function and it gets irritating, because I add too much arguments to every function.

I know the problem may sound stupid to you, but I still decided to ask here because I'm wondering why everything should be kept in one class, and not separated and is this the right way to do things.

Question No.2

All my classes are united in one class called "App". I'm pretty sure they call this 'composition'. This is my App class:


//Some includes...

class App
{
public:

App();
~App();

InputManager myInputManager;
NetworkManager myNetworkManager;
ResourceManager myResourceManager;
Timer timer;
vector<Player> players;

//And so on.....

};

#endif

But now, for example, if I want to add another function to my NetworkManager class and that function needs a variable that is stored as a private to the class 'App' that owns an object of the type NetworkManager, I need to pass a reference to App as an argument to a NetworkManager function and it looks something like this

myNetworkManager.serverReceivePackets( App& myApp );

So I'm actually passing as an argument an object that contains the object to which the function belongs, and it seems really bad to me,

because I can access the same variables in two ways. For example

NetworkManager::serverReceivePackets( App& myApp )

{

serverIP = packet->getIP(); //1st way

myApp->myNetworkManager->serverIP = packet->getIP(); //2nd way. I haven't actually tried this, it's too ugly, I can't.

}

I don't know why but this looks really ugly to me and it stops me from coding, because I can't watch it. Is there other way of accessing private App vars from here?

If you still haven't died from stupidity, Question No.3:

I'm kind of finished with the basic networking and I want to add some singleplayer and some menus. But I'm trying to figure out the best structure for this.

The best thing I found on the internet is this:

First, I create a GameState class that has a bunch of virtual functions because I need to use polymorphism to switch between singleplayer mode, multiplayer mode, main menu and options menu. The class looks something like this:


#ifndef GAMESTATE_H
#define GAMESTATE_H


//Base class
class GameState
{
    public:
    virtual void handleEvents() = 0;
    virtual void updateLogic() = 0;
    virtual void renderScene() = 0;
    virtual ~GameState(){};
};

#endif

The idea is to use a pointer to GameState like this: GameState *currentState and this way I can create any state I want.

And in the 'while' loop I just write something like this:

currentState->handleEvents();

currentState->updateLogic();

currentState->renderScene();

Now imagine I'm in the main menu and I want to play singleplayer. and I click on "Story mode", the only code I write here is this:

currentState = new SinglePlayer();

No, my bad. This won't work.

The proper way to do it is this: currentState = new SinglePlayer( myResourceManager.GetSomeUglyModelsAsYouAlwaysNeedToDo() );

The main problem with this method is that I need to initialize some private members of App differently for every new state I create, which causes problems. This App class is ruining my health, why did I make it in the first place....

Is this the way normal people do this stuff, or is there something better?

Guys, sorry for the stupid questions, but they are kind of in my head for a very long time and there's always something I don't like, sometimes I'm too perfectionistic, other times I just skim through the details and then spend hours debugging just because of a single detail I've missed. That's why I wrote that novel. Thanks for reading. I would like to make a TL;DR version, but I can't. If someday I become a game programmer, then every single person in the world can become one.

Advertisement

A few things there that I won't comment to much on because I know there are better methods than what I use but I can't remember the specifics myself so I'll leave that to someone else. Having to access some single resource manager object from everywhere is quite a common problem and probably one of the main reasons people go for a global or a singleton to deal with this issue; because that's the simplest way to do so (though bringing with it it's own problems). Your method of passing it around is probably one of the most robust methods but it comes at the expense of having to pass it around which as you've discovered is a bit of a pain. Another method is to use a pseudo singleton, that is an object that is owned by something high level (like your App class) but can be accessed like a singleton would be with a static 'GetInstance' type method but It's lifetime is controlled by App. That comes with it's own issues.

I did see a really great post on here about splitting up your ResourceManager but I really can't recall where it was. In effect it was still just your resource manager but it was neatly broken down into assets, caches, factories and something else (maybe handles). I liked it and I would have replicated it if I'd had the chance. With that you are still stuck with having to pass things around. Using factories to create your objects (instead of creating them directly with new) helps to reduce the amount of plumbing going on to pass things around.

Question 2:

The approach of having one high level class that contains things is a fine idea. If it has two objects A and B where B relies on A then that is easily solved. I won't promise this will compile but hopefully it shows the idea. When you use references you sorta guarantee the lifetime of the objects that are relying on each other. I would definitely avoid what you are doing passing in a reference to App. Only pass in what it needs and if the two objects are that strongly coupled then pass it in as a reference during construction.


class A;
class B
{
   A& a; // make const, I forget the ordering so I've omitted it
   B(A& _a):a(_a){}
   void DoSomething()
   {
      a.Something();  
   }
};
class App
{
   A a;
   B b;
   App():b(a){}
};

Question 3

GameStates are a perfect way to deal with your problem here so stick with them. From what you have said it sounds like there's more of an issue with your App class doing more things than it necessarily should. Your states should work ok regardless of what the App state is up to within reason. Sounds like you solving your first problem will go a long way towards also helping solve your third problem. Pass the state the resource manager instead and let it decide what to do with it. That gives you more flexibility.

Hopefully someone will post a better solution to your first question as that seems to be the part that is limiting you right now.

Interested in Fractals? Check out my App, Fractal Scout, free on the Google Play store.

I did see a really great post on here about splitting up your ResourceManager but I really can't recall where it was

Probably this (or containing basically the same information):

http://www.gamedev.net/topic/682148-is-this-practical-for-a-resource-manager/

See haegarr's post especially.

Hello to all my stalkers.

omg, there is someone who actually read my whole post. Thanks A LOT for the comprehensive answer, Nanoha. ^_^

I thought there was something wrong with me because I'm always bothering with code-design questions, but now I see that it happens to other people, too.

You kind of threw me into another planet when you mentioned 'singleton', because I've never heard of that word, but then I searched the internet and it seems that there are a bunch of well-known design patters that I have got no idea of :http://gameprogrammingpatterns.com/contents.html (I'm gonna swallow this book)

From what you have said it sounds like there's more of an issue with your App class doing more things than it necessarily should. Your states should work ok regardless of what the App state is up to within reason. Sounds like you solving your first problem will go a long way towards also helping solve your third problem.

You were right. It was doing way more things than it should have. I transferred most of the private variables from class App to where they really belong and made s?me small architectural changes and now everything works very nice and I really like this new state-based polymorphic kind of stuff, because it's way more readable and it's pretty easy to add other features and it takes a lot less code and I don't have to pass App& to every function.

Nanoha, Lactose!, thanks again for the posts. :rolleyes: For the first time this year, I like my code. :cool:

I did see a really great post on here about splitting up your ResourceManager but I really can't recall where it was

Probably this (or containing basically the same information):

http://www.gamedev.net/topic/682148-is-this-practical-for-a-resource-manager/

See haegarr's post especially.

Totally awesome that my post got referenced!

Go Hawks!

I don't know if this would help but here is some of my code for my state manager. It works like a singleton.


// state.h
class State
{
	friend class StateManager ;

	public :

	State( ) ;
	virtual ~State( ) ;

	virtual bool Init( ) ;
	virtual void Shutdown( ) ;

	protected :

	virtual void onLoad( ) = 0 ;
	virtual void onTick( float time ) = 0 ;
	virtual void onDraw( ) = 0 ;
	virtual void onStop( ) = 0 ;
	virtual void onResume( ) = 0 ;
	virtual void onExit( ) = 0 ;
} ;


// statemanager.h
class StateManager : public State
{
	public :

	StateManager( ) ;
	virtual ~StateManager( ) ;

	virtual bool Init( ) ;
	virtual void Shutdown( ) ;

	// list management
	void LoadState( State* state ) ;
	void PushState( State* state ) ;
	void ExitState( ) ;
	
	// echo
	void Tick( float time ) ;
	void Stop( ) ;
	void Resume( ) ;
	void Draw( ) ;

	protected :

	virtual void onLoad( ) ;
	virtual void onTick( float time ) ;
	virtual void onDraw( ) ;
	virtual void onStop( ) ;
	virtual void onResume( ) ;
	virtual void onExit( ) ;

	std::list<State*> list ;
	bool isActive ;
} ;

extern StateManager* GetStateManager( ) ;


// statemanager.cpp
StateManager::StateManager( )
{
	isActive = false ;
}


StateManager::~StateManager( )
{
	Shutdown( ) ;
}


bool StateManager::Init( )
{
	list.clear( ) ;
	isActive = true ;

	return true ;
}


void StateManager::Shutdown( )
{
}


void StateManager::LoadState( State* state )
{
	for ( auto it = list.rbegin( ) ; it != list.rend( ) ; ++it ) {
		( *it )->onExit( ) ;
	}

	PushState( state ) ;
}


void StateManager::PushState( State* state )
{
	state->onLoad( ) ;
	list.push_front( state ) ;
}


void StateManager::ExitState( )
{
	if ( list.empty( ) == false ) {
		( *list.begin( ) )->onExit( ) ;
		list.pop_front( ) ;
	}
}


void StateManager::Tick( float time )
{
	onTick( time ) ; 
}


void StateManager::Stop( )
{
	onStop( ) ;
}


void StateManager::Resume( )
{
	onResume( ) ;
}


void StateManager::Draw( )
{
	onDraw( ) ;
}


void StateManager::onLoad( )
{
}


void StateManager::onTick( float time )
{
	if ( isActive == true ) {
		if ( list.empty( ) == false ) {
			( *list.begin( ) )->onTick( time ) ;
		}
	}
}


void StateManager::onStop( )
{
	if ( list.empty( ) == false ) {
		( *list.begin( ) )->onStop( ) ;
		isActive = false ;
	}
}


void StateManager::onResume( )
{
	if ( list.empty( ) == false ) {
		( *list.begin( ) )->onResume( ) ;
		isActive = true ;
	}
}


void StateManager::onExit( )
{
	if ( list.empty( ) == false ) {
		for ( auto it = list.rbegin( ) ; it != list.rend( ) ; ++it ) {
			( *it )->onExit( ) ;
		}

		list.clear( ) ;
	}
}


void StateManager::onDraw( )
{
	if ( list.empty( ) == false ) {
		for ( auto it = list.rbegin( ) ; it != list.rend( ) ; ++it ) {
			( *it )->onDraw( ) ;
		}
	}
}


StateManager* GetStateManager( )
{
	static StateManager manager ;
	return &manager ;
}

EDIT: I originally posted the older version, sorry.

Go Hawks!

m.r.e, I just use a changeState function that switches between all the states and it seems that it's enough for now. But thanks for the code. I will revisit it in some later stage of my development, because it looks too complicated for now. :)

m.r.e, I just use a changeState function that switches between all the states and it seems that it's enough for now. But thanks for the code. I will revisit it in some later stage of my development, because it looks too complicated for now. :)

Oh... sorry, I'm just trying to be helpful. For later, I'll explain it.

The state interface provides basic methods that are commonly used. Init() and Shutdown() are both used initialize private data the state may need: images, sounds, etc.. onLoad is used when the state is transitioned on-top the stack (i.e. added to the processing list). This is useful for when I need to reset a level or wave or something like that. onExit() is the counter for onLoad. When that state is removed from the stack, onExit() is called. onTick and onDraw are pretty straight forward, but I do iterate backwards over the list when drawing for correct drawing order. onStop and onResume are used to signal a state when it has lost focus. Basically, ALT+TAB away from application or another state was added(pushed) on top of it.

The state manager derived from state. I think this is best because I can make my GUI, levels, etc. derive from StateManager and while still treating it as a basic state. This allowed me to break up logic. For example, MainMenu is a state, Settings Menu is a state, Loading Level is a state, get it? LoadState() in StateManager is used when you want to clear the entire stack and only want the current state you specify on it. Its not really needed anymore because what I just explained about StateManager deriving from state. PushState and ExitState to exactly as the depict, push a state on the stack exit(pop) a state. The other methods allow access to protected "State" methods. They need to be there or you couldn't use the StateManager.

I declare a static instance of StateManager in the global function GetStateManager(). Once I call this function, I have a working StateManager. Its pretty simple. That's basically it. Nothing real special.

Go Hawks!

1. If you're passing the same ResourceManager to everything everywhere, then you may as well just make it a global. Singleton is just a fancy name for a global to try and make it sound less evil. You could even go C style and interact with it strictly via global functions rather than OOP style.

2. Don't pass App to the NetworkManager, pass whatever members of App that the function actually needs to interact with. Or in some cases it's better to use a pass-by-value approach, where the NetworkManager function takes some arguments and/or returns a value, and App handles the job of fetching the arguments from its members and feeding the return value to whatever member needs it.

3. I think the GameState thing is good as-is. It's ok if changing the App state involves more than just creating a new GameState object.

I'm wondering why everything should be kept in one class

It probably shouldn't. But even if you do want one big ResourceManager, I don't see why that's a problem. Passing things into functions is what programming is about. Don't try to avoid it.

if I want to add another function to my NetworkManager class and that function needs a variable that is stored as a private to the class 'App' that owns an object of the type NetworkManager

It's not clear what you're trying to do here - get data from the App to the NetworkManager, or get data from the NetworkManager to the App?

Either way, it's trivial, and you don't need to do what you're doing.

App to the NetworkManager - myNetworkManager.serverReceivePackets(myApp.GetWhateverVariable() );

NetworkManager to the App - myApp.setWhateverVariable(myNetworkManager.serverReceivePackets() );

currentState = new SinglePlayer( myResourceManager.GetSomeUglyModelsAsYouAlwaysNeedToDo() ); The main problem with this method is that I need to initialize some private members of App differently for every new state I create, which causes problems. This App class is ruining my health, why did I make it in the first place....

Given that the first line doesn't make any reference to your App object, you've not really explained what your problem is here. If you're changing state, the App should already exist. And nothing else should be changing its variables - it should change its own variables, based on results it gets from other functions.

This topic is closed to new replies.

Advertisement