Skip to content
Back to skills

Unity

ASecurity

Unity is a cross-platform game engine for 2D, 3D, AR and VR games, scripted in C# with a component system, Universal Render Pipeline, UI Toolkit, Addressables and DOTS. Use when a user asks to write Unity C# scripts, build a player controller, use ScriptableObjects or Addressables, set up input, run headless builds in CI, or pick a Unity version and licence.

  • 142 stars
  • 0 votes
  • 0 copies
  • 4 views
  • Added September 6, 2026
developmentgoc#bashdockergitapi

Works with

  • terminal
  • cli
  • api

Security analysis

A100/100

Pro scans all 2 files and shows the line behind each finding

Scanned October 4, 2026

npx -y skills add TerminalSkills/skills --skill unity --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Unity?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Unity
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/terminalskills-unity/badge)](https://www.skillsdirectory.com/skills/terminalskills-unity)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: unity
description: >-
  Unity is a cross-platform game engine for 2D, 3D, AR and VR games, scripted in C# with a component system, Universal Render Pipeline, UI Toolkit, Addressables and DOTS. Use when a user asks to write Unity C# scripts, build a player controller, use ScriptableObjects or Addressables, set up input, run headless builds in CI, or pick a Unity version and licence.
license: Apache-2.0
compatibility: "Unity 6 (6000.x, 6.3 LTS) via Unity Hub on Windows, macOS or Linux; C# 9 scripting. Headless builds still need an activated licence."
metadata:
  author: terminal-skills
  version: "1.1.0"
  category: development
  tags:
    - unity
    - game-engine
    - c-sharp
    - mobile
    - cross-platform
---

# Unity

## Overview

Unity is a game engine and editor. A scene holds GameObjects; behaviour comes from components, mostly C# `MonoBehaviour` scripts. It builds to desktop, iOS, Android, WebGL, consoles and XR headsets. The current line is Unity 6 (version numbers `6000.x`; 6.3 is a long-term-support release). Projects created before Unity 6 keep working, but several APIs were renamed (see Guidelines).

Licensing as published on unity.com: Unity Personal is free for organisations with less than $200K revenue and funding in the last 12 months; Unity Pro (about $2,310 per seat per year) is required above that; Enterprise is for companies above $25M. The old Unity Plus plan no longer appears. Check the compare-plans page before quoting numbers.

Most agent work here is writing scripts and editor tooling under `Assets/`, so the examples are C#. Unity regenerates `.csproj` files and `Library/`; commit `Assets/`, `Packages/` and `ProjectSettings/`, not `Library/`, `Temp/` or `Logs/`.

## Instructions

### Install and open a project

Install Unity Hub from unity.com/download, then add an editor version (pick an LTS release) with the platform modules you need (Android Build Support, iOS, WebGL). Create projects from the Hub templates; the Universal 3D and Universal 2D templates use URP. Pin the editor version in `ProjectSettings/ProjectVersion.txt` and keep everyone on the same one.

### A character controller with the Input System

New Unity 6 projects use the Input System package. If a project is set to "Input System Package (New)" under Edit > Project Settings > Player > Other Settings > Active Input Handling, calling the legacy `Input.GetAxis` or `Input.GetKey` throws an `InvalidOperationException` at runtime. Use input actions, or set the option to "Both" for old code.

```csharp
// Assets/Scripts/PlayerController.cs
using UnityEngine;
using UnityEngine.InputSystem;

[RequireComponent(typeof(CharacterController))]
public class PlayerController : MonoBehaviour
{
    [Header("Movement")]
    [SerializeField] private float moveSpeed = 7f;
    [SerializeField] private float jumpHeight = 1.6f;
    [SerializeField] private float gravity = -25f;

    [Header("Input (assign actions from an Input Actions asset)")]
    [SerializeField] private InputActionReference move;   // Vector2
    [SerializeField] private InputActionReference jump;   // Button

    private CharacterController controller;
    private Vector3 velocity;

    private void Awake() => controller = GetComponent<CharacterController>();

    private void OnEnable()  { move.action.Enable();  jump.action.Enable(); }
    private void OnDisable() { move.action.Disable(); jump.action.Disable(); }

    private void Update()
    {
        if (controller.isGrounded && velocity.y < 0f)
            velocity.y = -2f;                       // keeps the controller grounded

        Vector2 input = move.action.ReadValue<Vector2>();
        Vector3 direction = transform.right * input.x + transform.forward * input.y;
        controller.Move(direction * (moveSpeed * Time.deltaTime));

        if (jump.action.WasPressedThisFrame() && controller.isGrounded)
            velocity.y = Mathf.Sqrt(jumpHeight * -2f * gravity);

        velocity.y += gravity * Time.deltaTime;
        controller.Move(velocity * Time.deltaTime);
    }
}
```

Use `Update` for input and non-physics logic, `FixedUpdate` for Rigidbody forces, and cache `GetComponent` results in `Awake`.

### ScriptableObjects for data

```csharp
// Assets/Scripts/WeaponData.cs
using UnityEngine;

[CreateAssetMenu(fileName = "NewWeapon", menuName = "Game/Weapon Data")]
public class WeaponData : ScriptableObject
{
    public string weaponName = "Iron Sword";
    public Sprite icon;
    public GameObject prefab;
    public float damage = 12f;
    public float attacksPerSecond = 1.5f;
    public float range = 2f;
    public AudioClip attackSound;
    [TextArea] public string description;
}
```

Create assets with Assets > Create > Game > Weapon Data and drag them into inspector fields. Do not write to a ScriptableObject at runtime for save data: changes persist in the Editor but not in builds. Copy values into plain C# classes and serialize those.

### Events between systems

```csharp
// Assets/Scripts/GameEvent.cs
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Events;

[CreateAssetMenu(menuName = "Game/Event")]
public class GameEvent : ScriptableObject
{
    private readonly List<GameEventListener> listeners = new();

    public void Raise()
    {
        for (int i = listeners.Count - 1; i >= 0; i--)   // safe if a listener unregisters
            listeners[i].OnEventRaised();
    }
    public void Register(GameEventListener l) => listeners.Add(l);
    public void Unregister(GameEventListener l) => listeners.Remove(l);
}

public class GameEventListener : MonoBehaviour
{
    [SerializeField] private GameEvent gameEvent;
    [SerializeField] private UnityEvent response;

    private void OnEnable()  => gameEvent.Register(this);
    private void OnDisable() => gameEvent.Unregister(this);
    public void OnEventRaised() => response.Invoke();
}
```

Put each `MonoBehaviour` in a file with the same name as the class (`GameEventListener.cs`), or Unity cannot attach it. The listing above shows two classes only for brevity.

### Addressables

Install the Addressables package from Package Manager, mark assets as Addressable, and set groups in Window > Asset Management > Addressables > Groups. Load and release explicitly:

```csharp
using System.Threading.Tasks;
using UnityEngine;
using UnityEngine.AddressableAssets;
using UnityEngine.ResourceManagement.AsyncOperations;

public class LevelLoader : MonoBehaviour
{
    private GameObject currentLevel;

    public async Task LoadLevel(string levelKey)
    {
        AsyncOperationHandle<GameObject> handle = Addressables.InstantiateAsync(levelKey);
        await handle.Task;
        if (handle.Status == AsyncOperationStatus.Succeeded)
            currentLevel = handle.Result;
        else
            Debug.LogError($"Could not load {levelKey}: {handle.OperationException}");
    }

    public void UnloadLevel()
    {
        if (currentLevel != null) Addressables.ReleaseInstance(currentLevel);  // not Destroy
        currentLevel = null;
    }

    public async Task UpdateRemoteContent()
    {
        var check = Addressables.CheckForCatalogUpdates(false);
        await check.Task;
        if (check.Result.Count > 0)
            await Addressables.UpdateCatalogs(check.Result).Task;
        Addressables.Release(check);
    }
}
```

### Headless builds in CI

Put the build method in an `Editor` folder and call it from the command line:

```csharp
// Assets/Editor/BuildScript.cs
using UnityEditor;
using UnityEditor.Build.Reporting;

public static class BuildScript
{
    public static void Build()
    {
        var options = new BuildPlayerOptions
        {
            scenes = new[] { "Assets/Scenes/Main.unity" },
            locationPathName = "Builds/Win64/Skyline.exe",
            target = BuildTarget.StandaloneWindows64,
        };
        BuildReport report = BuildPipeline.BuildPlayer(options);
        EditorApplication.Exit(report.summary.result == BuildResult.Succeeded ? 0 : 1);
    }
}
```

```bash
Unity -batchmode -nographics -quit -projectPath ./Skyline \
  -buildTarget StandaloneWindows64 -executeMethod BuildScript.Build \
  -logFile - -accept-apiupdate
```

The editor must be activated for batch mode (a Pro or Personal licence, activated with `-username`/`-serial` or a licence file from the Hub). Only one editor instance can open a project at a time. A non-zero exit code means a failure; read the log. For CI, the open-source GameCI Docker images and GitHub Actions wrap this.

## Examples

### Example 1: Add a jump to the player

Request: "My character walks but can't jump, and I get an InvalidOperationException about UnityEngine.Input."

The project uses the new Input System, so legacy `Input` calls throw. Replace them with the `PlayerController` above: create an Input Actions asset with a `Move` (Vector2) and a `Jump` (Button) action, assign both to the component, and press Play. The error disappears and Space or the gamepad south button now jumps about 1.6 units.

### Example 2: Load a level from a CDN

Request: "Ship levels as downloadable content so the app stays under 150 MB."

Mark each level prefab Addressable in a "Levels" group, set the group's build and load paths to Remote, host the build output folder on a CDN, and call `LevelLoader.UpdateRemoteContent()` on startup followed by `LoadLevel("Level_03")`. The first launch downloads the catalog and bundles; later launches use the cache.

## Guidelines

- Pitfalls with Unity 6: `Rigidbody.velocity` is now `linearVelocity` (and `drag` is `linearDamping`); `FindObjectOfType` is deprecated in favour of `FindFirstObjectByType`.
- Avoid `Resources` folders and `Find` calls in `Update`; pool frequently spawned objects instead of `Instantiate`/`Destroy` to avoid garbage-collection spikes.
- Release every Addressables handle you create, or the memory stays loaded.
- Use the Profiler and Frame Debugger on real target hardware early; the Editor is not representative for mobile.
- Split code into assembly definitions to cut script recompile time.
- URP suits mobile, VR and most projects; HDRP is for high-end PC and console.
- Do not edit `.meta` files or `Library/` by hand, and keep `.meta` files in version control; missing ones break asset references.
- Pre-Unity 6 or Unity 2022 tutorials show `UnityEngine.Input` and the Built-in Render Pipeline; check the project's editor version first.
- For a code-first alternative with a smaller footprint see Godot; Unity is the better fit when you need its console, XR or asset-store ecosystem.

Files in this skill

  • SKILL.md7 KB
  • _scores.json1.6 KB

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…