Collisions and Floating Characters

Silhouette of floating person by the shore

When you can’t stay grounded… ask your developer why they made you that way. How to keep your Unity character grounded after hitting colliders.

Seems like there’s an ask your developer why they did it theme forming here. Turns out this developer did not know they were making their player float after it collided with an object. Lesson learned: test player movement in a realistic environment. Today we will take a look at the fix and get our character’s feet back on the ground.

In our last post, we went over how to move the character with rotation and forward and backward movement. The approach I described there uses the Transform component and doesn’t work well if your character has a Rigidbody component and you want it to respond to gravity or use physics in any way. Fixing the floating issue took some digging around, but eventually I stumbled across the answer to this post which lead me in the right direction. Then I found this video which has a great demonstration of the difference between movement using Transform and Rigidbody. Today, I’ll share with you what I learned while fixing the floating issue.

Transform and Rigidbody and Their Differences

Both Transform and Rigidbody are components that can be added to a GameObject and both can be used for moving the object. The important distinguishing feature between the two is that Rigidbody uses the physics engine and Transform does not. Rigidbody should be used when your character is using physics, like gravity and colliders. Using transform will not allow for collision detection and won’t respond to physics. This is why my character would float after hitting an object. It would go up to the top of the object and then it wouldn’t respond to gravity after it moved away from the object.

When using Rigidbody or the physics engine, it’s important to used FixedUpdate() instead of Update().

void Update()

Calls frequency of Update() is based on frame rate which can vary based on the machine. We will use for getting the user input.

void FixedUpdate()

Calls frequency of FixedUpdate() is at the rate of the physics system. It is called before the physics system updates. We will use it for moving the character.

Class Variables

Rigidbody rb;                // Rigidbody component
Vector3 zMovement;           // Forward and backward movement 
                             // based on input
Vector3 moveValues;          // zMovement changed to face the 
                             // direction of the character 
                             // based on it's rotation
Vector3 rotationMovement;
public float MovementSpeed = 5f;
public float RotationSpeed = 5f;
private float horizontal;

Start()

rb = GetComponent<Rigidbody>();
zMovement = Vector3.zero;
rotationMovement = Vector3.zero;

At the start we are zeroing out the movement vectors, since we are only changing one axis in the update we want the others to be zero. Because we aren’t changing them at all in the update, we can just make them zero here once and they are set to go. We also get the Rigidbody component at the start, since we only need to do it once.

Update()

horizontal = Input.GetAxis("Horizontal") * RotationSpeed;
zMovement.z = Input.GetAxis("Vertical") * MovementSpeed;

As I described before in Update(), we are getting our user inputs in this method. We also multiply the input times the speed here. It would also work to move the speed multiplication to argument of the Quaternion.Euler() and MovePostiion() calls in FixedUpdate() below.

FixedUpdate()

Prepare to be moved! This is where the action happens.

// Rotate Left and Right
rotationMovement.y = horizontal;
Quaternion deltaRotation = Quaternion.Euler(rotationMovement 
    * Time.fixedDeltaTime);
rb.MoveRotation(rb.rotation * deltaRotation);

// Forward Backward Based on Character's Perspective
moveValues = transform.TransformDirection(zMovement);
rb.MovePosition(transform.position + moveValues * 
    Time.deltaTime);

rotationMovement.y – assign the movement to the y axis based on the horizontal input we got in Update() to our input to this Vector3 variable.

deltaRotation – get rotation based on the movement input (in the format Unity uses to represent rotation, Quaternion). We use and Time.deltaTime as a factor to makes sure the speed is the same regardless of frame rate.

rb.moveRotation() – move the character based on it’s current rotation and the calculated rotation.

moveValues – assign direction of the movement Vector3 to be local (character perspective) instead of global.

rb.movePosition() – move the character based on current position, movement based on input, and Time.deltaTime which makes sure the speed is the same regardless of frame rate.

In Conclusion

There you have it. Now the character will move AND respond to physics. Basically, there are two (and a half) takeaways here. The first is to test your scripts in a realistic environment. The second is that if you are using physics at all for your character, write your movement using Rigidbody methods and use FixedUpdate().

Until next post, be a goldfish.

Sources

Print Friendly, PDF & Email