Unity C# Scripting for Beginners

Learn Unity C# scripting from zero: scripts as components, Start and Update, Inspector variables, input, movement, collisions, prefabs and common errors.

Inicio › Guías › Unity C# Scripting for Beginners

Illustration: Unity C# Scripting Basics

Unity C# scripting means writing small C# files, called scripts, and attaching them to GameObjects as components. Each script tells one object what to do: move, react to a hit, keep score or spawn enemies. This guide teaches the C# you actually need in Unity 6, from Start and Update to collisions and prefabs, with short examples you can paste in, run and change.

Time needed2 to 3 hours with practice
CostFree
You needUnity 6 and a code editor
Skill levelBeginner, no coding needed

How Unity scripts work

In Unity, a script is a component, just like a Rigidbody 2D or a Sprite Renderer. You write it once and attach it to any GameObject that should behave that way. One script per job keeps things simple: a Health script, a Spinner script, a Spawner script.

To make one, right-click in the Project window and choose Create > Scripting > MonoBehaviour Script. Type the name straight away, for example Health. Unity uses that name for the class, which is the blueprint inside the file. Keep the file name and class name the same to avoid confusing errors.

Your script inherits from MonoBehaviour, Unity's base class for components. Inheriting means your script gets all of MonoBehaviour's abilities for free. One of them: Unity calls special methods in your script at the right moment. You never call Start or Update yourself. Unity does. The MonoBehaviour page in the Unity manual has the full details.

When Unity calls your script's methods Awake runs when the object is created, then Start runs before the first frame. After that a loop repeats while the game runs: FixedUpdate for physics steps, then Update once per frame, then LateUpdate after all Updates. When the object is removed, OnDestroy runs. Awake object is created Start before first frame repeats while the game runs FixedUpdate steady physics steps Update once per frame LateUpdate after all Updates OnDestroy object is removed
The order Unity calls the most common methods. Set things up in Awake or Start, and run game logic in Update.

Use Awake or Start to set things up, like finding a component. Use Update for anything that checks or changes every frame. LateUpdate runs after every Update, which suits a camera that follows the player. A frame is one picture on the screen, and games draw 30 to well over 100 of them each second.

Variables and the Inspector

A variable is a named box that holds a value. In C#, every variable has a type that says what kind of value fits in the box. Here is a reusable Health script that shows the four types you will use most.

using UnityEngine;

// Gives any GameObject hit points.
public class Health : MonoBehaviour
{
    [Header("Settings")]
    [SerializeField] string displayName = "Slime"; // text
    [SerializeField] int maxHealth = 3;        // whole number
    [SerializeField, Range(0f, 2f)]
    float hurtCooldown = 0.5f;                 // decimal number
    [SerializeField] bool destroyAtZero = true; // true or false

    int health;               // private: only this script sees it
    float lastHurtTime = -10f;

    void Start()
    {
        health = maxHealth;
    }

    public void TakeDamage(int amount)
    {
        // Ignore hits that arrive too fast.
        if (Time.time - lastHurtTime < hurtCooldown) return;
        lastHurtTime = Time.time;

        health -= amount;
        Debug.Log(displayName + " HP: " + health);

        if (health <= 0 && destroyAtZero)
        {
            Destroy(gameObject);
        }
    }
}
  • int holds whole numbers, like lives or coins.
  • float holds decimals, like speed or time. Write them with an f, as in 0.5f.
  • bool is true or false, like "is the door open?"
  • string holds text in quotes.

[SerializeField] shows a private variable in the Inspector, so designers can tweak it without touching code. [Header] adds a heading and [Range] turns a number into a slider. You will also see public variables in tutorials. They appear in the Inspector too, but any other script can change them, which causes surprise bugs later.

Watch out

Once a value is saved in the Inspector, the Inspector wins. If you change maxHealth = 3 to 5 in code, objects already in your scene keep 3. Change it in the Inspector, or right-click the component and choose Reset.

