1.5 — Movement II: Speed, deltaTime & Tuning
Your character moves, and one thing about that is wrong: hold two direction keys at once and it travels 41% faster. Today you find out why, fix it in one line, and tune the number that decides how the character feels.
My character moves faster on the diagonal than in a straight line. The movement from 1.4 already works, and that is what makes the wrongness hard to see.
First, before any code: cut that into parts small enough that you could look each one up on its own.
- Read which way the player is pushingThe two axis reads from 1.4, assigned to the
inputXandinputYfields you already declared. Unchanged — nothing here is the bug.Read: theInput.GetAxis("Horizontal")row in the quick reference. - Build a direction vector out of the two numbersPack both axes into one
Vector2: pure direction, with no speed and no time multiplied in. Its length is whatever the input happens to be — the whole problem.Read: theVector2row in the quick reference, and the vector you built in 1.4. - Rescale that vector to a length of exactly 1
direction = direction.normalized;keeps the arrow pointing the same way and throws its length away, so "up and right" is(0.707, 0.707)instead of(1, 1). The fix is general, not a special case for diagonals.Read: the(a - b).normalizedrow in the quick reference — direction, length exactly 1. - Scale that unit direction into a distance, and apply itOne multiplication:
direction * moveSpeed * Time.deltaTime. Because the direction is always length 1, this distance is identical whichever way you face. Then the Transform applies it, as in 1.4.Read: theTime.deltaTimeand translate rows in the quick reference.
The new part sits in the middle rather than at the end. The vector from 1.4 was two numbers stapled together, which is fine until you do arithmetic with them: (1, 0) is length 1, (0, 1) is length 1, but (1, 1) is 1.41 — so two keys moved you 41% further. Keep direction and distance in separate parts and the fix is one line.
Is "which way" still in the same part as "how far"? If you cannot point at the one line where speed is applied, the two are still tangled.
Your turn
The fix was one line, and it landed in the middle part rather than the last one. Why does a bug get easy to find exactly when the problem has been split into the right parts?
- Project window →
Assets/Scripts, and openPlayerController.cs— the file you built in 1.4. - Game view → press Play and hold D for two seconds, then W and D together. The diagonal covers visibly more ground — you can see the bug before you write anything.
- Hierarchy → select the Player, then Inspector → Move Speed. Change the number while the game runs and walk again: too low is sluggish, too high is twitchy.
The Player's Inspector. Move Speedis the only fieldPlayer Controller (Script)has, and this is the box — a serialized value beats the code default, so the number you leave in it is the one the game runs with. - Inspector → Move Speed again, bisecting toward a number that feels right for a world this size —
4to7is a good starting range. Write it down. - Game view → stop the game, then enter that number in the same box in edit mode so it persists. Then GitHub Desktop → commit and push the fix and the tuned speed.
Unity offers four ways to move an object, each because it handles something the others handle badly. You use the first today.
| Technique | How it works | Needs a Rigidbody2D? | Best for |
|---|---|---|---|
transform.Translate(vector) | Offsets coordinates directly, skipping the physics engine. | No | Simple top-down games, moving UI, linear projectiles |
Rigidbody2D.MovePosition(position) | Drives the body to a target position, so whatever is in the way gets a say: a Dynamic body is stopped by a solid collider, a Kinematic one arrives anyway. | Yes — Dynamic if you want a wall to stop it | Moving platforms and scripted set pieces that must arrive at an exact position |
Rigidbody2D.linearVelocity = vector | Assigns a velocity and lets the physics engine move the body. The number is per second, not per frame. | Yes — Dynamic if you want a wall to stop it | Snappy platformers, arcade physics, momentum across surfaces |
Rigidbody2D.AddForce(vector) | Applies a physical push, factoring in mass, drag and inertia. | Yes — Dynamic; a Kinematic body ignores forces | Jumping, thrusters, ice physics, knockback |
Socratic: Choosing the Right Tool for the Job
- You proved in 1.4 that the translate call walks through walls, while
Rigidbody2D.MovePosition(position)is stopped by one — on a Dynamic body. What does that call consult that the Transform never does? - Three of these four need a component your Player does not have. What would you add, and where — on the Player, or on the child Sprite?
- Why would a spaceship drifting through space prefer
Rigidbody2D.AddForce(vector)to the translate call?
The question this answers“why does my character move faster diagonally, and how do I fix it?”
You are editing Assets/Scripts/PlayerController.cs. You have working movement from 1.4. This replaces two of those lines; the fields and the input reads do not change.
Step 1Separate direction from speed
In 1.4 you multiplied the input by speed in two separate places. Pull those apart: first work out which way to go, then decide how far.
// PlayerController.cs — inside Update(), replacing the two displacement lines from 1.4.
Vector2 direction = new Vector2(inputX, inputY);
direction = direction.normalized;
Vector2 movement = direction * moveSpeed * Time.deltaTime;
// PlayerController.cs — unchanged from 1.4: the finished distance goes to the Transform.
transform.Translate(movement);- 2A vector pointing whichever way the player holds. Pure direction — no speed, no time — and its length is whatever the input happens to be: D alone gives
(1, 0), but W and D together give(1, 1), which is 1.41 long, not 1. That extra 41% is the bug you can feel. - 4The fix:
.normalizedkeeps the vector pointing the same way but rescales it to a length of exactly 1, so "up and right" becomes(0.707, 0.707).There is no secondVector2in front ofdirection— the variable already exists, so this line assigns to it, asinputXdid in 1.3. - 6Scales that unit direction up to a real distance: one second of travel (
moveSpeed), cut down to one frame (Time.deltaTime).Becausedirectionis always length 1, andTime.deltaTimeis the seconds the last frame took, this is the same real-world speed at 30 FPS and at 240 FPS. - 9Hands the distance to the Player's own Transform, which adds it to the current position. Unchanged from 1.4.
Check your work: the finished filebuild it as you read — then open this to check every line
The only lines that should differ from 1.4 are the ones building direction and movement — fields, input reads and translate call are unchanged.
// ------------------------------------------------------------------
// REFERENCE FILE — Assets/Scripts/PlayerController.cs
// Written to be read, not pasted. Build it with the walkthrough
// above, one step at a time, then open this and compare.
// If it is sitting in your project unchanged, that is the thing
// to go and do now — the walkthrough is still above you.
// ------------------------------------------------------------------
using UnityEngine;
public class PlayerController : MonoBehaviour
{
[SerializeField] private float moveSpeed = 5f;
private float inputX;
private float inputY;
void Update()
{
inputX = Input.GetAxis("Horizontal");
inputY = Input.GetAxis("Vertical");
Vector2 direction = new Vector2(inputX, inputY);
direction = direction.normalized;
Vector2 movement = direction * moveSpeed * Time.deltaTime;
transform.Translate(movement);
}
}Checkpoint — before moving on, you must be able to:
- Compare four ways to move an object:
transform.Translate(vector),Rigidbody2D.MovePosition(position),Rigidbody2D.linearVelocity,Rigidbody2D.AddForce(vector) - Say which of the four need a Rigidbody2D, which body type a wall can stop, and what moving along a path does differently from a teleport
- Explain, in your own words, why (1, 1) is a longer vector than (1, 0) — and what
.normalizeddoes about it - Explain how
Time.deltaTimeproduces the same real-world speed at different frame rates - Explain the relationship between a code default and a serialized Inspector value
- Tune your speed while the game runs, save it in edit mode, and commit
Reference Docs
Need a deeper explanation? These pages cover the same concepts:
- Unit 1: Setup & Movement — speed, vectors, and physics functions
- Unity + C# Quick Reference — Rigidbody2D and Transform APIs
- How do I…? index — find this lesson again by the question you were asking
Saved on this device only — the Dashboard tracks your progress.