Lets focus on the obvious advantage first: in the first example, the controllers are mostly independent from each other.
PlayerSelectCharacterController does not need to know about the class
CharacterMoveController or vice versa. Each of these classes can be developed separately, tested separately, or if necessary, placed in different compilation units.
That is not possible in the second example:
PlayerSelectCharacterController needs to know about
CharacterMoveController, this is a hardcoded dependency.
With just two classes, this may not be worth to think about, but if your game has 20 or more states, you will easily get a huge dependency tree, where every controller depends on every other controller, maybe in some cyclic way.
What may be less obvious: in games, state transitions are often not that simple as in your question's example. There might be complex rules how to change from one state to another. For a certain kind of complexity, it may be desirable to separate the two concerns of
- deciding about the new state, and
- actually changing the state.
I would actually expect the logic you showed us in
characterSelected to be happen somewhere outside the
PlayerSelectCharacterController, maybe in the
characterSelected maybe a function returning the new state, but the assignment to globalData should not happen directly in
PlayerSelectCharacterController). That would also avoid the need to call
parentController.stateChanged() from within
PlayerSelectCharacterController, this could be done by the outer loop, which can prevent some potential problems with recursion as well as code duplication.
As a general strategy, try to build the individual controllers as separate building blocks, with no side effects to global data, and no direct dependencies to other controllers of the same kind. Otherwise you risk getting an unmaintainable "big ball of mud".