Hello fellow gamedevs!
I'm currently working for about a year on a game in C++/SFML (i've worked with Lua and Java so far), however as the project grows larger and more complex i've began to question the way the basic class/info flow is handled, and i'd like to have some advice what, in your opinion, would be a good structure/good programming practice for my games class relations.
To narrow it a bit down, the actual game aspect of the game has one main "StateGame" class, which holds a gameloop method (executed every frame and recieving a deltatime float) aswell as pointers to other classes, such as a Player class, a Controls class (responsible for user input), etc
Below you can see both classes with only the for this discussion relevant attributes/methods
The StateGame class (of course very simplified)
class StateGame {
public:
std::shared_ptr<Player> player;
std::shared_ptr<Controls> controls;
void gameLoop(float dt) {
//handle controls every frame
this->controls->handleInput(dt);
};
}
aswell as a Controls class (also very simplified), which is supposed to handle the user Input
class Controls{
public:
void handleInput(float dt);
}
The content of the player class isn't really important for this topic.
My question now is : What would be the best programming practice to let the controls class have access to the player class (to move it around, for example. I'd like to have the controls and the player object to be seperate classes).
So far i've figured out three possibilities:
A) Give controls class a direct pointer to the player class
class Controls{
public:
std::shared_ptr<Player> player;
void handleInput(float dt);
}
This would give the Controls class direct access to the Player class via
this->player
(fast performance, good), however at the same time it would create a seperate pointer to the player class (bad) instead of the StateGame class being soley responsible for it, and considering that other aspects also need to know info from the player class aswell (GUI for example, or enemies) this practice would require other classes to have direct pointers to the Player class aswell, it would result in the Player class being coupled with many other classes (bad). It would get even worse with other classes having pointers to eachother (all subclasses pointing to eachother, rather than letting the StateGame handle its children).
This is the method i've been using so far (first game in C++, possible overuse of pointers. It's such a luxury that you can just have everything point to eachother to have direct access.... but as i've written above, i fear that it could become too chaotic )
B) Give controls class indirect acces by "jumping" through StateGame class
Another possibility would be to give Controls a weak pointer to the StateGame class
class Controls{
public:
std::weak_ptr<StateGame> game;
void handleInput(float dt);
}
and then access the player class via
this->game->player
which would result in no direct pointer between the Player and the Controls class and other classes (good), but it would require an extra detour jump through the StateGame class, which is of course slower than a direct access via member (not to mention that i'd have to .lock() the weak_ptr first).
C) Let StateGame pass a shared_ptr ( or const reference ) of the Player class to the Controls clas
This possibility would be to not let the Controls class have member pointers to any of those classes (neither Player nor StateGame) but rather let the StateGame class pass a shared_ptr (or const reference) everytime the Controls class is supposed to do something with the Player class (aka the StateGame class is responsible for delivering needed info to the member classes/subclasses)
class StateGame{
public:
std::shared_ptr<Player> player;
std::shared_ptr<Controls> controls;
void gameLoop(float dt) {
//handle controls every frame
this->controls->handleInput(dt, this->player);
};
}
and then let Controls accept a shared_ptr as additional parameter in the handleInput() method
class Controls{
public:
std::weak_ptr<StateGame> game;
void handleInput(float dt, std::shared_ptr<Player> player);
}
which would result in the handieInput()-method having direct access to the player class by the pointer given by the parent StateGame class, the parent class StateGame having complete control over what it's child member classes get and it would eliminate the need of member pointers to the StateGame or Player class in the Controls class (good), however it would require the StateGame class to pass a shared_ptr (or a const reference to it) every frame and i'm not sure how much impact this has on the performance, compared to the other possibilities, or if it actually is better programming practice.
Basically the question is : Is it a better mix of practice and performance to have
a) classes have direct pointers to eachother, regardless where they are in the hierarchy and regardless how chaotic the endresult is (dozens of child classes pointing to eachother) or
b) member classes access other classes by taking a detour and jumping through their "superior class" (in this case : StateGame) or
c) should instead the "parent" class (StateGame) distribute the info (in this case : pointer to player) to its member classes (in this case Controls)?
a) and b) would mean that each frame the game would loop trough all member classes, which then request player info from their parent class (StateGame)
c) would mean that each frame the StateGame class would loop through all member classes and the parent would distribute all the info the children need
Which of these methods would you consider the best practice? Is there any better method of handling this?
Maybe the performance differences aren't that great, however if i program something i'd like to do it the best way, both performance-wise and in terms of programming practice, and i don't have any problem rewriting what i've written so far.
Thank you very much!