Variables can also hold other objects. A field like [SerializeField] Transform target; shows an empty slot. You drag a GameObject into it, and your script can then read that object's position. This is called a reference, and it is the safest way for scripts to find each other.

Functions, if statements and Debug.Log

A function (also called a method) is a named set of steps. TakeDamage(int amount) is one. The part in brackets is a parameter, a value you hand over when you call it, like TakeDamage(2). Because it is public, other scripts can call it.

An if statement runs code only when a condition is true. Conditions use comparisons: == means "is equal to", != means "is not equal", and <= means "is less than or equal". You can join conditions with && (and) or || (or). return leaves the function early.

Debug.Log prints a message to the Console. It is your best friend while learning. When something doesn't work, log the values you expect and see what really happens. Remove extra logs when you are done, because thousands of messages slow the editor down.

Update, FixedUpdate and Time.deltaTime

Update runs once per frame. A fast computer runs more frames than a slow one, so "move 1 unit every frame" means different speeds on different machines. The fix is Time.deltaTime: the number of seconds since the last frame. Multiply by it, and your speeds become "per second".

using UnityEngine;

// Spins the object at the same speed on any computer.
public class Spinner : MonoBehaviour
{
    [SerializeField] float degreesPerSecond = 90f;

    void Update()
    {
        // deltaTime = seconds since the last frame.
        float step = degreesPerSecond * Time.deltaTime;
        transform.Rotate(0f, 0f, step);
    }
}

FixedUpdate runs at a steady rate that matches the physics engine, 50 times a second by default. It can run zero, one or several times in one frame. Put physics changes, like setting a Rigidbody's speed, in FixedUpdate. Check button presses in Update, or a quick tap can slip between two physics steps.

Reading input and moving objects

New Unity 6 projects use the Input System package. It comes with an InputSystem_Actions asset full of ready-made actions, such as Move and Jump. An action is a named control that works on a keyboard, a gamepad and more, so your code doesn't care which device the player uses.

This side-view player walks with Move and jumps with Jump:

using UnityEngine;
using UnityEngine.InputSystem;

// Side-view player: walk with Move, jump with Jump.
public class Jumper : MonoBehaviour
{
    [SerializeField] float walkSpeed = 5f;
    [SerializeField] float jumpForce = 7f;

    Rigidbody2D body;
    InputAction moveAction;
    InputAction jumpAction;

    void Start()
    {
        body = GetComponent<Rigidbody2D>();
        moveAction = InputSystem.actions.FindAction("Move");
        jumpAction = InputSystem.actions.FindAction("Jump");
    }

    void Update()
    {
        // Button presses: check them every frame.
        if (jumpAction.WasPressedThisFrame())
        {
            body.AddForce(Vector2.up * jumpForce,
                ForceMode2D.Impulse);
        }
    }

    void FixedUpdate()
    {
        // Change only the sideways speed; gravity does the rest.
        float x = moveAction.ReadValue<Vector2>().x;
        body.linearVelocityX = x * walkSpeed;
    }
}

Attach it to a Square with a Rigidbody 2D and a Box Collider 2D, and add a long Square with a Box Collider 2D below it as the floor. Press Play, walk with A and D or the arrow keys, and jump with Space. GetComponent<Rigidbody2D>() finds another component on the same GameObject. AddForce with ForceMode2D.Impulse gives one instant kick, which is perfect for a jump. This version can jump in mid-air. Adding a ground check is a great first exercise.

There are two ways to move things in Unity. Change the Transform directly, such as transform.Translate(Vector3.right * Time.deltaTime), for objects that ignore physics, like a floating menu arrow. Use the Rigidbody 2D for anything that should collide, fall or get pushed. Mixing both on one object makes it jitter or pass through walls.

Good to know

Unity 6 renamed Rigidbody2D.velocity to linearVelocity, and added linearVelocityX and linearVelocityY. Code using Input.GetAxis comes from the older Input Manager and throws errors in projects set to the new Input System. Most older tutorials use both, so translate as you go.

