1. Local Space and Global Space
Before writing a single line of code, you need to understand one of the most fundamental concepts in any 3D engine: the difference between local space and global space (also called world space). Confusing the two is one of the most common sources of bugs for developers new to Unity and sometimes even for experienced ones.
Imagine a passenger sitting in a car. From the passenger's perspective, they are always in the same seat their position relative to the car never changes. That is their local position. But from the perspective of someone standing on the street watching the car drive past, the passenger's position is constantly changing. That is their global (world) position. Both descriptions are correct; they simply use different frames of reference.
In Unity, every GameObject exists in the global world space, but it also has a local space defined relative to its parent. When you look at the Inspector panel with a GameObject selected, the Transform component shows local coordinates position, rotation, and scale relative to the parent. If the object has no parent, local space and world space happen to be the same thing, which can make the distinction feel invisible until the day you nest one object inside another and everything starts behaving unexpectedly.
In C# code, you access the two positions through the Transform component:
// World (global) position of this GameObject
Vector3 worldPos = transform.position;
// Local position relative to parent (same as world if no parent)
Vector3 localPos = transform.localPosition;
// Setting position in world space
transform.position = new Vector3(0f, 1f, 5f);
// Setting position in local space
transform.localPosition = new Vector3(0f, 0f, 2f);
A practical rule of thumb: when you want to place an object at a specific point in the world regardless
of its hierarchy, use transform.position. When you want to position an object relative to
its parent such as a weapon in a character's hand use transform.localPosition.
2. Cartesian Coordinates and the Transform Component
Unity uses a left-handed Cartesian coordinate system. The X axis points right, the Y
axis points up, and the Z axis points forward (into the screen). Every point in space is described by
three numbers (x, y, z), which is exactly what Unity's Vector3 struct stores.
Positions and scales are represented as Vector3; rotations are stored as
Quaternion internally, though the Inspector displays them as Euler angles in degrees for
human readability.
The Transform component is special: it is the only component that is present on every single GameObject in Unity, without exception. You cannot remove it. This makes it the anchor point for all spatial information about an object.
using UnityEngine;
public class CoordinatesExample : MonoBehaviour
{
void Start()
{
// Accessing position (world space) transform is automatically cached
Vector3 pos = transform.position;
Debug.Log("World position: " + pos);
// Accessing local position
Vector3 localPos = transform.localPosition;
Debug.Log("Local position: " + localPos);
// Accessing rotation as Quaternion
Quaternion rot = transform.rotation;
// Accessing rotation as Euler angles (degrees) convenient for display
Vector3 eulerRot = transform.eulerAngles;
Debug.Log("Rotation (Euler): " + eulerRot);
// Accessing local Euler angles (relative to parent)
Vector3 localEuler = transform.localEulerAngles;
// Accessing scale (always local there is no world-space scale getter)
Vector3 scale = transform.localScale;
Debug.Log("Scale: " + scale);
}
}
3. Calculating the Distance Between Two Points
Knowing how far apart two objects are is one of the most common needs in game programming. The distance between two points A and B in 3D space is given by the Euclidean distance formula:
Distance = |A − B| = √( (Ax − Bx)² + (Ay − By)² + (Az − Bz)² )
Unity already provides this as a one-liner with Vector3.Distance():
float distance = Vector3.Distance(A, B);
// Or equivalently, using the magnitude of the difference vector:
float distance2 = (A - B).magnitude;
Performance: sqrMagnitude vs magnitude
When you only need to compare distances, you can avoid the square root entirely by comparing squared distances instead:
float attackRange = 5f;
Vector3 enemyPos = enemy.transform.position;
Vector3 playerPos = transform.position;
// Inefficient: performs a square root
if (Vector3.Distance(enemyPos, playerPos) < attackRange)
{
Attack();
}
// Efficient: compare squares, no square root needed
if ((enemyPos - playerPos).sqrMagnitude < attackRange * attackRange)
{
Attack();
}
4. Value Clamping with Mathf.Clamp
Clamping means constraining a value so that it cannot exceed a specified range. Unity provides this as
Mathf.Clamp():
// Clamp a player's health between 0 and 100
float health = 150f;
health = Mathf.Clamp(health, 0f, 100f);
Debug.Log(health); // 100
A complete health system example showing clamping in context:
using UnityEngine;
public class HealthSystem : MonoBehaviour
{
[SerializeField] private float maxHealth = 100f;
private float currentHealth;
void Start()
{
currentHealth = maxHealth;
}
public void TakeDamage(float amount)
{
currentHealth = Mathf.Clamp(currentHealth - amount, 0f, maxHealth);
Debug.Log($"Health after damage: {currentHealth}/{maxHealth}");
if (currentHealth == 0f)
{
Die();
}
}
public void Heal(float amount)
{
currentHealth = Mathf.Clamp(currentHealth + amount, 0f, maxHealth);
Debug.Log($"Health after healing: {currentHealth}/{maxHealth}");
}
private void Die()
{
Debug.Log("Game over!");
}
}
Mathf.Clamp01
The range 0 to 1 is so common in game development normalized fractions, interpolation parameters, alpha
values that Unity provides a dedicated shorthand: Mathf.Clamp01(t).
float t = 1.5f;
t = Mathf.Clamp01(t);
Debug.Log(t); // 1
5. Calculating a Percentage
Given a current value and a maximum value, the percentage is:
percentage = (value / max) × 100. In practice it is often more useful to work with the
normalized form a number between 0 and 1:
// Example: experience bar
float currentXP = 750f;
float xpToNextLevel = 1000f;
// As a 0-100 percentage
float percentageDisplay = (currentXP / xpToNextLevel) * 100f;
Debug.Log($"XP: {percentageDisplay}%"); // 75%
// As a normalized 0-1 value (for UI fill amounts, shaders, etc.)
float normalizedXP = currentXP / xpToNextLevel;
Debug.Log($"Normalized XP: {normalizedXP}"); // 0.75
Always make sure the denominator (max) is never zero before performing the division:
float safeNormalized = (maxHealth > 0f) ? currentHealth / maxHealth : 0f;
6. Calculating a Value from Its Percentage
The reverse operation given a percentage, find the corresponding value uses the formula: value = t × max
float maxHealth = 200f;
float t = 0.3f; // 30%
float healthFromT = t * maxHealth;
Debug.Log($"Health from t: {healthFromT}"); // 60
// Example: damage scaling 25% of target's max health
float targetMaxHP = 500f;
float spellDamagePercent = 25f;
float damage = (spellDamagePercent / 100f) * targetMaxHP;
Debug.Log($"Spell damage: {damage}"); // 125
Connection to Lerp
float minSpeed = 2f;
float maxSpeed = 10f;
float speedT = 0.75f; // 75% of the way to max speed
float currentSpeed = Mathf.Lerp(minSpeed, maxSpeed, speedT);
Debug.Log($"Speed: {currentSpeed}"); // 8 (2 + 0.75 * (10 - 2))
7. Inverting a Value
Inversion of a normalized value: if you have a number t that goes from 0 to 1, the inverted
value 1 - t goes from 1 to 0. The classic use case is fading same timer, opposite effect:
using UnityEngine;
public class FadeEffect : MonoBehaviour
{
[SerializeField] private float fadeDuration = 2f;
private float timer = 0f;
private SpriteRenderer spriteRenderer;
void Start()
{
spriteRenderer = GetComponent<SpriteRenderer>();
}
void Update()
{
timer += Time.deltaTime;
float t = Mathf.Clamp01(timer / fadeDuration);
// Fade-in: alpha goes from 0 to 1 as t goes from 0 to 1
float fadeInAlpha = t;
// Fade-out: alpha goes from 1 to 0 as t goes from 0 to 1
float fadeOutAlpha = 1f - t;
// Apply fade-out
Color color = spriteRenderer.color;
color.a = fadeOutAlpha;
spriteRenderer.color = color;
}
}
Absolute Value
float speed = -3.5f;
float speedMagnitude = Mathf.Abs(speed);
Debug.Log($"Speed magnitude: {speedMagnitude}"); // 3.5
// Useful for checking if a value is close to zero, regardless of sign:
float velocity = -0.001f;
if (Mathf.Abs(velocity) < 0.01f)
{
Debug.Log("Object is nearly stationary.");
}
With these foundational tools coordinate spaces, distances, clamping, percentages, and inversion you have a solid mathematical toolkit for the most common calculations in any Unity project. In the next chapter, we will build on this foundation by exploring vectors in depth.
Need help with Unity3D development?
I'm a senior developer with 16+ years experience, including AAA projects at Ubisoft. Let's discuss how I can help with your game or interactive project.
Start a Conversation