Sunday, May 10, 2015

Player movement

  One of the most important things about gameplay for me is how fluent the controls feel and how the character behaves in the game. It has been a constant battle of fine-tuning the values, adding and removing and changing the code to make everything as flawless as possible.

  When the project began, I was first using Unity's own CharacterController component, but soon found out that it didn't allow me so much control of how the character behaves, much less with our custom gravity and other physics systems, so I quickly adapted a rigidbody based movement system. This brought problems of its own though, and required much more scripting to be done.

Some examples of rigidbody movement problems:

  • the player was climbing on obstacles it wasn't supposed to
  • it often thought it was falling when it was actually not, or vice versa
  • external forces were affecting the player in a way that was not intended
  • movement on stairs was erratic

Collection of triggers and colliders
  Especially the issue with checking whether the character is touching the ground or not, was surprisingly challenging. OnTrigger and OnCollision checks aren't always as reliable as you might prefer them to be, and single raycasts are often TOO accurate, easily resulting in a jerky movement.

  I ended up trying to freeze the Y position of the rigidbody to prevent the player from climbing on things, but when combined with the missed grounded checks from OnTrigger and OnCollision functions, the player often ended up floating a few inches from the ground, or stuck into an infinite falling state on the edge of the environment collider it was standing on.

  Eventually I just took a step back, removed 20% of the script, and allowed the physics engine do its thing.  That means the player movement is no longer restricted when under normal circumstances and the combination of OnCollision and OverlapSphere backup checks make sure that the weird floating and falling issues are gone for good.
With some custom scripting on stairs, moving platforms and such special cases, the player is finally behaving the way we want it to. This system is also lighter and collects less garbage data due to the removal of some obsolete functions and co-routines, so it's a win-win.

  As the development process is iterative, one has to go through these steps of minor success and lots of failures, re-writing code and optimizing until you get the result you're looking for. In my experience, very few things work after the first try, but I think it's this process of learning through mistakes that teaches us the best.

- Timo Kuronen

Bonus screenshot; playing with the lights