Collisions and triggers

Unity tells your script when two objects touch, by calling special methods. Which one it calls depends on the setup.

Collision or trigger: which method runs? A decision chart. If both objects have a Collider 2D and at least one has a Rigidbody 2D, Unity checks Is Trigger. If Is Trigger is ticked, OnTriggerEnter2D runs and nothing is pushed. If not, OnCollisionEnter2D runs and the objects bump, which needs one Dynamic body. If either question is answered no, nothing happens. Both have a Collider 2D? nothing happens no yes One has a Rigidbody 2D? nothing happens no yes Is Trigger ticked? yes no OnTriggerEnter2D overlap, nothing is pushed OnCollisionEnter2D they bump (one body Dynamic)
Most "my collision doesn't work" problems come from one of these three questions.

Here is a Hazard script for spikes or lava. It works with both setups and uses the Health script from earlier:

using UnityEngine;

// Put this on spikes, lava or anything that hurts.
public class Hazard : MonoBehaviour
{
    [SerializeField] int damage = 1;

    // Solid colliders: the objects bump.
    void OnCollisionEnter2D(Collision2D collision)
    {
        Hurt(collision.gameObject);
    }

    // Is Trigger ticked: they only overlap.
    void OnTriggerEnter2D(Collider2D other)
    {
        Hurt(other.gameObject);
    }

    void Hurt(GameObject target)
    {
        // True only if the target has a Health script.
        if (target.TryGetComponent(out Health health))
        {
            health.TakeDamage(damage);
        }
    }
}

Notice how the scripts work together. Hazard doesn't care whether it hits a player or an enemy. Anything with Health can be hurt. Small scripts that each do one job are easier to test, fix and reuse.

Spelling matters here. OnTriggerEnter2D with a capital O and "2D" at the end is a Unity message. OnTriggerEnter without "2D" belongs to 3D physics, and in a 2D game it silently never runs. You can also check tags with other.CompareTag("Player") when only certain objects should react.

Prefabs and Instantiate

A prefab is a GameObject saved as a file in your Project window. Instantiate makes a live copy of it in the scene while the game runs. That is how games create bullets, enemies, coins and particles on the fly.

using UnityEngine;

// Drops a copy of a prefab every few seconds.
public class Spawner : MonoBehaviour
{
    [SerializeField] GameObject prefab;   // drag a prefab here
    [SerializeField] float interval = 2f; // seconds between drops
    [SerializeField] float range = 6f;    // how far left or right

    float timer;

    void Update()
    {
        timer += Time.deltaTime;
        if (timer >= interval)
        {
            timer = 0f;
            float x = Random.Range(-range, range);
            Vector3 spot = new Vector3(x, transform.position.y, 0f);
            GameObject copy =
                Instantiate(prefab, spot, Quaternion.identity);
            Destroy(copy, 10f); // remove it after 10 seconds
        }
    }
}
One prefab, many clones A Rock prefab saved in the Project window on the left. Instantiate creates three live copies on the right, each named Rock(Clone), in the running scene. A note says Destroy with a delay removes each clone later. Rock prefab saved in the Project window Instantiate() Rock(Clone) a live copy in the scene Rock(Clone) a live copy in the scene Rock(Clone) a live copy in the scene Destroy(copy, 10f) removes each clone later
Instantiate copies a prefab into the running scene. Clean up copies you no longer need, or the game slowly fills up.

To try it, make a small Circle with a Rigidbody 2D and a Circle Collider 2D, and drag it into the Project window to create the prefab. Delete it from the scene. Create an empty GameObject high above the floor, add Spawner, and drag the prefab into its Prefab slot. Press Play and watch rocks rain down. Add the Hazard script to the prefab, and the rocks will hurt anything with Health.

Common C# errors in Unity and how to fix them

