Character movement in 3D games - what I've found - 18/07/2011, 15:41Character movement in 3D games is a trickier thing to do than it appears at first sight. Or at least it is for me, which is why I'm sharing this bit of information: I think I finally got it right.
I'll start by speaking about my previous attempts which did not go that well, and their disadvantages
1) Directly modifying the coordinates of the character, based on input.
This is by far the simplest: "if player presses right then character.x += 0.1".
What is the problem here? It makes it harder to account for frame-rate variation, for one thing, but especially makes it hard to implement other cool effects you might want to have, such as slippery surfaces, wind, slowdown due to steep surfaces. In the worst case scenario, it could conflict with the physics engine and make the character "vibrate" when the user instructs it to walk towards a wall. It will technically walk "into" the wall and depending on how the physics engine reacts, the result will be either vibration (the physics engine corrects the character's position to be outside the wall) or even worse: it may trespass the wall (the physics engine "corrects" the character's position to be the other side of the wall).
The "Lanjobot" project had this movement scheme, which made it look funny when the character could scale extremely steep mountains without any kind of slowdown, and people constantly pointed that out to me. I honestly didn't know what to answer because I had no idea how to properly do that (2007 was a long time ago. Not that I've been thinking all these 4 years about character movement (at least not all the time)).
2) Applying an acceleration directly to the player's character.
"if player presses right then character.acceleration.x = 1 and let the physics system take care of the rest. If the player stops pressing right, then slow down the character's speed by quickly ramping its velocity down to 0"
Of course it is a bit more complicated than that - the maximum speed must be capped, but that's the basic idea.
This plays very well with the physics system and feels natural with keyboard controls. It also works with analog joysticks as long as you don't try to suddenly go in the opposite direction to the character's movement. Why is there a difference between a joystick and a keyboard? the details lie in how direction change occurs in the keyboard with respect to an analog joystick.
When you use the keyboard and you change direction suddenly, most of the times there will be a small interval between keystrokes during which braking can occur.
Analog joysticks on the other hand are usually polled by the game engine instead of waiting to receive discrete events. This means the braking phase rarely occurs when changing directions because polling will rarely coincide exactly when the joystick is in the center (or the deadzone for that matter).
This results in having the player's character brake and turn very well when using keyboard, but sliding backwards (moonwalk-style) when using a joystick, which severely complicates actions like jumping from one small platform to another: people will instinctively press the opposite direction after a jump to brake.
This is one of the problems that have affected Memai so far, and in the versions publicly available at this moment it still does, however I am testing the newest method which I thought up a few days ago (it always takes me ages to write).
It is a bit more complicated. The basic idea is this: the character has basically two speeds - one derived from its feet (its actions, its intentions), and one derived from external factors (e.g. someone pushed him, wind, etc). We'll call the first one Va, the second one Ve. Both are vectors in 3D, and the second one can be ignored until the very end (and anyway it is 0 most of the time). There is a friction factor on the floor, which will govern how slippery it is. Lets call that fr.
On each frame, the input is read and the direction the player wants to move, call it Da a unit vector in 3D, is obtained. The magnitude of the current Va is stored in a scalar: Sa. Then, update Sa by applying the acceleration due to the input as if it were in the same direction - in my code this is done by doing Sa' = (Aa*dt), where Aa is the acceleration due to the player's actions and dt is the time elapsed between the current frame and the last- and multiply Da by Sa'. Which will give us the velocity in vector form the player would like the character to actually move in, Va'. Now is where friction factor, fr, comes into play: if the floor is slippery the player shouldn't be able to change its velocity so easily. Let's consider that for fr values close to 0, is when the floor is almost frictionless (for example an ice surface). For fr values close to 1 is when the floor is not slippery at all, so the character can freely change velocity with its own feet. Let's call the definitive velocity in which the character will move based on its actions, Vf. Vf will then be
Vf = ((fr)*Va') + ((1.0-fr)*Va)
What this means is that Vf takes into account both the "intentional" velocity Va' and the current velocity Va. If fr is close to 0, Vf will be mostly Va unaffected by Va' which is a rather believable slipping effect, while if fr is close to 1, the character will be able to change direction at any time - the way we expect platform games to work, and quite close to reality (although of course characters in platform games will always be more agile than real people. Blame Mario 64 for that, I consider it to be the game with the best control response for 3D platforming).
Then take speed capping into account. Also when the joystick is in the deadzone or there is no input from the keyboard, braking should occur. There is a slight modification to be made to the braking, as it also needs to take into account the friction factor fr.
The way I handle it is by taking multiplying Va by a braking factor br (which is a scalar), then by the friction factor fr and multiplying it times dt, the elapsed time between each frame, and storing the result in Vb, the velocity vector for braking:
Vb = br*fr*dt*Vb
Vf is then calculated as follows:
Vf = Va - Vb
to avoid "vibration" once Vf is under a certan threshold, it is set directly set to vector 0.
This gives a nice feeling of almost instantly braking on surfaces with friction factors close to 1.0, and braking with difficulty on slippery surfaces with friction factors close to 0. And pushing the controller in the opposite direction will make the character brake faster on these slippery surfaces, as it should be.
Finally after all is said an done, Ve is added to Vf, and the physics system takes care of the rest. Until the next frame.
< Back to blog