Fundamentally, if you have implementations of any interface (Projectile
in this case) that need data that isn't available via that interface to operate then your solutions are:
- Modify the interface such that it can supply the data.
- Give the data directly to the subclass implementation.
- Accept that perhaps some of the implementations are can longer correctly be considered implementors of that interface, and treat them separately.
The first option tends to scale poorly and lead to very poor OO designs, because over time the interface or base class becomes bloated with various pieces of data that are only actually relevant to a subset of implementations or subclasses.
The second option is generally preferable (you could argue that the third is as well, but that often involves a more significant set of changes). Alexandre's answer provides possible implementations of both, but you yourself provided (what I think is) a better one:
I could also store a pointer to the player in the Missile class,
I'd argue this is better from an OO design perspective (because it doesn't push subtype-specific behavior up into a base type), and also better from a more abstract design perspective (because it doesn't require reliance on global state).
You could generalize this to storing a reference to some kind of HasPosition
type of interface, so your missiles can track anything.
You note that
this quickly becomes messy
but I'd challenge that argument, on the basis of the above points. Also on the basis that where I can see some eventual complexity here, since you describe your game as "simple" I'm not convinced it would come up.
I will concede that you've traded a type design problem for a lifetime design problem in going this route, however: now you have to either ensure that the target always outlives the missile (not practical in most game contexts) or you have a dangling reference.
However you can address that by providing the reference via something other than a vanilla, language-level pointer or reference. Using a handle or similar "weak" reference that allows you to determine if the referred-to thing is still alive and valid allows you to deal with the lifetime problem by first checking for validity in the missile's Update
function. If the target reference is still valid, track it. Otherwise let the missile continue along whatever its previous trajectory was, or maybe try to acquire a new target.