Errors appear in red in the Console. Double-click one to jump to the exact line in your code editor. The message also shows the file and line, like Health.cs(21,9), meaning line 21. Always fix the first error first, because one mistake can cause several messages.

MessageWhat it meansHow to fix it
CS1002: ; expectedA line is missing its semicolon.Add ; at the end of that line, or the line above.
CS0246: type or namespace could not be foundA using line is missing, or a name has wrong capitals.Add the using line, such as using TMPro;, or fix the spelling.
CS0103: name does not exist in the current contextA typo, or the variable lives inside another function.Check the spelling and where you declared it.
CS0266: cannot implicitly convert float to intYou put a decimal into a whole-number box.Change the type or use Mathf.RoundToInt.
NullReferenceExceptionYour code used something that is empty.Fill the Inspector slot, or check GetComponent found it.
UnassignedReferenceExceptionAn Inspector slot was never filled.Drag the right object or prefab into the slot.
MissingReferenceExceptionThe object was destroyed, but code still uses it.Stop using it after Destroy, or check it first.
Can't add script componentThere are compile errors, or the names don't match.Fix all red errors, then match file and class names.

One more trap in newer versions. From Unity 6.6, new projects skip the "domain reload" when you press Play. In plain words, static variables keep their old values between test runs. Avoid static while learning, or reset those values yourself in Awake.

How to practice and what to learn next

Reading code is not enough. You learn by changing it and seeing what breaks. Try these small challenges with the scripts above:

  1. Reverse the spin. Make Spinner change direction when you press Jump. Hint: multiply the speed by -1.
  2. Add a ground check. Let Jumper jump only when touching an object tagged Ground. Hint: a bool set in OnCollisionEnter2D and OnCollisionExit2D.
  3. Speed up the rain. Make Spawner drop rocks faster over time, but never faster than every 0.3 seconds. Hint: Mathf.Max.
  4. Show the health. Display the player's health on screen with a TextMeshPro text.

Want a full project to practice on? Build the coin game in how to make a game in Unity. When you get stuck, an AI assistant can explain errors line by line, as long as you use AI the smart way. To read touch screens instead of keys, see how to make a mobile game in Unity. And to make your code feel fun, not just work, read game design basics.

For deeper study, Unity's free Junior Programmer pathway covers these topics with projects, and Microsoft's C# documentation explains the language itself. Unity 6 uses C# 9, so some newer C# features from general tutorials won't compile. Browse the Unity indie games on Mutop100 to see what solo developers build once these basics click.

FAQ

Is C# hard to learn for Unity?

No, not if you learn it in small pieces. You only need a handful of ideas to start: variables, functions, if statements and Unity's methods like Start and Update. Most beginners get a simple script working in their first week.

How long does it take to learn C# for Unity?

Most beginners can write simple scripts on their own after a few weeks of regular practice. Feeling comfortable usually takes two to three months of building small projects. You keep learning new parts for years, and that's normal.

Should I learn C# before Unity?

No, you can learn both at the same time. Seeing your code move something on screen makes the ideas stick. A general C# course is still useful later for deeper topics.

What version of C# does Unity 6 use?

Unity 6 uses C# 9. Features added in later C# versions, such as file-scoped namespaces, don't compile in Unity yet. If code from a website fails on modern syntax, this is often why.

Which code editor should I use for Unity?

Visual Studio Community on Windows, Visual Studio Code with Microsoft's Unity extension, or JetBrains Rider all work well. Rider is free for non-commercial projects. Pick your editor in Unity under Edit > Preferences > External Tools (Unity > Settings on a Mac).

Why doesn't my Unity script do anything?

Usually the script isn't attached to an object in the scene, or the object is disabled. Also check for red Console errors and method names with wrong capitals, such as update instead of Update. Unity only calls methods spelled exactly right.

Turn your scripts into a game players can find

Once your C# skills turn into a playable game, list it free in the Mutop100 indie games directory and let players vote for it.

List your game free
← Volver a Guías de MU Online