Introduction
Unity is the most popular game engine for beginners and pros, powering over 50% of mobile games in 2026. This tutorial guides you step-by-step to create your first 2D game: a blue square (player) that moves with arrow keys, collects yellow coins (+1 score), and displays the total at the top. No fluff: we install, code, and test. At the end, you'll have a playable executable. Why is this crucial? Unity handles physics, rendering, and multi-platform builds without boilerplate. Think of it like assembling a puzzle: the scene is your board, scripts are your moving pieces. Estimated time: 45 min. Ready to ship your first game?
Prerequisites
- Unity Hub 3.10+ (download from unity.com)
- Unity Editor 2023.2 LTS or 6000.0 (via Hub)
- Basic C# knowledge (variables, if/else)
- Recent Windows/Mac/Linux
- VS Code or Visual Studio (for editing scripts)
Install Unity and Create the Project
# 1. Download Unity Hub from unity.com
# 2. Install Unity Editor 2023.2 LTS via Hub > Installs > Add
# 3. Create project:
unityhub://2023.2.20f1/2d/core
# Or via Hub interface: New Project > 2D Core Template > Name: 'MyFirstGame' > CreateThese commands/instructions launch Unity Hub to install the stable LTS editor and create an optimized 2D template (orthographic camera, ready UI Canvas). Avoid preview versions: they often crash on beginner projects. The template already includes Physics 2D.
Set Up the Main Scene
Open Scene (double-click in Project > Assets > Scenes > SampleScene).
- Hierarchy: Delete Sample Cube. Right-click > 2D Object > Sprite > Square → rename to 'Player'. Position (0,0,0), Scale (0.5,0.5,1).
- Add Rigidbody2D (Component > Physics 2D > Rigidbody2D): Gravity Scale = 0 (no falling).
- Add BoxCollider2D: Is Trigger = false.
Set camera background to black: Main Camera > Background = Solid Color > Black. Save with Ctrl+S.
Player Movement Script
using UnityEngine;
public class PlayerController : MonoBehaviour
{
[SerializeField] private float speed = 5f;
private Rigidbody2D rb;
void Start()
{
rb = GetComponent<Rigidbody2D>();
}
void Update()
{
float horizontal = Input.GetAxis("Horizontal");
float vertical = Input.GetAxis("Vertical");
Vector2 movement = new Vector2(horizontal, vertical) * speed;
rb.velocity = movement;
}
}Attach this script to the Player: it reads arrow key/WASD inputs via GetAxis (smooth, -1 to +1 range), applies speed to Rigidbody2D for fluid physics. [SerializeField] lets you edit speed in the Inspector without recompiling. Pitfall: Forgetting rb = GetComponent() will crash in Start(). Test: Hit Play and move the square!
Attach the Script and Test Movement
Attach: Select Player > Add Component > type 'PlayerController' > attach the script. Set Speed=5 in Inspector.
Test: Play (Ctrl+P). Arrow keys = movement. Stop = auto Rigidbody friction.
Improve: Add screen limits. Window > Analysis > Physics Debugger (see green colliders).
Coin Collection Script
using UnityEngine;
public class Coin : MonoBehaviour
{
[SerializeField] private int value = 1;
private void OnTriggerEnter2D(Collider2D other)
{
if (other.CompareTag("Player"))
{
GameManager.Instance.AddScore(value);
Destroy(gameObject);
}
}
}On Coin (CircleCollider Is Trigger=true): OnTriggerEnter2D detects collision with 'Player' tag. Adds score via singleton GameManager, destroys coin. CompareTag is optimized over == "Player". Pitfall: Forgetting Player tag (Hierarchy > Player > Tag > Add Tag > Player > assign).
Prepare UI Score and Player Tag
- Player Tag: Tag dropdown > + > Player > assign to Player.
- UI Score: Right-click Canvas > UI > Text - TextMeshPro. Rename to 'ScoreText', Anchor Top-Center, Text="Score: 0", Font Size=36.
- Position at top-center of screen. Play: movement works, Player-Coin collision ignores score (next script).
GameManager Script for Score
using UnityEngine;
using TMPro;
public class GameManager : MonoBehaviour
{
public static GameManager Instance;
[SerializeField] private TextMeshProUGUI scoreText;
private int score = 0;
void Awake()
{
if (Instance == null)
{
Instance = this;
DontDestroyOnLoad(gameObject);
}
else
{
Destroy(gameObject);
}
}
public void AddScore(int points)
{
score += points;
scoreText.text = "Score: " + score;
}
}Persistent singleton: Awake ensures unique Instance. AddScore updates TMPro UI. [SerializeField] links ScoreText in Inspector (drag from Hierarchy). Pitfall: Forgetting TMPro import or drag → NullRef. Create Empty GameObject 'GameManager', attach script, link ScoreText.
Infinite Coin Respawn Script
using UnityEngine;
public class CoinSpawner : MonoBehaviour
{
[SerializeField] private GameObject coinPrefab;
[SerializeField] private float spawnInterval = 2f;
[SerializeField] private Vector2 spawnArea = new Vector2(8f, 4f);
void Start()
{
InvokeRepeating(nameof(SpawnCoin), 1f, spawnInterval);
}
void SpawnCoin()
{
Vector2 pos = new Vector2(
Random.Range(-spawnArea.x/2, spawnArea.x/2),
Random.Range(-spawnArea.y/2, spawnArea.y/2)
);
Instantiate(coinPrefab, pos, Quaternion.identity);
}
}Attach to Empty 'Spawner' (Position 0,0). Drag Coin as prefab (drag Coin > Project > Assets/Prefabs). SpawnArea defines zone (-4 to +4 X/Y). InvokeRepeating = reliable timer. Pitfall: No prefab → Instantiate null. Make Coin prefab first.
Finalize: Coin Prefab and Spawner
Coin Prefab: Drag Coin from Hierarchy to Assets/Prefabs. Delete original.
Spawner: Right-click Hierarchy > Create Empty > 'CoinSpawner'. Attach script, drag Coin Prefab, set Interval=2, Area=(10,5). Play: Coins spawn, collect them, score increases!
Polish: Add Sprite Renderer colors (Player=blue, Coin=yellow). Window > Package Manager > 2D Sprite if needed.
Build and Export the Game
# Via Unity Editor:
# File > Build Settings > PC/Mac/Linux Standalone > Add Open Scenes
# Player Settings > Company/Name/Product
# Build > Choose 'Builds' folder
# Or CLI (optional):
/Applications/Unity/Hub/Editor/2023.2.20f1/Unity.app/Contents/MacOS/Unity \
-buildTarget Win64 -executeMethod AutoBuilder.BuildPlayer -quit -batchmodeBuild Settings compiles a playable exe. Add icon (Player Settings > Icon). Test outside Editor. Pitfall: Forgetting scenes → empty build. CLI for CI/CD.
Best Practices
- Version Control: Git init, Unity .gitignore (Assets, Library, etc.). Commit after each script.
- Layers/Physics: Player layer 'Player', Coin 'Collectible' → Layer Collision Matrix for optimization.
- Prefabs: Make everything reusable a prefab (Player, Coin, UI).
- Input System: Migrate to new Input System (Package Manager) for multi-platform.
- CPU Profile: Window > Analysis > Profiler for <16ms/frame.
Common Errors to Avoid
- NullReference: Check GetComponent() and Inspector drags. Console > Clear on Play.
- Physics Glitch: Use FixedUpdate for forces, Update for input. Gravity Scale=0 if unneeded.
- UI Not Visible: Canvas Scaler > UI Scale Mode = Scale With Screen Size.
- Build Fails: Scripting Backend = IL2CPP for perf, Api Compatibility .NET Standard 2.1.
Next Steps
- Unity Learn: 2D Platformer
- Unity C# Documentation
- Our Advanced Unity Learni Courses: multiplayer, shaders.
- GitHub example project: fork this tutorial!