Over the past few weeks I fixed a bunch of bugs, and then turned my attention to player physics and movement control.
Problems with using CharacterController
Since the very earliest days of the game's development I've been using a CharacterController for the player along with a "character motor" script (adapted from a Unity sample) to control it using the CharacterController's Move() function. The character motor would apply forward / back, strafing, and jumping movement, acceleration due to gravity, and if the player was standing on a moving object it would make them move with it.
This setup worked reasonably well, but the CharacterController doesn't provide proper interactions with Rigidbodies in the world. The player is treated as a static immovable object when things collide with them. The player can't push objects around, and they can't be pushed by objects either. It just looks wrong when for example a vehicle hits the player at high speed and they don't move at all.
Also, the character motor implementation had a bug where, if the player collided with a Rigidbody, they would occasionally get thrown across to the other side of the map. Most often this happened when exiting a seat, very annoying! I suspect this issue was related to the code that moved the player when standing on something, but I'm not 100% sure.
Despite all these problems, I didn't particularly want to add more work to my plate by throwing out this character motor code and starting again from scratch. So I tried playing around in the OnControllerColliderHit() message to try and get the player to push stuff around, and in OnCollisionStay() I tried to make the player be pushed by objects that collide with them. I couldn't get this to work nicely though, I think it really is necessary for the player to have a Rigidbody to give proper physical interactions, anything else is always going to be a bit of a hack at best.
At this point I realised I needed to ditch the CharacterController and start from scratch after all, using a Rigidbody instead.
A physics based player
So now instead of a CharacterController, the player has a CapsuleCollider and a Rigidbody. I implemented new code that replicates the original forward / back, strafing, and jumping behaviour, by applying a force to the player's Rigidbody. It uses a Physics.SphereCast() to detect contact with the ground and to find the Rigidbody that the player is standing on, if any. If they are standing on a Rigidbody, its velocity is transferred to the player. Also, the force used to move the player is applied in the opposite direction to the Rigidbody that they're standing on, thus obeying Newton's third law.
To sum up the features and benefits of this new approach:-
- Correct player collisions with other Rigidbodies.
- Player can push other Rigidbodies around.
- Player can be pushed by other Rigidbodies.
- Player can stand on a moving Rigidbody.
- Obeys Newton's third law when player accelerates while standing on a Rigidbody, this results in really cool "conservation of momentum" behaviour.
- Player falls due to gravity automatically from having a Rigidbody, but even better, the player's weight now pushes down on the Rigidbody that they're standing on.
- Eliminates the old "player thrown across to the other side of the map" bug.
- Code is simpler and cleaner than the old character motor implementation.
Here are some examples of all this in action:-
As you can see this provides a level of interaction between the player and the constructions they build that simply wasn't possible before, so I'm happy I bit the bullet and re-implemented things this way!
However it's by no means perfect, and there are a few issues / missing features that will need addressing at some point, for example:-
- I'd like to implement code to handle climbing steps, I think the player needs an extra force to push them up steps, as they tend to get stuck at the moment.
- I also want to add something to make the player slide off ledges, as right now the player can stand more than half way off a ledge with their feet in the air, which looks weird.
- The old character motor had code to make the player slide down steep slopes, not especially crucial but it would be good to get this working in the new implementation.