Sign in to follow this  
Horscht

Alternatives to global variables / passing down references through deep "call-trees"?

Recommended Posts

When I started programming one of the biggest mysteries to me was how to provide access to things like SoundPlayer, Renderer, ErrorLog, StateMachine etc to very deeply "nested" objects, like the player.

Let's say I create my StateMachine in main(), inside the constructor of StateMachine I initialize it's member: GameState, which in turn initializes it's member Level which in turn initializes it's member Player.

Player wants to play a sound when he jumps, so he would need to call soundPlayer.playSound(SOUND_JUMP). But Player doesn't have a reference to a SoundPlayer object. He also wants to draw himself at the center of the screen, so he needs a reference to a Renderer and the Window, to get it's dimensions. What do?

After a lot of inconclusive articles (or failure to understand them), I decided to just use a static global class, since I wanted to generate results as fast as possible and I kinda got stuck with it.

Actually, I used a mixture of dependency injection through constructors, dependecy injection through methods and global variables...

Here is an example (not actual code):

class Player
{
    private:
        SoundPlayer& _soundPlayer;
        Sprite _sprite;
    public:
    Player(SoundPlayer& soundPlayer) :_soundPlayer(soundPlayer) {} // Dependency injection, storing the reference inside the object
    void Render()
    {
        Renderer* renderer = Renderer::GetInstance(); // Global static horrible singleton!

        renderer->Render(_sprite);
    }
    void Shoot(ParticleSpawner& particleSpawner) // Dependency injection through function argument, not stored
    {
        particleSpawner.Spawn(PARTICLE_BULLET);
    }
};

What I really don't like, are the static GetInstance() function calls everywhere. It just isn't clear that this class depends on the other, it just hides somewhere inside of a random method. At least in the constructor it's clear, you can't even create an instance if you don't pass it's dependency in.

Inside the methods parameters it's already a little more awkward, because you're happy you could create an instance of the object, but then it suddenly needs an instance of another class.

 

The worst part is when I have a deep "component hierarchy" or whatever it's called. I read the term "call-tree" before and think it fits quite well.

Player needs a reference to an instance of SoundPlayer, otherwise it can't be created, so it's owner ALSO needs that reference to pass it in, but it itself DOESN'T need it, except for creating the player, so it also puts it into its constructor as a required parameter, so now THIS class ALSO needs a reference to SoundPlayer, even though it has no intention of ever using it, but one of its components needs it, to in turn construct one of its components, which in turn needs it to construct one of its components, which in turn... feels kind of wrong to me :|

 

I got so tired of typing it all out that My state machine just has a #define to pass it all in.

Kind of like this:

class StateMachine
{
    private:
        Renderer& _renderer;
        SoundPlayer& _soundPlayer;
        ErrorLog& _errorLog;
        FileSystem& _fileSystem;
        NetworkSystem& _networkSystem;
        InputDevice& _inputDevice;
        BreadMakingSystem& _breadMakingSystem;
        SystemsManager& _systemsManager;
        // ...
        State* _gameState;
    public:
        StateMachine(Renderer& renderer, SoundPlayer& soundPlayer, ErrorLog& errorLog, FileSystem& fileSystem, NetworkSystem& networkSystem, InputDevice& inputDevice, BreadMakingSystem& breadMakingSystem, SystemsManager& systemsManager):
            _renderer(renderer),
            _soundPlayer(soundPlayer),
            _errorLog(errorLog),
            _fileSystem(fileSystem),
            _networkSystem(networkSystem),
            _inputDevice(inputDevice),
            _breadMakingSystem(breadMakingSystem),
            _systemsManager(systemsManager)
        {
        }
        enum StateType
        {
            STATE_GAME,
            STATE_INTRO,
            STATE_TITLE,
            STATE_OPTIONS
        };
        void SwitchState(StateType state)
        {
#define STUFF_TO_PASS renderer, soundPlayer, errorLog, fileSystem, networkSystem, inputDevice, breadMakingSystem, systemsManager
            switch (state)
            {
                case STATE_GAME:
                    _state = new GameState(STUFF_TO_PASS);
                    break;
                case STATE_INTRO:
                    _state = new IntroState(STUFF_TO_PASS);
                    break;
                case STATE_TITLE:
                    _state = new TitleState(STUFF_TO_PASS);
                    break;
                case STATE_OPTIONS:
                    _state = new OptionsState(STUFF_TO_PASS);
                    break;
            }
        }
};

And inside of the GameStates constructor, again a #define to keep passing it on to it's children.

 

