Tight Corners - Solving a Movement Issue
Over the past few weeks, the movement code in Reboot has come a long way. I've reworked the camera for smoother feedback when the player is acceleration and decelerating. The player also has a significant amount of control over the cameras rotation around the player in order to better observe the play space. But there was a single nagging issue for a few weeks that bugged myself and the rest of the team when it came to how the player moved throughout the level. And so once the camera had reached an acceptable state, and movement in general was on stable footing, I turned my attention to the simple act of turning our skating television.
The turning in the game worked just fine. There were no bugs. But the feel was off. There was a lack of responsiveness in terms of how tight turns were and that lead to a noticeable amount of fishtailing on tight corners. After some testing and discussion, it became clear that the root cause of the issue lay in some kind of disconnect between the way turning was handled in code versus how velocity was calculated.
Turning by itself, requires no physics interaction. The player is turned by simply applying input data to the y axis of the player's local Euler angles. This functioned well enough, but it did not account for velocity. In the old code for example, the momentum of the player character's velocity would be directed forward, while the player would correctly face the player's input direction. The player would begin to move in the direction they were facing, but it was not a one to one motion. This caused the fishtailing effect. The momentum of the previous velocity vector was being applied to the player's rigidbody for at least a few additional frames, resulting in turning that felt slippery and friction-less.
To solve this, I attempted a number of solutions, some complex and some minuscule. The solution I found turned out to be fairly painless to implement. The solution, in essence, required the direction of the velocity vector to immediately match the direction of the player model's forward transform. This was ultimately achieved in three lines of code. The first step was to store the magnitude of the frame's current velocity. This meant storing the player's speed. Next, I zeroed the players velocity within that same frame, and then set the rigidbody velocity equal to the player's forward direction multiplied by the stored magnitude.
The solution to this issue turned out to be a relatively painless one, but I learned some valuable lessons regardless. I spent a lot of time doing research and plotting out ways to do complex math on to do what I was able ultimately solve in three lines. I learned nagging problems don't always require massive solutions. The amount of time a problem takes does not directly correspond to the complexity of the solution. Another important lesson was that fixing one issue can easily result in others. While movement was tight and snappy, we found out that during our last QA, my solution resulted in strange physics if the player attempted to turn the character while in midair. All in all, solving this issue was pretty fulfilling and taught me a lot. Next time, I'll be sure to do some more testing before I go and celebrate my problem solving skills.