Is this not how it's done traditionally? Or am I missing something?
'traditional' isn't a technical term, but in this context it would mean 'plainly wrong and rather straw-mannish'. Of course you can get leakage if you do it that way. You're using raw pointers for resource control. That doesn't mean that C&S is the only alternative. Doing it right is another alternative, as I showed in the block after that.
C&S collects the copying behavior and then adopts it in a single transaction. That's the correct pattern, but C&S is not the only way to implement that pattern, and it can be unclear, make the machine do more work than it needs to, and/or trigger side-effects.
Using smart pointers or other RAII implementors allows you to prepare the new state, and then once everything is created successfully you adopt it. That's the 'normal' way of writing exception safe code. C&S is just a trick for doing that by invoking the copy ctor and then adopting it only if construction succeeds.
It does not magically guarantee exception safety.
Your code here is NOT exception safe!
// copy constructor
Player( const Player& that ) : m_Texture( new Texture(*that.m_Texture) ),
m_Sprite( new Sprite(*that.m_Texture) ),
m_Health( new m_HealthController(*that.m_Health) )
{
}
What if the allocation for m_Health fails? m_Texture and m_Sprite have already been allocated! How will you reach them in order to release them, since the object failed construction?