This time I want to speak about my early experiences with building a Mario Kart feel for the game, implementing suspensions. I've been holding back because of many findings recently, at a point where I'm not even sure that we need them still
Nevertheless implementing a spring will be useful in many other mechanics, and it's probably one of the simplest "complicated things", adding a touch of randomness to your game, and the entry point to bringing a kind of challenges that, whilst they may be frustrating, can sky-rocket the replay value to players.
This is the very beginning, so we just have a box, representing our racing car. We'll apply gravity only for now. Gravity just means that for any time step, we're adding an acceleration of "G", the universal gravity constant (or the one that you choose for your game). Concretely, imagine that you subdivide the simulation time into small steps (milliseconds apart), and you compute and apply the forces at that time. The acceleration will affect the velocity, the velocity will affect the position.
If you're not convinced, you can try this very simple script into Unity, which you add to a cube that has no rigid body.
public class TEMP : MonoBehaviour {
private Vector3 velocity = Vector3.zero;
private Vector3 acceleration = Vector3.zero;
void FixedUpdate () {
acceleration = Physics.gravity;
velocity += acceleration * Time.fixedDeltaTime;
transform.position += velocity * Time.fixedDeltaTime;
}
}
You'll see that your box accelerates down more and more, just like real gravity. You can try to look at the values to understand what happens. Basically the acceleration is the result of applying the same force constantly (with no other force opposing it). In Unity, the following will result in the same, assuming the cube this time has a rigid body with Use gravity unticked:
public class TEMP2 : MonoBehaviour {
void FixedUpdate() {
GetComponent<Rigidbody>().AddForce(Physics.gravity, ForceMode.Force);
}
}
So the spring is implemented on the same model, because it's what we call a reactive device. As a super simplified description, the spring has a rest compression (a percentage of how compressed it is, with 1 being that it's fully extended, and 0 fully compressed, that is of zero length) which it tries to get back to, opposing what it can of force to bring the objects which it's attached to together.
When extended, the spring will apply force to the the item at the other end to pull it (it'll pull the ground too, but since we can say that its mass is infinite and it won't budge, it's enough to resolve it only on one end), the velocity of that item will gradually increase, and with nothing to stop it, the item will go below the rest ratio. When it gets lower, the spring will start to pull it back up, but since the object already has some velocity (inertia) downwards it'll take time to stop and start to go back up. Then again, the object will be pushed up until it reaches the green line, go past it, and then be pulled again.
This can be implemented very simply by the following formula:
F = -k * (x - d)
Where k is the stiffness of your spring (the bigger this, the more the spring will "push" or "pull", so the more impulse it will give to the object toward the rest point, which will make the spring appear stiffer), x is the current length of the spring, d the length at rest (green line above). This will give us F, the force that needs to be applied to the spring at any time (that is in our timesteps, inside FixedUpdate).
By implementing that, we'll that typical bouncing effect. In the current implementation, the object will never stop bouncing. For that we need to add what we call a damper. A damper simulates the energy loss due to the action of the spring, so that it might stabilize eventually to the rest point.
The formula is:
F = -k * (x - d) - b * v
Where b is the damping constant, and v is the velocity between the two points connected by the spring. In our case, we can use the velocity of our rigid body, assuming that the ground is not moving.
Try it, and play with the damping and stiffness constants until it feels right. I'd suggest printing the values so that you can understand what happens. In another article, I'll elaborate more why we use these springs and what to expect from them.