Posted 16 April 2013 - 08:45 AM
Well, typically the Physics system would handle this sort of thing, rather than the Game class or the Car class where it currently resides. In physics-enabled applications, the Physics subsystem will integrate the simulation, apply any collision correction to fix penetrations, then notify the participant objects of their new transforms. In your current model, though, there just aren't very many elegant answers to your question. Integrating movement/physics into the object methods like this presents you with some issues that may come back to bite you later:
1) Visibility of the Heightmap to Car. Since Car is responsible for its own movement and physics, it needs to be aware of the world. That means, it'll have to have a reference of some sort to the Heightmap so that during UpdateDrive it can call GetHeight to correct its Y coordinate. This adds a dependency upon Heightmap to Car, so that any change to Heightmap requires changes or, at least, recompilation for Car. It will also have to have access to whatever organizational structure you use to hold all cars, in order to correct for collisions with other cars and objects. This can create circular dependency chains and knotty logical issues.
2) Different versions of Car for Player or AI controlled cars. If you decide to implement AI cars, you'll need a different version of Car from what you have now since an AI car wouldn't respond to controller input. So that means either duplicating update code, or refactoring Car into a hierarchy (a thorny, twisted road to go down in a game of any sort of complexity).
3) Modifying the simulation could mean modifying code spread out across many modules. Say you want to add gravity to the simulation, rather than just clamping Y values directly to the heightmap surface. This would facilitate, for example, jumping and falling. However, if the physics are spread out among all the various participating object classes (PlayerCar, AICar, etc...) then you have to go and correct each participating class's Update methods with the new code. Make sure you get them all, because if you forget to enable gravity for one then it will not behave uniformly with the remainder of the objects. And if later you decide to tweak something else, then again you need to traverse the entire codebase modifying each class Update method to account for it.
In a simulation that is built upon an actual physics library (rather than homegrown gestalt update methods) then actual movement, collision correction, etc... are encapsulated in a physics subsystem whose sole responsibility is to integrate the physics and move stuff around. Individual cars don't move themselves; they simply apply, or inject, forces into the system. Pressing the accelerator, for example, would inject a linear impulse in one direction, tapping the brake would inject a linear impulse or some other form of decelerating impulse in another. Holding the thumbsticks for steering would inject angular impulses. And so on. But the actual movement of objects would be encapsulated within the physics system, acting upon the properties set for a given object, and pushing transform matrices back out to the participating objects after the simulation step is performed. In this way, changes to the Physics subsystem are confined to a single module rather than spread out across several classes, and behavior is shared among all types of objects without introducing any kind of dependencies between those types.
It is for architecture questions like this that people have begun to strongly recommend composition-based systems rather than concrete classes such as Car. If your game were to progress along your current curve toward a fairly complex simulation, you would find an exponential increase in the difficulty of maintaining and modifying it. Eventually, for example, you might want to implement other vehicle types: motorcycles, trucks, jet-powered cars, etc... And you find yourself either building horrible, complex hierarchies and trying to shoehorn every vehicle type into it, or duplicating a horrendous amount of code across multiple discrete classes. Either way, it becomes more and more of a nightmare trying to maintain it, and significant changes to the simulation will require significant changes to large portions of the codebase.