I've encountered systems like this; hell, I've even written some when I was in a hurry during prototyping. It is not specifically wrong, it is just one way in which systems can evolve. Reasons systems evolve this way include:
- The root controller class / instance needs to be privy / proxy to much or all of what other actors in the system, are doing (perhaps even before it is done); maybe it needs to take some secondary action.
- The root controller is the only one who is authorised to use this message to issue imperatives to other sub-controllers, i.e. the usual Hierarchy of Control we see in most programs. So by this manner of thinking, messaging in this system has to go via the root controller.
- The lack of time, willingness, or knowledge as to how to improve the system.
...the reality is the first two are just ways of thinking about the system. This kind of "deep propagation" up and down through a control tree gets increasingly tedious to manage explicitly, as the tree deepens... and deepens... as it tends to do in an app as complex as the typical game.
Is this necessarily a bad thing?
As someone else said, Yes, usually. Under most conditions, tight coupling is to be avoided. The reason is that the cost of modifying the code becomes large, quickly.
Are there any simple architectural solutions to avoid having every class contain a pointer to parentGame?
One way is to pass methods from super-controllers downward, let sub-controllers call them. This breaks encapsulation; but it is a relatively quick solution. Ultimately though, if these methods are modifying state on the parent, it still acts as a sort of messaging hub, just as you describe.
I would not recommend this except as an interrim measure on the road to a better architecture.
Another way is the observer pattern, A.K.A. pub(lish)-sub(scribe). This prevents your having a direct link to an instance of a specific class; rather, you have indirect, generic links to instances of a specific interface from which your classes inherit. As subscriber, you can now listen to the messages which the publishers broadcast, or broadcast your own message to those subscribed to you. A given class can even be both a publisher and a subscriber, provided it inherits or implements both sets of functionality!
Anything that needs to know when something else changes, but doesn't want to create a specific, typed link to the something else in question, should use this pattern.
One of the reasons it works so well is the observer has a list of these abstract / interface-derived objects. If there are no objects, the iterator does nothing, and there is no null reference error (which you will get plenty of, in your current project). If there is one object, or many objects which the observer is listening to, it treats them all similarly. Lists / Arrays are powerful this way, and it is one reason language design is headed in this direction; avoiding dreaded NREs / NPEs completely.
The essence is that you can have multiple observer arrays and multiple response styles (callbacks) for those different sorts of objects observed by each array. Different objects may subscribe, for example, to messages from both children and global services, with each type of child, and each type of service, being intercepted and handled in a different manner.