Moving a character: which of the three ways is yours
How do I make a character move?
Pick one of three, never two: CharacterController for a player you control directly, Rigidbody for anything physics should push around, and transform.Translate only for things that do not collide. Multiply every speed by Time.deltaTime or the game runs faster on a faster machine.
There are three ways to move something in Unity and they do not mix. Picking the wrong one is why a character sinks into walls, or refuses to move at all, or moves at twice the speed on a 120 Hz phone.
The same wall, met three ways. Only the CharacterController slides along it, only the Rigidbody can shove the crate, and transform.Translate does not know the wall is there.
Pick one — the whole decision
CharacterController — a player you drive directly. It slides along walls, climbs steps and never gets pushed around by physics. This is what most third-person and first-person games use.
Rigidbody — anything physics should be in charge of: a rolling ball, a crate you shove, a ragdoll. You ask it to move; the physics engine decides what actually happens.
transform.Translate — only for things that never collide with anything: a floating pickup, a moving cloud, a UI element. It teleports; it does not push.
CharacterController
Add the component, then move it with Move(). It takes a distance for this frame, not a speed, which is why deltaTime is in there.
using UnityEngine;
[RequireComponent(typeof(CharacterController))]
public class PlayerMove : MonoBehaviour
{
public float speed = 6f;
public float gravity = -20f;
CharacterController cc;
float verticalSpeed;
void Awake()
{
cc = GetComponent<CharacterController>();
}
void Update()
{
// Input is a direction, length 0 to 1. Never a distance.
Vector3 input = new Vector3(Input.GetAxisRaw("Horizontal"), 0f, Input.GetAxisRaw("Vertical"));
if (input.sqrMagnitude > 1f) input.Normalize();
// Gravity has to be applied by hand: a CharacterController ignores physics.
if (cc.isGrounded && verticalSpeed < 0f) verticalSpeed = -2f;
verticalSpeed += gravity * Time.deltaTime;
Vector3 move = input * speed + Vector3.up * verticalSpeed;
cc.Move(move * Time.deltaTime);
}
}Rigidbody
Two rules and the rest follows: write to the Rigidbody, never to transform, and do it in FixedUpdate, not Update. Physics runs on its own clock, and writing to transform tells the physics engine nothing — it will happily move the object back.
using UnityEngine;
[RequireComponent(typeof(Rigidbody))]
public class BallMove : MonoBehaviour
{
public float speed = 8f;
Rigidbody rb;
Vector3 input;
void Awake()
{
rb = GetComponent<Rigidbody>();
}
void Update()
{
// Read input here: Update runs once per frame, so no press is missed.
input = new Vector3(Input.GetAxisRaw("Horizontal"), 0f, Input.GetAxisRaw("Vertical"));
if (input.sqrMagnitude > 1f) input.Normalize();
}
void FixedUpdate()
{
// Move here: FixedUpdate runs on the physics clock.
rb.MovePosition(rb.position + input * speed * Time.fixedDeltaTime);
}
}Why the input is read in Update and used in FixedUpdate
FixedUpdate runs 50 times a second by default; your game might render at 120. Reading Input.GetKeyDown in FixedUpdate therefore misses presses that happened between physics ticks.
Use Time.fixedDeltaTime inside FixedUpdate and Time.deltaTime inside Update. Unity actually returns the right one either way, but writing the matching name keeps the intent readable.
Set the Rigidbody's Interpolate to Interpolate if the camera follows it, or the movement looks like it is vibrating at 50 Hz.
transform.Translate
void Update()
{
transform.Translate(Vector3.forward * speed * Time.deltaTime);
}the lesson that builds this
Movement, jumping and landing on blocks
This post is a standalone recipe. In the course, the same thing is built as part of the one project that runs through all five tabs — Character & Combat, lesson 02.