I was asking myself what the best way to do all of this would be, while refactoring Renderer::GetInstance() out and instead rewriting 100 constructors to accept a reference to Renderer instead. I just thought "What the hell am I doing? Is this really better?!"

 

Before I go and rewrite everything I want to make sure I do it correctly this time.

This is something that really irritates me about programming, there never seems to be a definitive correct way and I'm only satisfied with perfection, even though I'm nowhere good enough to even achieve anything close to it :P

Should I just leave it like this or is there a better way?

Share this post


Link to post
Share on other sites
I would say it depend on your goals. If you want to get a game done, leave it like this. If you want to learn new techniques you might want to look in to messaging systems (someone else probably going to give more detailed advice on that). Or in some way do it the other way around, it's not the player that wants to play a sound. It's the 'soundmanager' that wants to play a sound when someones jumping.
 
That said, since i stopped worrying about things that didn't 'feel right' i actually started finishing projects :) I use globals for almost all subsystems as long as they dont carry state (and even then sometimes). I've had no code-reuse problems, i reuse my globals class and do a bit of copy pasteing :) If i actually find something to be a problem, i try to fix it then, or if the game's almost complete i leave it be and fix it in my next project instead.
 
 

Share this post


Link to post
Share on other sites
As MartinMM said, if you want to make a game, make a game. If it's not broken, don't fix it. You'll waste a lot of valuable time trying to perfect these things (I know this personally.)

That being said, your thought process seems backwards to me. A player wouldn't tell the audio system to play a sound, the engine should do that when it tells the player to jump.

Digressions aside, store your subsystems in a variable and pass that instead of passing each one separately.

Share this post


Link to post
Share on other sites

That being said, your thought process seems backwards to me. A player wouldn't tell the audio system to play a sound, the engine should do that when it tells the player to jump.

I agree and I have a hard time thinking outside the box.

My current approach is to have all the logic related to the player inside one class: Player.

In Player::Update() I check if the jump button has been pressed, using a reference to InputDevice that got passed into the constructor and saved as a member, then play the sound the same way, but instead with a SoundPlayer. If I had more things for whoknowswhat I would also put it there smile.png

I don't really know how to really do it from "outside" since I would have to expose a million different things like position, velocity, state etc (which would be the hardest since the Player class itself uses kind of a state machine pattern instead of a variable).

 

There is an in-between zone between the extremes.

It goes by various names, including "service finder" and "well known objects".

Reminds me of this one:

http://gameprogrammingpatterns.com/service-locator.html

But I don't really see how it is much different from a static singleton. It just goes through one more class to actually fetch the service. The only upside I see is, it can dynamically return different derived classes. (Like the NullSound example).

 

Now if enough sub-systems are available, the problem of disorganization appears again. This can be lowered by using a layered architecture. Sub-systems collaborate with sub-systems in the same layer, they utilize sub-systems of the next lower layer, but they MUST NOT call sub-systems of upper layers (although they MAY respond to them), and they SHOULD NOT utilize sub-systems from the layer after next. Of course, in reality this is more a rule thumb than a strict law.

Let's make sure we are talking about the same things here, could you explain what exactly you mean by layers and levels, up and down?

The way I see it is, a layer is a scope, like the function body of main() or the scope of a class like GameState. Up is main() and down is something like statemanager.gamestate.level.player, right?

So if the Player wants to be drawn, it should not interact with the Renderer but instead the Renderer should "collect" all Renderables "below" it, or the class that owns Renderer should feed the renderer the Sprites/Renderables?

So something like:

void GameState::Render()
{
    _renderer.Render(_level.GetPlayer().GetSprite());
    _renderer.Render(_level.GetEnemy().GetSprite());
    _renderer.Render(_level.GetPickup().GetSprite());
}

Of course that would need to be made more general, maybe through an Interface with a recursive function like GetAllRenderables() or whatever :)

I don't know it's just an idea.

The way I do it is kind of the opposite, I call stateManager.Render() which calls gameState.Render() which calls level.Render() which calls player.Render() which then gets a pointer to the DirectX device directly and does all its drawing directly in low level code hahaha :) But once I wanted different graphic-layers, for UI and debug output, it became clear that this is not a very good way to do it.

Share this post


Link to post
Share on other sites
I think you might be violating the principle of a class having 1 sole purpose.
Some food for thought, how about:

Class player
Class input
Class scene/ level or world
Class audio

Class game, with update/ render/ init or something

