3
\$\begingroup\$

I have a Screen class that encapsulates the updating and rendering of several distinct states, such as the menu, gameplay, and credits. A stack of screens exists on which the screens themselves need to be able to push new, and pop screens (imagine selecting "Start game" from the main menu, pushing the gameplay screen and popping the main menu screen).

I could have a ScreenManager class to babysit my screens but this would basically just be a wrapper around a list. I am therefore considering to pass each screen the std::list<Screen*> _screens and have them push and remove as they see fit. What I am worried about is overhead of copying this list around. If I recall correctly, since the list contains pointers to screens, the only thing I would be copying are the list of pointers, correct? And not the actual screens themselves.

Would this be an advisable way to go, or would you recommend creating a "wrapper class", such as a ScreenManager in any case?

\$\endgroup\$
1

3 Answers 3

6
\$\begingroup\$

If you are copying that list around (i.e. passing in the list by value) then the overhead of copying a list of pointers is the least of your concerns - it probably won't even work as you expect. Each Screen will have its own separate list, and modifications won't propagate to other screens. You would need to share the same list between all screens. There are two simple ways to solve this:

  • You could make the list static inside the Screen class so that only one instance will be created and shared by all Screens.

  • Or you could pass the list itself as a reference or a pointer so that all Screen's have access to the same list and no copying is performed. I.e:

// Inside the Screen class you would store the screen as a...
// Pointer
std::list<Screen*>* _screens;
// or
// Reference, must assign in constructor/initialization list
std::list<Screen*>& _screens;

Careful with scope too. You must keep the list alive throughout the duration of the application.

But still, I'd really recommend creating a ScreenManager class and I see no benefit from doing the list handling like this. Even if it's just a simple wrapper, it's going to be easier to understand (and extend if needed).

And by the way, a ScreenManager class is not necessarily just a wrapper around a list. There can be many different features added to it. For example, my ScreenManager is also responsible for:

  • Keeping a dictionary/map registry of screens and their ids which allows me to do everything by id: screenManager.PushScreen("PauseScreen")
  • Handling "smooth" transitions between screens, such as fades or slides. Basically, each screen has a fade out and fade in time and a float fade value between 0 and 1. The screen manager takes care of handling these transitions and interpolating the fade value. The screen decides what to do with this value (i.e. darken the screen, offset sprites, etc).

And I've seen screen managers supporting other operations such as:

  • Add delays between switch/push/pop screen operations.
  • Popping several screens at once.

I think it's neat to have all of this encapsulated in its own class, rather than polluting the Screen class. So, it depends a bit on what you need, but I still think creating a simple ScreenManager class would be better than passing that list around.

\$\endgroup\$
1
  • 1
    \$\begingroup\$ Thanks for the insightful answers everyone! I had indeed not considered it might not even work as expected and decided to drop simply passing the list, opting for a ScreenManager. Perhaps I have been developing an unhealthy skepticism towards any class with "Manager" in it even though the answers have demonstrated it is a better solution in this case! \$\endgroup\$
    – Laurens
    Dec 27, 2011 at 18:29
2
\$\begingroup\$

Since pointers act only as references to addresses you would only be copying those addresses and not the screens themselves.

Personally, I prefer to have a ScreenManager merely because the manager can be used for more than just holding the screens. For example, what if I wanted to find all of the Enemies being drawn to a given Screen? Rather than do some fancy checking inline, I could just create a function which returns all of the objects of a certain type from some Screen inside of the ScreenManager. In the end, the choice between a simple list and a manager depends on how complicated of a game you're making.

\$\endgroup\$
2
\$\begingroup\$

As my previous speaker noted, I'd also prefer the ScreenManager of the same reasons.

If you really want to follow through with adding the list to your Screen class, I'd use a pointer to the list since there should be only one instance of it (like singleton pattern, which is another argument for a ScreenManager class)

Concurrency is another issue you should be aware of when building this logic into Screen class. E.g. the ScreenManager of my engine loops over all Screens and calls the Update method of every Screen. It is possible that a Screen pops itself off the stack in Update which breaks the iterator logic in the ScreenManager class. Pretty easy to fix if there is just one place to look at...

\$\endgroup\$
2
  • \$\begingroup\$ Allow me to toss in a tip here. As for the problem you mention in your last paragraph, this is another problem that is easy to address with a ScreenManager. When one of my Screens issues a Push or Pop action inside the Update method, the action is not executed immediatly. Instead the ScreenManager batches the operation (e.g. I have a private struct called ScreenManagerAction which can be of type Switch, Push, Pop + parameters and I store these actions in a list) and the actions are only applied after the Update loop ends. This way the iteration of the screens does not break. \$\endgroup\$ Dec 27, 2011 at 17:52
  • \$\begingroup\$ Interesting approach, thanks for sharing! Since I am currently using XNA/C# I simply copy my list of Screens in the ScreenManager class to an array using ToArray() before iterating. Different approach, basically same result ;-) \$\endgroup\$ Dec 27, 2011 at 23:01

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .