How to structure a game in OOP

Started by
19 comments, last by L. Spiro 10 years, 10 months ago

  • For instance, there is no reason for a sound object to contain a pointer to a physics object. Keep your systems separate and join them using a higher level interface.

Well, this is where it gets tricky for me. The APIs for sound and graphics are different systems. I can encapsulate them in my own classes and simplify the interfaces for my use. But for example there has to be a place in the code where a sound is triggered when the animation passes a point. We are now far down a method in the animation class and we discover that we need to trigger a sound. Where is the pointer to the sound interface when we are in the animation system?

Globals?
Singletons?
Coupling by having a pointer to the sound system as a member in the animation class?

Having a higher level interface that calls the DoAnimation() method, I need return values in order to call DoSound() afterwards. But I just shuffle the burden up the food chain. Now I have to code a ton of different states that must be passed and returned and bloat the calling code, making it more complex.

The way I handle this is with callbacks. For instance, let's take a Rock object as an example. A Rock object falls to the ground, and when it hits the ground, it should trigger a "thump" sound.


typedef std::function<void()> DelegateFunction; 

class Rock {  
  Sound mSound;  
  Animation mFallAnimation;    
  Rock()  
  {     
    DelegateFunction delegate = std::bind(&Rock::FallAnimationEnded, *this);     
    mFallAninmation.addDelegate(delegate);     
    mFallAnimation.Start();  
  }  

  void FallAnimationEnded()  
  {     
    mSound.Play();  
  }
};

class Animation
{   
  std::vector<DelegateFunction> mDelegates;  
  void AddDelegate(DelegateFunction func) { mDelegates.push_back(func); }  

  void Start(); // Begins a timer, or adds it to an animation manager, however you want to do this.  

  void Update() // Called every animation update  
  {    
    if(animationStillRunning) 
    {      
      this->UpdateAnimationStuff();    
    } else {     
      for(int i = 0; i < mDelegates.size(); i++) {         
        mDelegates();      
      }  
    }
  }
};

So as you can see, we have no coupling between our Sound system or Animation system. The Rock is created, it will attach its FallAnimationEnded member function to the animation's delegate list, and when the animation is done, it will call all registered delegates. Obviously this code is far from complete and I'm not even sure if all my syntax is correct, but the idea should be clear.

Advertisement

Gonna bump this, I would like some feedback on a structure I am working on.


int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
	Dx9Device DeviceInterface;							//Wrapper Interfaces to hardware APIs
	Dx9Audio AudioInterface;
	Network NetworkInterface;
	WinInput InputInterface;

/*
Some code
*/

	Assets AssetsManager;								//Game managers
	Entities EntitiesManager;
	Area AreaManager;
	Audio AudioManager;

	MainLoop(&DeviceInterface, &AudioInterface, &NetworkInterface, &InputInterface, &AssetsManager, &EntitiesManager, &AreaManager, &AudioManager);

	return 0;
}


void MainLoop(Dx9Device* pDeviceInterface, Dx9Audio* pAudioInterface, Network* pNetworkInterface, WinInput* pInputInterface, Assets* pAssetsManager, Entities* pEntitiesManager, Area* pAreaManager, Audio* pAudioManager)
{
	ToLogicData Data;
	GameData Gamestate;

	while(TickMessages())
	{

		GameInput(pNetworkInterface, pInputInterface, &Data);													//Get Input and output pData
		GameLogic(pAudioManager, pAreaManager, pEntitiesManager, pAssetsManager, &Data, &Gamestate);			//Get pData, update and output game state
		GameOutput(pDeviceInterface, pAudioInterface, pNetworkInterface, &Gamestate);							//Render game state, play audio from state and networking
	}
}

Is this a good way for passing interfaces and data around?

Whenever you need to pass a ton of parameters it is better to make a structure with those parameters as its members and pass a reference to that structure.

L. Spiro

I restore Nintendo 64 video-game OST’s into HD! https://www.youtube.com/channel/UCCtX_wedtZ5BoyQBXEhnVZw/playlists?view=1&sort=lad&flow=grid


Sorry that has got to be the absolute worst reason to rewrite your code


Totally agree 100%. The only reason you should rewrite this stuff is if you find it stops you from extending your game or is inflexible in some way. Refactoring just to avoid other peoples opinions of what is bad software engineering is just a time suck when what you should be concentrating on is getting things done.

Sure some people don't like statics, globels or singletons but that doesn't mean they should be avoided entirely. Each has its use cases.


Let's back up. If he's already finished writing the game and wants to go back and refactor it, then I don't see the harm or the waste of time. Now, if he's hasn't finished writing the game yet, then yes it's more important to finish writing the game then worry about good software engineering. You'll get that with experience. But I wouldn't call the effort a complete waste of time.

Beginner in Game Development?  Read here. And read here.

 

Whenever you need to pass a ton of parameters it is better to make a structure with those parameters as its members and pass a reference to that structure.

L. Spiro

Another option for this particular situation is the Service Locator Pattern (http://en.wikipedia.org/wiki/Service_locator_pattern), though there are some who consider this an anti-pattern.

Whenever you need to pass a ton of parameters it is better to make a structure with those parameters as its members and pass a reference to that structure.

L. Spiro

Why not pass by address, that way you can pass a NULL?

Whenever you need to pass a ton of parameters it is better to make a structure with those parameters as its members and pass a reference to that structure.

L. Spiro

Why not pass by address, that way you can pass a NULL?

Well if you pass the structure by reference, then you only have to pass one NULL, as opposed to 10 or 15.

Beginner in Game Development?  Read here. And read here.

 

I ment if I place everything in a single structure, why pass that structure as reference instead of address.

Because the reference to the struct has replaced a load of required parameters, so not passing one would be pointless...

"Most people think, great God will come from the sky, take away everything, and make everybody feel high" - Bob Marley

Really, do the simplest thing that could possibly work.

I tend to end up with some big hairy object called "GameManager" or something, which I create as a local in main() or somewhere fairly close, pass by reference between a small number of high-level functions, which then call the more specific things.

This "GameManager" tends to be a composition of a number of more specific managers which handle specific aspects.

I often seem to have some "ObjectManager" or something which contains lists of various objects which it manages, and deals with calling the tick functions, rendering functions (possibly passing a renderer object by reference).

This topic is closed to new replies.

Advertisement