Now, game::update
{
if(player does something and scene something is state abc) audio.playfx(id)
Etc.

That way your game class (what you might call main), doesn't have to go to deeper trees.
Depending on the size of the game you might get a big game class, but there you can create helper/ subclasses probably.

Share this post


Link to post
Share on other sites

But I don't really see how it is much different from a static singleton. It just goes through one more class to actually fetch the service.

It makes all the difference in the world.

 

A static singleton is initialized at a time that is difficult to control. It may be initialized on first use, which may even happen before main() is called.  A static singleton is destroyed when static objects are destroyed, after main() has already completed. A singleton class has a strict rule that there can only be one of them, ever, and it is not permitted to ever create another instance.

 

 

Under the service locator pattern there is a global pointer, and you ensure through policy that the pointer is valid between certain times.  It is initialized at the time you specify, and it can be cleaned up at the time you specify.  While that well-known pointer only has a single instance, and that single instance has pointers to other objects, nothing prevents you from having other instances of the objects, you can have any number of them created for any other purpose.

 

Thus you can establish by policy a well-known set of interfaces, and with policy you guarantee to the game objects that the interfaces are valid when you call their Update() function. But also through policy you make it known that the pointers can change at any other time, and that the values may be different in between successive calls to Update(). They are valid for a limited time only. You can request a pointer to the current audio subsystem and tell it to play an audio clip, but by policy you are not to modify the pointer to the audio subsystem, nor are you are allowed to store the pointer, it is treated as invalid once the Update() function is complete.

 

 

Thus, a system of ::simulator->audio->play() is very different from ::audio.play(), because lifetimes are controlled, access is controlled, and the interfaces can be replaced as needed with different objects, including mock objects and test objects, and they can be swapped out if needed during execution.  

 

It still suffers from the problem of a hidden dependency (you didn't explicitly pass the bundle it belongs to) but since the whole purpose was to introduce a hidden dependency so you don't need to pass the parameters everywhere, that's a known concern that can be accepted and understood.  You could resolve that by passing the bundle pointer through the objects, but that's a trade off.  Adding a parameter everywhere has a tiny cost of passing the parameter but it gets multiplied thousands or even millions of times per second. Thus you take a penalty in the form of needing to know and understand the policy of the hidden dependency, done in order to save the cost of thousands or millions of parameters per second.  

Share this post


Link to post
Share on other sites

You only have a few options to get access to your "global objects"

 

- Global static - Ugly ugly

- Some resolution system - Not much different to global, easier to switch object for others BUT still just global state.

- Pass the references into objects as they are constructed so they are available as member variables. 

- Pass the references into function calls that need them

 

I believe the last option is actually always the best solution. No hidden crap, the function signature is honest to its requirements and gives you better understanding as to what it does and makes it easier to make the function pure.

 

There are all sorts of tricks you can use to thread the values down through a call stack, my personal preference being monads (such as reader/state monads) or passing in functions that carry the required state via closures etc (C++ gained closures in C++ 11 I believe when lambdas came to the language)

 

If you are having to pass this data down too many levels your code structure is probably wrong :)

Share this post


Link to post
Share on other sites
I just embrace parameter passing and stop fighting it. I like it. It forces me to think carefully about dependencies at every point.

If you have a bunch of objects that are always passed around together, there is nothing wrong with wrapping them up in a container object and passing that around, but if you then call a method that only needs access to one of them, you can just pass that one rather than the container.
 
class RenderContext
{
    GraphicsDevce graphics;
    TextureCache textures;
};

void render(RenderContext &context)
{
    Texture t = findTexture(context.textures);
}

Texture findTexture(TextureCache &textures)
{
   // ***
}
As to the "deep" issue, I generally find the deeper I go, the less I need access to, which dovetails very nicely with this kind of approach.

Even if you never write a unit test, it is often useful to think "How would I test this method in isolation? How much set up outside the method would be required to test it meaningfully?" If it is a massive amount of global state that requires being set up before the method can be tested, something is wrong.

void test_findTexture()
{
    TextureCache dc;
    Texture t = findTexture(dc);
}
Edited by Aardvajk

Share this post


Link to post
Share on other sites

I usually have some soft of "core" object which contains the following:

 

struct Core
{
  Audio audio; // Handles the sounds
  Shader shader; // Handles the shaders
  Environment environment; // Handles external stuff like paths / filesystem
  Keyboard keyboard; // handles keyboard input etc...
  // Etc..
};

 

Then when I add a new node to the scene, I pass a pointer to Core to it but then when I add a new node to that node, the add() function sets the core pointer within the new node to that of the parent node. So in effect, every single object in the game has a pointer to Core. So then I can do this...

 

void PlayerNode::onUpdate()
{
  // ...
  if(health <= 0)
  {
    core->getAudio()->play(deathSound);
    remove();
  }
  // ...
}

Share this post


Link to post
Share on other sites

I usually have some soft of "core" object which contains the following:
 
 

struct Core
{
  Audio audio; // Handles the sounds
  Shader shader; // Handles the shaders
  Environment environment; // Handles external stuff like paths / filesystem
  Keyboard keyboard; // handles keyboard input etc...
  // Etc..
};
 
Then when I add a new node to the scene, I pass a pointer to Core to it but then when I add a new node to that node, the add() function sets the core pointer within the new node to that of the parent node. So in effect, every single object in the game has a pointer to Core. So then I can do this...
 
 
void PlayerNode::onUpdate()
{
  // ...
  if(health <= 0)
  {
    core->getAudio()->play(deathSound);
    remove();
  }
  // ...
}


In terms of access control, how is this different from just having globals exactly?

Share this post


Link to post
Share on other sites

In terms of access control, how is this different from just having globals exactly?

 

Because the individual class instances that need a Core member hold pointers to it, and the classes that don't need access don't have pointers to it.

 

I do something similar (passing in a 'collection' struct holding the major resource systems). The only downside is if a class needs only 'Audio' and 'Textures' access, he also gets 'Maps' and 'Enemies' access; so I'm trading some unnecessary (but explicit) access for convenience, without making that access implicit and available to everything.

 

Logging and debugging facilities are global, though I've considered moving them to a Service Locator-type design so certain areas of logging can be enabled to give more details only when needed (but when disabled gives less verbose reporting of every tiny little thing, so I'm not flooded with info). Haven't done that yet though.

Share this post


Link to post
Share on other sites

This is the exact OOP conundrum that burned me out. :/ I was trying to convert a tile engine written in C from the old OpenGL fixed function pipeline to GL 3.3 with shaders, while converting it to C++ and ran into this wall. I wanted each tile to be able to have its own shaders, textures and mesh, but realized that most tiles would use the same shader, texture and mesh. I could not figure out how to create a pool of shared resources without having deeply nested call trees and that was a nightmare in and of itself! I needed a pool of textures, one of meshes and one of shaders that were shared by the tile map. I must have gone through every online reference I could find and couldn't figure it out!

 

I ended up quitting programming (a hobby). I am really hopeful that this thread can shed some light on this!

Share this post


Link to post
Share on other sites

Thus you can establish by policy a well-known set of interfaces, and with policy you guarantee to the game objects that the interfaces are valid when you call their Update() function. But also through policy you make it known that the pointers can change at any other time, and that the values may be different in between successive calls to Update(). They are valid for a limited time only. You can request a pointer to the current audio subsystem and tell it to play an audio clip, but by policy you are not to modify the pointer to the audio subsystem, nor are you are allowed to store the pointer, it is treated as invalid once the Update() function is complete.

So I should call ServiceLocator::GetRenderer() every update/render, instead of storing it once in the constructor? Isn't that kind of inefficient?
But other than that it sounds interesting, but also like a lot of extra work and complexity.
Implementing NullSound, NullRender etc, and designing the system around being able to run in a "null" state.
I can imagine it becoming kind of complicated when functions return something, like a null version of TextureCache::LoadTexture(), it would have to return a NullTexture, or some kind of default texture... hmm...
 
Whatever, I think I will probably give ServiceLocator a try. I have to rewrite the whole render code anyway, since I did not think ahead.


@haegarr:
How would I go about checking the state of Player inside of PlayerController?
Provide accessors for every member, like GetPosition() etc, or declare PlayerController a friend class, or something else?
Should I also seperate logic for sound playing etc into something like PlayerSoundController, or put it all into one PlayerController?
Some kind of messaging system sounds like it would make things easier, but I'm not sure if I can come up with a good one.
I coded in different scripting languages for different games (like coding Addons for World of Warcraft) and there it was always soooo easy smile.png
Register for an event, through a global variable and then just pass a function to play a sound, like event.Register(PLAYER_TAKE_DAMAGE, playMySound).
Boom, done!
I really need to learn more about decoupling and abstraction... that's one of my weaknesses, everything in my code is so "direct".

Share this post


Link to post
Share on other sites


How would I go about checking the state of Player inside of PlayerController?
Provide accessors for every member, like GetPosition() etc, or declare PlayerController a friend class, or something else?

Single Responsibility Principle.

 

For example, why does Player have a getPosition() method? That sounds more like the job of a Transform or a MoveableObject class.

 

Register for an event, through a global variable and then just pass a function to play a sound, like event.Register(PLAYER_TAKE_DAMAGE, playMySound).

You can do this equally well in C++, using a signals/slots mechanism. Take a look at boost::signals2, or GameDev's own VVFEL.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this