I think the core architecture of the Game State Management sample is really solid, but the specific screen implementations included with it are a little more placeholderey. We figured most people would reskin or replace the individual screens, so we concentrated on the design of the core system (GameScreen, ScreenManager, etc) and didn't spend so much time polishing things like MessageBoxScreen and NetworkBusyScreen.
What goes around comes around, right? I didn't have time to do much reskinning while making my AppWeek game. Dang it! Hoist by my own petard...
I found myself irritated by the way many of these screens lazily load small assets in their LoadContent methods. For instance, MessageBoxScreen loads a gradient texture, and LobbyScreen loads icons for displaying the player status. They use a shared ContentManager, which they look up from ScreenManager.Game.Content, and rely on ContentManager to cache the data for them. This means the file will only actually be pulled in from disk the first time someone asks for it, rather than every time a MessageBoxScreen is displayed, but the lazy loading also means there will be a small glitch the first time a MessageBoxScreen comes up.
Even a small loading glitch is enough to make the transition animation look jerky and unprofessional. Creating a game that feels slick and polished is kind of like being an Olympic gymnast, as it's not enough just to do something cool: for that elusive 10 score, you have to make it look effortless, too. Dropping frames while you load content doesn't exactly make the right impression!
To fix this, I declared a list of the offending assets:
static string[] preloadAssets = { "UI/gradient", "UI/cat", "UI/chat_ready", "UI/chat_able", "UI/chat_talking", "UI/chat_mute", };
I added this code to the LoadContent method of my main Game class:
foreach (string asset in preloadAssets) { Content.Load<object>(asset); }
This discards the return value from the Content.Load call, because it is only loading the assets in order to prime the shared ContentManager. When a MessageBoxScreen later asks for that same gradient texture, the ContentManager will already have it loaded, so will not need to hit the disk. No more glitches, and no need to bother changing the individual screen implementations.
Note: screens which use the shared ScreenManager.Game.Content are different to how screens with larger pieces of content (notably BackgroundScreen and GameplayScreen) work. Those have their own local ContentManager, which they initialize and unload in their LoadContent and UnloadContent methods, so their content does not stay loaded after the screen goes away. Although they take a while to load, these screens do not cause ugly glitches because the delay is hidden behind a LoadingScreen overlay, which is used whenever such a 'big' screen is activated.