0
\$\begingroup\$

I'm trying to build a basic platforming game in C++ using SDL, and I've encountered this issue enough to know that there's something I'm missing.

What I'm working with, at the most basic level, is two classes, a GameState class that manages everything, and a Player class, that will move around.

The main GameState object will have a list of obstacles that the Player cannot move into, so I want my Player move() function to take a GameState* in order to move based on various components of it.

However, I'm hitting a problem I've seen a bunch of times -- a circular dependency issue. The problem is that the GameState object needs to have a Player member variable to update as the game runs (so I need to import the Player header to it), but I need to import the GameState header into the Player header in order to have my move function take a GameState* as a parameter.

This doesn't work. Normally I'd just settle and have the Player's move() function just take a list of obstacles, but I'm kind of hitting this issue all over the place and there are multiple instances where I want to access other variables stored in the GameState. Is this kind of behavior even possible? I've been racking my brain for a solution but I don't really know what the best way to fix this is, so I thought I'd ask.

Thanks in advance!

Edit: a simple example in code of what I'm talking about:

//in GameState.h
class GameState{
public:
    // ...
    Player* plr;
};

//in Player.h
class Player{
public:
    // ...
    move(GameState* state);
};
\$\endgroup\$
2
\$\begingroup\$

Have the player contain the data like position and a state block, and then have the game state move the player directly using that data. For any kind of state-driven, single player game this is more than sufficient.

In this scenario the game state should really be called "character controller" because it uses states internally but what it's doing is controlling the character based on user input.

This fixes your problem because the player knows nothing except that it holds some data blocks and the controller knows everything and your circular dependencies disappear.

This also works for enemy NPCs, except their different controllers don't take user input and rely on separate game states and paths or scripts to tell them where to move to.

Assuming that the player character is really just an NPC under the hood you can then pull tricks like taking over an NPC, having the player character controlled in a cinematic playback, etc... all by just changing controllers. You could even have a synchronized army of NPCs working under one controller.

| improve this answer | |
\$\endgroup\$
  • \$\begingroup\$ Thanks for the answer! One question though; I'd have a list of obstacles in my character controller to keep the player bound. Would I need to copy the same list of obstacles over to an enemy controller? \$\endgroup\$ – Benjamin Kovach Aug 17 '13 at 22:17
  • \$\begingroup\$ You have a World which has all the global stuff like obstacles. The controllers live and breathe in that world and can ask it questions like "can I move from here to there and not hit anything?" It gets more intricate, but that's the basics of this simple organization. \$\endgroup\$ – Patrick Hughes Aug 17 '13 at 22:35
  • \$\begingroup\$ How does the controller know if it can move without direct access to the obstacles? I guess that's what I'm confused about -- if I have a move() function in my Player class that needs to query the obstacles list, how do I get that? Just pass them from the World class to the character controller? Is there a place I can read up on this type of design? It might help to see a concrete example, etc. \$\endgroup\$ – Benjamin Kovach Aug 17 '13 at 22:54
  • 1
    \$\begingroup\$ The world (aka level) knows all about global things like obstacles, rules, triggers. The controllers know about the world and are the brains for NPCs. To move an NPC (or player) the controller gets the position from the NPC and asks the world "what happens if I move from position to newposition?" The controller then updates the NPC position and its own state if it needs to. Not sure what to recommend reading beyond en.wikipedia.org/wiki/Design_Patterns because programs are all different, and here this solved your immediate problem I hope, without too much complication. \$\endgroup\$ – Patrick Hughes Aug 17 '13 at 23:27
  • \$\begingroup\$ I think I understand now. I was a little bit confused because I was still under the impression that the World housed the Player/NPCs as well. This makes more sense -- thank you for your help! \$\endgroup\$ – Benjamin Kovach Aug 17 '13 at 23:37
2
\$\begingroup\$

Obviously, you can just rethink your design to avoid this error in this case, but that won't always be possible.

To get around circular dependency you can simply declare a class inside the other, without #include-ing. E.g.

//in GameState.h

#include "Player.h"
class GameState{
public:
    // ...
    Player* plr;
};

//in Player.h
class GameState;       // Note we declare GameState here, but will not 
                       // have a definition until we link.
class Player{
public:
    // ...
    move(GameState* state);
};

This means we cannot see methods or members of the "GameState" class, but you will be able to have GameState* and GameState&. I don't believe ( correct me if I'm wrong ) you will be able to put a GameState object on the stack. Now, you must #include "GameState.h" in "Player.cpp", and then you can use the GameState object as you like in that file.

| improve this answer | |
\$\endgroup\$
  • \$\begingroup\$ Thanks for the answer. I did consider this, but then I'd have to include everything that needed the GameState in the same file in order for everything to work right, which would involve including the Enemy class, which is just too much. \$\endgroup\$ – Benjamin Kovach Aug 19 '13 at 2:01

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.