User Tools

Site Tools


notes:unity3d:engine

Editor

Hot key Function
Q Pan
W Move
E Rotate
R Scale
T Rect Tool (used with 2D graphics)
combines movement, rotation, and scaling
F Reset the view on the currently selected object
Alt Switch to the hand tool in the orbit mode while a transform tool is selected
Ctrl+Alt Switch to the hand tool in the move mode while a transform tool is selected
Shift+Ctrl+N Create an empty object
Ctrl+9 Access the Asset Store (Window > Asset Store)

The full list of Unity hot keys can be found here.

Hand tool

Hand tool mode
(navigation maneuver)
Keyboard/mouse
Move (translate camera) Click-and-drag
Orbit (rotate camera) Alt + click-and-drag
Zoom (scale camera) Alt + right-click-and-drag
Pan Ctrl + click-and-drag
Look around Ctrl + right-click-and-drag
Fly-through right-click + W (forward), S (backward), A (left), D (right), Q (up), E (down)

View gizmo

  • Move to the scene top-down view:
Press the green Y
  • Shift + MiddleCube switches the scene view camera to Perspective view.
  • You can switch to Ortographic view by clicking on the text below gizmo.
  • When orbiting in Ortographic view, you are automatically switched to Isometric view.
  • When in Isometric view, press the white MiddleCube to go back to the Perspective view.
  • Look at the plane from above by clicking 'y' in the view gizmo.

Global and local mode

  • Use Global mode when you want to position an object relative to the scene. In the Global mode, the object's axes align with the scene axes shown in the view gizmo.
  • Use Local mode when you want to position an object relative to its local coordinate system.

Editing in the scene view

  • To snap an object to a position, hold the Ctrl key while moving an object. This makes it easier to align objects.

Game Object

A GameObject acts as a container for components. A component is a class derived from MonoBehaviour. It can be attached to a GameObject to change its behaviour.

All Unity primitive objects, cubes, spheres, capsules, have a standard size, they are either 1x1x1 or 1x2x1 Unity units.

What How
Create an object, for example a cube GameObject > 3D Object > Cube
-or-
Hierarchy view > Create > 3D Object > Cube
Duplicate an object Select the object in Hierarchy view > Ctrl+D -or- Edit > Duplicate
-or-
Right-click the object in Hierarchy view > Duplicate
Scale an object Select the Scale tool > Drag the axis handle
-or-
Inspector view > Transform component > Click and drag the title of the field X, Y, or Z
-or-
Type a number directly in to the field
Reset the object's Transform component Inspector view > Transform component > Gear menu > Reset
Focus on an object in Scene view - Select the object in Hierarchy view or Scene view.
- Hover the cursor over Scene view.
- Press F -or- Edit > Frame Selected
-or-
- Double-click the object in Hierarchy view
Add a custom component (a script) to an object Drag-and-drop the script onto the object

Finding

Use GameObject.Find or GameObject.FindGameObjectWithTag methods to find an object in the scene. Keep in mind that these methods may be expensive. It means that you should call them during events such as Start and Awake rather than Update.

GameObject.FindGameObjectWithTag performs better than GameObject.Find or Object.FindObjectsOfType because internally Unity converts the tag parameter to a numerical value which speeds up comparison.

Method Description
GameObject.FindWithTag(string tag)
-or-
GameObject.FindGameObjectWithTag(string tag)
returns the first occurence of an object with a matching tag
GameObject.FindGameObjectsWithTag(string tag) returns an array of objects with a matching tag
GameObject.Find(string objectName) returns the first occurence of an object with the given name (case sensitive)
returns null if no object is found with the given name
Object.FindObjectsOfType<ObjectType>() return an array of active components of a given type
// Search the scene for the first occurence of an object with the tag "Enemy".
GameObject enemy = GameObject.FindWithTag("Enemy");
 
// Search the scene for all objects with the tag "Enemy" and return them in an array.
GameObject[] enemies = GameObject.FindGameObjectsWithTag("Enemy");
 
// Search the scene for the first occurence of an object with the name "Player" (two equivalent statements).
GameObject obj = GameObject.Find("Player");
GameObject obj = GameObject.Find("Player").gameObject;
 
// Find a game object by its name and call a method MyMethod of its component MyScript.
GameObject.Find(name).GetComponent<MyScript>().MyMethod();
// Get a list of all colliders in the scene.
Collider[] cols = Object.FindObjectsOfType<Collider>();
 
// Get a list of all transforms in the scene.
Transform[] objects = Object.FindObjectsOfType<Transform>();

Comparing

Compare tags of two GameObjects:

// gameObject - a reference to this GameObject
// obj - a reference to another GameObject
bool theSameTag = gameObject.CompareTag(obj.tag);

Determine if two GameObjects represent the same object:

bool theSameObjects = (gameObject.GetInstanceID() == obj.GetInstanceID());

Destroying

Actual object destruction is delayed until after the Update method but it is always done before rendering.

// Find and destroy a GameObject.
GameObject obj = GameObject.Find("Sphere");
Destroy(obj);
 
// Destroy a GameObject after 3 seconds.
Destroy(gameObject, 3);
 
// Destroy a MeshRenderer component of a GameObject.
Destroy(GetComponent<MeshRenderer>());
 
// Destroy an object immediately. This function should only be used in Editor, not in a game.
DestroyImmediate(gameObject);

Parent/Child

Attach an object to another object as a child through the Transform component:

// 'childObj' and 'parentObj' are instances of GameObjects.
childObj.transform.parent = parentObj.transform;

Iterate all children attached to a GameObject:

for(int i=0; i<transform.childCount; ++i)
    Debug.Log(transform.GetChild(i).name);

Get the position of a GameObject's child:

Vector3 pos = transform.FindChild(childName).position;

Activate/Deactivate

Activating GameObjects (activeSelf, activeInHierarchy)

Use the SetActive method to activate a GameObject. Note that the GameObject's Update method is called only if the object is active.

Determine if a GameObject with a tag 'Container' has any active children:

GameObject container = GameObject.FindWithTag("Container");
 
bool anyActive = false;
foreach (Transform child in container.transform)
{
    if (child.gameObject.activeSelf)
    {
        anyActive = true;
        break;
    }
}

Deactivate an object if it's active:

GameObject obj = GameObject.FindWithTag("MyObject");
if (obj.activeSelf)
    obj.SetActive(false);

DontDestroyOnLoad

Use the DontDestroyOnLoad method to make a GameObject persistent between the scenes:

void Start()
{
    DontDestroyOnLoad(gameObject);
}

Keep in mind that all child objects will survive with the persistent object as well as any assets or resources it uses, such as meshes, textures, and sounds. For this reason, many persistent objects are empty GameObjects with no children, featuring only the basic components.

Also, be careful not to create duplicates of persistent objects when reloading or returning to a scene in which the persistent object was created. A remedy to that is to create your persistent object as a singleton.

GetComponent

Use the GetComponent method if you need to access a single component and you know its data type. GetComponent returns a reference to the first component of a matching type attached to a GameObject.

Rigidbody rb = GetComponent<Rigidbody>();
Transform trans = GetComponent<Transform>();
CharacterController cc = GetComponent<CharacterController>();
MyScript script = GetComponent<MyScript>();

Use the MonoBehaviour.Invoke method to call a method of another component. The second argument specifies the time (in seconds), after which the method should be invoked:

// Get a reference to a component.
MyScript script = GetComponent<MyScript>();
 
// Call the OnSave method on the MyScript component.
script.Invoke("OnSave", 0.5f); // call OnSave after half a second

In this example, the handler is any component provided via Inspector:

public class CustomComponent : MonoBehaviour
{
    public MonoBehaviour handler;
 
    void Start()
    {
        handler.Invoke("OnSave", 0.0f);
    }
}

Two ways of testing if a component is attached to a GameObject:

// Method #1
if (obj.GetComponent<MyComponent>())
{
    // ...
}
 
// Method #2
if (obj.GetComponent<MyComponent>() != null)
{
    // ...
}

Use the GetComponents method to return references to all components of a given type attached to a GameObject.

// Component is a base class of all components.
Component[] all = GetComponents<Component>();
 
// Get all MyScript components.
MyScript[] scripts = GetComponents<MyScript>();

Interobject Communication

Use GetComponentsInChildren and GetComponentsInParent if you need to access components outside of a GameObject.

  • GetComponentsInChildren - returns refrences to all components in all children of a GameObject.
  • GetComponentsInParent - returns refrences to all componets in an object's parent.

Another way of calling a method on another object is to access a script attached to that object:

RaycastHit hit;
if (Physics.Raycast(ray, out hit))
{
    // Retrieve the object the ray hit.
    GameObject obj = hit.transform.gameObject;
 
    // Access a script on the object.
    EnemyObject target = obj.GetComponent<EnemyObject>();
 
    if (target != null)
    {
        // Call a method of the target.
        target.Die();
    }
}

Physics

Rigidbody

To use physics a game object needs a Rigidbody component attached.

Standard rigidbodies are expected to be moved using physics forces.

Kinematic rigidbodies are expected to be moved using their transforms. They do not react to physics forces such as gravity. Kinematic rigidbodies are useful for objects like elevators and moving platforms as well as triggers such as collectables that need to be moved by their transform.

Example: Move an object using forces:

Rigidbody rb = GetComponent<Rigidbody>();
rb.AddForce = new Vector3(x,y,z); // causes the game object to move
rb.velocity = new Vector3(x,y,z);
rb.position = new Vector3(x,y,z);
rb.rotation = Quaternion.Euler(x,y,z);

Example: Move a game object by adding a force to its Rigidbody:

using UnityEngine;
 
public class PlayerController : MonoBehaviour
{
    // The parameter to control the speed of the GameObject.
    public float speed;
 
    private Rigidbody rb; // a rigidbody component associated with the GameObject
 
    private void Start ()
    {
        // Grab the rigidbody component attached to the GameObject.
        rb = GetComponent<Rigidbody>();
    }
 
    private void FixedUpdate ()
    {
        // Get input from a keyboard.
        float moveHorizontal = Input.GetAxis("Horizontal");
        float moveVertical = Input.GetAxis("Vertical");
 
        // Determine the force vector.
        Vector3 movement = new Vector3(moveHorizontal, 0.0f, moveVertical);
 
        // Apply the force to the GameObject's rigidbody.
        rb.AddForce(movement * speed);
    }
}

Example: Push a game object when it is clicked:

using UnityEngine;
 
public class MoveObject : MonoBehaviour
{
    Rigidbody rb;
 
    private void Start()
    {
        rb = GetComponent<Rigidbody>();
    }
 
    private void OnMouseDown()
    {
        // transform.forward is a shortcut for the z-axis in the local space of an object.
        // ForceMode.Force is a default value. It indicates that continuous changes should be applied.
        rb.AddForce(-transform.forward * 80, ForceMode.Force); // reverse the object's local z-axis
    }
}

The Interpolate settings can help smooth movement of a game object. If a game object appears to jitter when being moved by its Rigidbody, use the Interpolate settings to smooth its movement. The Interpolate setting will smooth the movement based on the game object's position in the previous frame. The Extrapolate setting will use the predicted position in the next frame.

The Sleeping Mode controls how the Rigidbody sleeps to save processing time. Never Sleep disables sleeping. Start Awake makes sure the Rigidbody is awake when instantiated. Start Asleep initially sets the Rigidbody to sleep but it can be woken by collisions.

The Collision Detection settings control the type of collision detection used with a Rigidbody. The default is Discrete. It is best to use discrete collision detection. Continuous is for fast moving game objects and collisions. Continuous collision detection may register a collision between objects' updates.

Static Colliders and Dynamic Colliders

  • Collider is a component that allows the game object to react to other colliders provided that one of the game objects has a rigidbody component attached.
  • Static colliders are non-moving objects (for example, a wall or a floor). Any game object with a collider but no rigidbody is expected to be static.
  • Dynamic colliders are moving objects (for example, a car). Any game object with a collider and a rigidbody is dynamic.

As a performance optimization, Unity calculates volumes of all static colliders in a scene and stores this information in a static collider cache. This makes sense as static colliders shouldn't move, and this saves recalculating this information every frame.

If you move, rotate, or scale a static collider, Unity recalculates all the volumes again and updates the cache. This consumes resources.

You can move, rotate or scale dynamic colliders as often as you want and Unity won't recache any collider volumes. Unity expects the dynamic colliders to be moved. You need to indicate which colliders are dynamic by attaching a rigidbody component to the game object.

Trigger Colliders

The physics engine does not allow two collider volumes to overlap. When the engine detects that any two or more colliders will overlap, it analyses the objects' speed, rotation, and shape and calculates a collision. Only dynamic objects are affected by the collision. The static objects are not affected.

The physics engine allows the game object that has a collider configured as a trigger to overlap the other object's collider volume but does not cause a collision. It means that the collider no longer stops other objects from passing through.

You can detect the contact with the trigger using the OnTriggerEnter event. Another object's collider is passed to OnTriggerEnter as the other reference.

In the same way as standard collisions, one of the objects must have a rigidbody. It's standard practice to make sure that your trigger colliders are static objects, meaning they will not be moved by the physics engine. It means that usually you will make a trigger and then pass a rigidbody through it.

The intention with the trigger collider, also known as a trigger zone, is that you can call code without the objects in your game physically colliding.

Example: Let's say, we have two game objects: a Player and an Enemy. The Enemy has an “Enemy” tag assigned and both objects have rigidbodies (because they are dynaminc) and appropriate colliders attached. Additionally, the Enemy's rigidbody is kinematic i.e., its movement is controlled by its transform rather than physical forces.

Player:
Enemy:

This is how to configure collisions between these two objects:

1. Configure the Enemy's Collider component as a trigger by selecting the Is Trigger checkbox:

Example: 2. Create a collision detection script and attach it to the Player:

using UnityEngine;
 
public class CollisionDetection : MonoBehaviour
{
    void OnTriggerEnter(Collider other)
    {
        if (other.gameObject.CompareTag("Enemy"))
        {
            Debug.Log("Crash!");
 
            Destroy(other.gameObject); // destroy the enemy
 
            // other.gameObject.SetActive(false); // deactivate the enemy
        }
    }
}

Tips

  • If you want to set infinity for Drag and Angular Drag, type inf. It will be turned into Infinity.
  • Prevent a game object from sinking into a floor by seting Freeze Position Y and Freeze Rotation X and Z.
  • Rigidbodies Overview - mass, drag, angular drag, gravity, isKinematic, interpolate (solving jittering), the types of collision detection, constraints, PhysicsManager.
  • Colliders - OnCollisionEnter, OnCollisionStay, OnCollisionExit
  • Triggers - OnTriggerEnter, OnTriggerStay, OnTriggerExit

Camera

Cameras Overview

What How
Set the position of the camera Lift the camera up by 10 units and tilt it down by 45 degrees.

// Get a reference to the first enabled camera tagged "MainCamera".
Camera mainCamera = Camera.main;
 
// Get a GameObject associated with the main camera.
GameObject cameraObj = Camera.main.gameObject;

Two methods of obtaining the width and the height of an orthographic camera:

Camera cam = Camera.main;
float height = 2f * cam.orthographicSize;
float width = height * cam.aspect; // cam.aspect = Screen.width / Screen.height
Camera cam = Camera.main;
Vector3 halfSize = cam.ScreenToWorldPoint(new Vector3(
    cam.pixelWidth,
    cam.pixelHeight,
    cam.nearClipPlane));
float height = 2f * halfSize.y;
float width = 2f * halfSize.x;

Make the camera follow a player GameObject:

using UnityEngine;
 
// Attach the CameraConroller script to the camera.
 
public class CameraConroller : MonoBehaviour
{
    // Our player object. It needs to be attached in the camera's Inspector view.
    public GameObject player;
 
    // A difference between the current position of the camera and the position of the player.
    private Vector3 offset;
 
    void Start ()
    {
        // Calculate the offset by subtracting the position of the player from the position of the camera.
        // The offset is constant.
        offset = transform.position - player.transform.position;
    }
 
    void LateUpdate ()
    {
        // Move the camera to align with the player. This means as we move our player, before displaying what
        // the camera can see, the camera is moved in to a new position aligned with the player.
        // Because this code is called in LateUpdate, we know that the player has already moved.
        transform.position = player.transform.position + offset;
    }
}

2D Games

  • If the sprite's Pixels Per Unit is set to 100 then 100 pixels in the image are 1 unit in Unity i.e., 1 pixel is 0.01 units.
  • The camera's orthographic Size is measured from the center of the screen to the top.
  • The SpriteRenderer component determines the displayed sprite asset (Sprite property) and its tint color (Color property).

An example of setting a pixel-perfect graphics: Let's say our screen dimensions are 1024×768. It means that the camera height should be 768 / 2 = 384 pixels. We need to divide it by the sprite's Pixel Per Unit which is 100 by default. This gives us the camera size 384 / 100 = 3.84 units. If you set the resolution of the game to 1024×764, you will get the pixel-perfect graphics.

  • CameraSize = ScreenHeight / 2 / PixelsPerUnit
  • ScreenHeight = CameraSize * PixelsPerUnit * 2
  • PixelsPerUnit = ScreenSize / (CameraSize * 2)

Assign a sprite image programmatically. Assumption: the gameObject has a SpriteRenderer component attached:

[SerializeField] private Sprite image; // a reference to the Sprite asset
 
void Start()
{
    // Set the sprite for the gameObject's SpriteRenderer component.
    GetComponent<SpriteRenderer>().sprite = image; 
}

Materials

Use a material to add colour or texture to a model.

What How
Create a material Project view > Material
-or-
Assets > Create > Material
Set the color of a material - Inspector view > Main Maps section > Albedo
- Click on the Albedo's colour field to open a colour picker
Preview a material Make sure that the preview window is open:
Apply a material to an object Drag the material from Project view on the object in Scene view or Hierarchy view.
-or-
Drag a material from Project view onto a slot in the object's Mesh Renderer component.
Inspect a mesh material Materials are associated with the Mesh Renderer component of a game object. Expand the Mesh Renderer to see the materials applied:
View properties of a material Select the material in the Project tab.
-or-
Select an object in the Hierarchy tab. In the Inspector, at the bottom of the game object's components, there are properties of associated materials. This part of inspector describes the actual material rather than an instance associated with the GameObject.
Change the shininess of a material Adjust the Smoothness property (smoother surfaces are more shiny)

Lighting

Lighting Overview

Types of lighting in Unity:

  • Realtime (dynamic lighting) - calculated in real-time while the game is running. By default, directional, spot, and point lights are realtime.
  • Precomputed (baked GI lighting and precomputed realtime GI) - calculated offline and saved to a texture lightmap. The texture is then applied to the static objects in the scene. By default, both Precomputed Realtime GI and Baked GI are enabled in Unity's Lighting panel. This overrides any settings configured per-light.

Types of light sources:

  • Directional lights (the sun)
    • do not have a source position, they can be placed anywhere in your scene
    • do not diminish over distance
    • rotating the directional light affects the visual result
    • shadows cast by directional lights look the same, regardless of their position relative to the source
  • Point lights (a light bulb)
    • illuminates objects based on the light's position in the scene
    • rotation has no influence on the light as this light shines equally in all directions
    • the intensity of point lights diminishes quadratically from full intensity at the centre of the light, to zero at the limit defined by the Range property
    • enabling shadows for point lights can be expensive
    • point lights may create light 'leaks' through walls and floors and therefore lights must be carefully placed to avoid such problems. This however is not a problem when using Baked GI.
    • note that other than Range, the settings for point lights are the same as for directional lights
  • Spot lights (a flashlight, headlamps on a car, a street light)
    • point in a direction based on its rotation and illuminates all objects within a cone
    • respond to both rotation and position
    • similar to point lights, spot lights may travel through geometry bounce on the other side.
  • Area lights (a ceiling striplight or a backlit panel)
    • defined as rectangles from which light is emitted in all directions, from one side only (the object's +Z direction)
    • useful when you wish to create soft lighting effects
    • only available in Baked GI. For a similar soft lighting effect you can use Emissive Materials which are supported by Precomputed Realtime GI.

Commonly used properties of light sources:

  • Intensity - controls the brightness of the light. Intensity is independent of range.
  • Color - controls the color of the light.
  • Range - determines how far a light is emitted from the centre of an object holding the light component.
  • Spot angle - determines the angle of the cone used by the spot light in degrees.
What How
Show Lighting panel Window > Lighting > Settings
Set properties of ambient lighting Lighting panel > Scene tab > Environment Lighting section
Change the per-light baking mode Light component > Mode > Realtime (default), Baked, or Mixed
Mark an object as Static for Precomputed Realtime GI 1. Inspector view > Check the Static checkbox
2. Drop-down to the right of the Static checkbox > Lightmap Static
Make an object visible to Baked Reflection Probes 1. Inspector view > Check the Static checkbox
2. Drop-down to the right of the Static checkbox > Reflection Probe Static
Set automatic lighting precompute Lighting panel > Scene tab > Check the Auto Generate checkbox (at the bottom)
Set the color space Edit > Project Settings > Player > Other Settings section > Color Space (Linear or Gamma)
Configure a material as an emissive material Inspector > Select 'Standard' shader > Check the Emission checkbox
Add a reflection probe to a scene GameObject > Light > Reflection Probe
Add a light probe group to a scene GameObject > Light > Light Probe Group
Clear GI cache Edit > Preferences > GI Cache > [Clean Cache]

Use Baked GI and Gamma Color Space for mobile
Use Precomputed Realtime GI and Linear Color Space for PC and games consoles

Emissive materials

  • allows static (Lightmap Static) objects to emit light
  • use Light Probes if you need dynamic, or non-static geometry, to pick up light from emissive materials

Cookies

  • A cookie acts like a mask in front of the light to create a patterned shadow.
  • Cookies use the alpha channel of a texture to give the light a projected shadow pattern.
  • Cookies must be a 2D texture when working with spot and directional lights.
  • Cookies must be a cube map when using a point light (because the point light shines in all directions).
  • When using directional lights there is an option to change the cookie's size, scaling the pattern in the scene.
  • Cookies do not work with area lights. When using baked lighting cookies only work with spot lights and are ignored for baked point lights and baked directional lights.

Shadows

  • There are two types of shadows available: hard and soft shadows.
  • Strength sets the value of the darkness of the shadow.
  • Resolution is a quality setting.
  • Bias controls an offset value to optimise shadow rendering. It affects how far from objects the shadows will start.

Halo

  • The Draw Halo property of the Light component draws the default halo around the light.
  • Halos respond to both the range and intensity of the light.
  • To override the default halo add an individual Halo component:
    • Uncheck the Draw Halo property of the Light component.
    • [Add Component] > Effects > Halo

Flare

  • To render a flare, assign a flare asset to the flare slot of your light source.
  • Flares only respond to the intensity of the light.
  • A lens flare component can be attached directly to the game object:
    • [Add Component] > Effects > Lens Flare
    • Leave the flare property on the light source empty or there will be two flares rendered on the light.
  • A flare layer component must be attached to a camera for that camera to render a flare element.

Render Mode

  • There are two different methods of rendering dynamic lights: using vertex lighting and using per pixel lighting.
  • Normal mapping, light cookies and real time shadows are only rendered for pixel lights.
  • Spot light shapes and specular highlights are much better when rendered in pixel mode as well.
  • Limit the number of pixel lights: Edit > Project Settings > Quality > Pixel Light Count

Set a spot light in the current position of the scene camera:

  • Select the spot light in your scene view.
  • Ctrl + Shift + F
  • You should see a circle that is the bottom on the spot light's cone
  • Move the scene camera a little bit
  • You should see the spot light's cone

Tips & tricks

  • Set the rotation of your directional light to X=50 and Y=60 to give a better shape to 3D objects in the scene.
  • Position your rim light under the game object. This way it will only light contours of the object.
  • For a complete control of lighting set the ambient light to black.

Shaders

Shader Remarks
Standard - Physically Based Shader (PBS)
- Supports colors (Albedo), texture, transparency (Rendering Mode), and a normal map
- Displays the color of the material (including the texture)
- Applies basic light across the surface
Particles > Additive - Adds the color of a particle to the color behind it.
- Makes a background transparent.
Particles > Multiply - Makes everything darker. It gives an opposite effect to the Additive shader.
Diffuse - A standard flat look for rendering 3D meshes
- Supports a texture
Bumped Diffuse - Use it when you have a normal map specified.
Bumped Specular - Use it if you want a shiny surface. There is a Shininess parameter available.
Unlit > Texture - Makes an object independent from the lighting system.
- Displays the texture exactly as it is in the original image.
Legacy > Transparent > Diffuse - Supports the alpha channel

Prefabs

A prefab is an asset that contains a blueprint of a game object or game object family. It has components already attached and set up. A prefab instance is a copy of a prefab placed in a scene.

Create a prefab by dragging a game object from the Hierarchy view and dropping it in the Project view. This automatically saves the object as a prefab. Back in the Hierarchy the original object's name turns blue to signify that it's now linked to a prefab.

If you want to edit the prefab, first make changes on the original object. Then, to propagate changes from the object to the prefab, select GameObject > Apply Changes To Prefab from the main menu.

Alternatively, you may want to delete the original object and spawn the new object from the prefab.

The instantiation of a prefab creates a copy of a game object in the scene.

Meshes

To render a mesh we need two components:

  • The mesh filter holds the mesh data for the model and passes it to the mesh renderer for rendering on screen.
  • The mesh renderer takes the mesh model data from the mesh filter and renders it in the scene at the position, rotation and scale defined by the object's transform component.

The mesh renderer does not render skinned meshes. Skinned meshes are used for rendering characters. Characters are animated using bones and every bone affects part of the mesh. Unity uses a skinned mesh renderer for characters.

Gizmos

What How
Turn off gridlines in Scene view Scene view > Gizmos > Uncheck Show Grid

Settings and Preferences

What How
Change the color of interface while in the play mode Edit > Preferences > Colors > Playmode tint > Select a color
Set the aspect ratio Game view > Select the aspect ratio from the drop-down list:
Manage tags Edit > Project Settings > Tags and Layers
Set/change the default script editor Edit > Preferences > External Tools > External Script Editor > Visual Studio -or- Mono Develop
Set a build target File > Build Settings > Select a platform > [Switch Platform]
Set build settings (aka player settings) File > Build Settings > [Player Settings]
-or-
Edit > Project Settings > Player

Input

What How
Access the input manager (a list of abstract input names
and the controls mapped to those names)
Edit > Project Settings > Input

Skyboxes

A skybox is a cube surrounding the camera with pictures of the sky on each side. As other textures, skybox images are first assigned to a material, and then applied to the scene.

What How
Access the default skybox MainMenu: Window > Lighting > Environmental Lighting panel:
Create a skybox material - Create a new material
- Set the Skybox > 6 Sided shader for the material
- Import six skybox textures (drag-and-drop them to the Project view or use Import New Asset
- Change the Wrap Mode of the imported textures to Clamp and press Apply

- Drag the textures to the appropriate slots of the skybox material
- Use the new skybox material in the scene (Window > Lighting > Environmental Lighting):
Toggle the skybox

Scripting

Events

  • Update is called before rendering a frame.
  • FixedUpdate is called before performing any physics calculations.
  • LateUpdate runs every frame, just like update, but it is guaranteed to run after all items have been processed in update. It's used for follow cameras, procedural animation, and gathering last known states.
Event Description
Awake Called before the Start event. The difference between Awake and Start is that Awake is always called at object creation and Start is called on the first frame in which the GameObject becomes active. If a GameObject starts the scene deactivated, then Start will not be called until the object is activated. For objects that are activated by default, Start is called at the beginning of the scene, after the Awake event. If you need to cache component references into local variables of a class then use the Awake event rather than Start. During the Start event, the assumption is that all local references to objects are already valid.
Start Called on the frame when a script is enabled just before any of the Update methods is called the first time. Start is called exactly once in the lifetime of the script.
Update Called once before rendering a frame for every active component on every active GameObject in the scene. It's useful for performing repetitive behaviors or functionality that must be updated over time, such as input events or mouse clicks. Note that the order in which Update events are invoked across all components for each frame is not guaranteed.
FixedUpdate Called in regular fashion with fixed time intervals between each call. It's useful for working with physics such as updating the velocity of your object.
LateUpdate Called once per frame just like Update but it is guaranteed to run after Update and FixedUpdate. It's useful for updating camera movement, performing procedural animation, and gathering last known states.

Input

GetAxis

Get input from a keyboard:

float moveHorizontal = Input.GetAxis("Horizontal");
float moveVertical = Input.GetAxis("Vertical");

Get a pressed key:

if (Input.GetKeyDown(KeyCode.R)) { }
if (Input.GetKeyDown(KeyCode.UpArrow)) { }

Transform

Use transforms to move kinematic rigidbodies.

Translation

Move an object back and forth along its local z-axis using arrow keys:

using UnityEngine;
 
public class MoveObject : MonoBehaviour
{
    public float speed = 10;
 
    private void Update()
    {
        if (Input.GetKey(KeyCode.UpArrow))
            transform.Translate(Vector3.forward * speed * Time.deltaTime);
        else if (Input.GetKey(KeyCode.DownArrow))
            transform.Translate(-Vector3.forward * speed * Time.deltaTime);
    }
}

Rotation

  • Transform.rotation - The rotation of an object in world space stored as a Quaternion.
  • Transform.localRotation - The rotation of an object relative to the parent stored as a Quaternion.
  • Transform.eulerAngles - The rotation as Euler angles in degrees. Unity converts Euler angles to and from quaternion values.

Spin an object by rotating its transform:

using UnityEngine;
 
public class Rotator : MonoBehaviour
{
    private void Update()
    {
        // Rotate the object's transform rather than setting its transform's rotation.
        // Multiply the rotation angles by Time.deltaTime to make the rotation smooth and frame rate independent.
        transform.Rotate(new Vector3(15, 30, 45) * Time.deltaTime);
    }
}

Rotate an object around its local y-axis (the “up” axis) using arrow keys:

using UnityEngine;
 
public class MoveObject : MonoBehaviour
{
    public float speed = 10;
 
    private void Update()
    {
        if (Input.GetKey(KeyCode.LeftArrow))
            transform.Rotate(Vector3.up, -speed * Time.deltaTime);
        else if (Input.GetKey(KeyCode.RightArrow))
            transform.Rotate(Vector3.up, speed * Time.deltaTime);
    }
}

Set the transform's rotation angles:

transform.localEulerAngles = new Vector3(0, 15, 0);

Renderer

Change the GameObject's color:

using UnityEngine;
 
public class ChangeColor : MonoBehaviour
{
    Renderer meshRenderer;
 
    private void Start()
    {
        meshRenderer = GetComponent<MeshRenderer>();
    }
 
    private void Update()
    {
        if (Input.GetKeyDown(KeyCode.R))
            meshRenderer.material.color = Color.red;
        else if (Input.GetKeyDown(KeyCode.G))
            meshRenderer.material.color = Color.green;
        else if (Input.GetKeyDown(KeyCode.B))
            meshRenderer.material.color = Color.blue;
    }
}

General Info

Debugging

Shortcut Function
F5 continue debugging
F9 toggle a breakpoint
F10 step over
F11 step into
Shift+F11 step out
Ctrl+Shift+F9 delete all breakpoints
What How
Show the Console window MainMenu: Window > Console
Show errors in the Console window Console window: Click on the error filter icon in the top-right corner
Show warnings in the Console window Console window: Click on the warning filter icon in the top-right corner
Jump to a line of code that generated an error, a warning, or printed a message Console window: Double-click the error, the warning, or the message
Recompile the script Project window: Right-click a script > Reimport
Set global #defines MainMenu: Edit > Project Settings > Player > Other Settings tab > Scripting Define Symbols > enter your defines and press Enter
- to disable a define, prefix it with \
- to add multiple defines, separate them with a semicolon
Switch Inspector to the Debug mode.
This allows you to view all public and private variables in Inspector
Click on the context menu icon in the top-right corner of Inspector and select Debug:

Write a message to the Console window:

// Method#1
Debug.Log("Hello");
 
// Method#2
print("Hello");
 
// Display the position of a GameObject.
print(transform.position.ToString());

Gizmos

What How
Show gizmos in the Game view Game view: Toggle the Gizmo button in the top-right corner

Gizmos help with visual debugging. Draw them in the MonoBehaviour's OnDrawGizmo event.

Example: This gizmo draws a bounding wireframe sphere of a specified radius centered on a GameObject. In addition, it draws a line-of-sight vector that represents the object's forward direction.

using UnityEngine;
 
public class MyGameObject : MonoBehaviour 
{
    public bool DrawGizmos = true;
    public float Radius = 4.0f;
 
    void OnDrawGizmos()
    {
        if (!DrawGizmos)
            return;
 
        // Draw a front vector. The front vector showns a direction the object is facing.
        Gizmos.color = Color.blue;
        Gizmos.DrawRay(transform.position, transform.forward.normalized * Radius);
 
        // Show the proximity radius around the object.
        Gizmos.color = Color.red;
        Gizmos.DrawWireSphere(transform.position, Radius);
 
        // Restore the gizmo's color back to white.
        Gizmos.color = Color.white;
    }
}

If you want to draw gizmos for selected objects only, call OnDrawGizmosSelected.

Inspector

Arrays

An array of game objects:

using UnityEngine;
 
public class SimpleTest : MonoBehaviour
{
    public GameObject[] Objects;
 
    void Start () { }
    void Update () { }
}

The above array shows up in the Inspector as follows:

You can specify the maximum capacity of the array (here 3):

Next, you can drag-and-drop objects into the array slots.

Enumerations

A variable of an enum type appears as a drop-down list in the Inspector window.

Properties

C# properties are not shown in the Inspector. Community-made scripts and solutions are available that can change this default behavior.

Fields

All public fields are exposed as properties in the Inspector. A downside of that is that other scripts may access these public fields which breaks encapsulation. In order to expose a field to the Inspector and keep it private, declare the field with the [SerializeField] attribute:

[SerializeField]
private GameObject enemy;

Logging

One way to create a logging mechanism in Unity is to use the Application class to receive exception notifications:

using UnityEngine;
using System;
using System.IO;
 
public class ExceptionLogger : MonoBehaviour 
{
    private const string LogFilename = "log.txt";
 
    void Start () 
    {
        // The Application.persistentDataPath location is always writeable.
        // When run under Unity, the path is C:/Users/testuser/AppData/LocalLow/DefaultCompany/TestProject
        Debug.Log ("PersistentDataPath: " + Application.persistentDataPath);	
    }
 
    // Register for exception listening.
    void OnEnable()
    {
        // The logMessageReceived event is invoked when a log message is received.
        Application.logMessageReceived += HandleLog;
    }
 
    // Unregister from exception listening.
    void OnDisable()
    {
        Application.logMessageReceived -= HandleLog;
    }
 
    // Update is called once per frame
    void Update () { }
 
    private void HandleLog(string logString, string stackTrace, LogType type)
    {
        using (StreamWriter writer = new StreamWriter(
               Path.Combine(Application.persistentDataPath, LogFilename)))
        {
            if (type == LogType.Exception || type == LogType.Error) 
            {
                writer.WriteLine("Logged at: " + DateTime.Now.ToString () + Environment.NewLine +
                    "Description: " + logString + Environment.NewLine +
                    "Trace: " + stackTrace + Environment.NewLine +
                    "Type: " + type.ToString ());
            }
        }
    }
}

Models

3D model file formats supported by Unity

File type Mesh Animation
FBX v v
DAE (Collada) v v
OBJ v
3DS v
DXF v

Additonal formats supported by Unity that are exported to FBX behind the scene. They require the corresponding application to be installed:

  • Maya
  • 3ds Max
  • Blender

Common properties of 3D models:

  • Scale Factor - Unity import models to a very small scale. You may want to adjust the Scale Factor to compensate the small File Scale value.
  • Generate Colliders - when checked, prevents objects from going through the model.
  • If your model does not have an animation, switch the animation off: Animation tab > deselect Import Animation

Note that when you import a 3D model, Unity creates a material for this model. You may want to assign a texture to the material.

Mesh Filter holds a mesh. Mesh Renderer renders the mesh.

Profiling

Available in Unity Pro.

The profiler gives you a statistical top-down view of how time and workload is distributed across the different parts of your game and across system hardware components, such as the CPU and graphics card.

When viewing the performance graph, it's a good practice to watch for sudden increases (peaks or spikes). These indicate frames of sudden and intense activity.

More info on the profiler can be found here.

Scripting

What How
Create a C# script MainMenu: Assets > Create > C# Script
-or-
Right-click on an empty space in the Project window > Create > C# Script
-or-
Create and attach a script in one step: Select a game object > Inspector > [Add Component] > New Script > [Create and Add] (note that the script is added into the project's root).

Script filenames:

  • Unity uses the filename to determine the name of a C# class to be created inside the file.
  • All the script files should have a unique name across the project.
  • If you rename the script file, you also need to rename the C# class inside the file to match the filename.

MonoBehaviour is the base class for script components i.e., the scripts that contain classes that inherit from MonoBehaviour are components.

  • You can search for a specific type of object by using t: in the search box followed by a type, e.g., t:Light, t:AudioSource
  • There is a path to the found object displayed at the bottom of the Hierarchy view.
  • When cancelling the search you will be automatically moved to the found object in the Hierarchy. The Hierarchy will be expanded to show the object.

Stats panel

How to access the Stats panel:

  • Select the Game view.
  • Click on the Stats button in the top-right corner of the toolbar.

The most important parameter in the Stats panel is FPS (frames per second) which indicates how often the Update event is called. Strive for 30 FPS or above.

Components & Classes

Animation

The Animation Controller is a state machine that governs animation states.

  • Create an Animation Controller: Project view > Create > Animation Controller
  • Add the Animation Controller to a GameObject you need to control (for example, a player).
  • To see the controller, double-click it in the Project view. It opens the Animator window.
  • Expand your model in the Project view to see the components of the model.
  • Move animations (marked with a [>] icon) from the Project view onto the Animator window.
  • An orange state is a default state. Set a default state by right-clicking on the state and selecting “Set as Layer Default State”.
  • Add parameters (there is a Parameters tab next to the Layers tab in the Animator window) for example a bool parameter IsWalking.
  • A Trigger parameter is similar to bool but once it is set to true, it sets back to false automatically (i.e., it resets itself).

Add conditions (transitions) to move from one state to another:

  • Right-click on a state and select “Make Transition”.
  • Move the transition to a destination state.
  • Click the transition allow. The Inspector shows Conditions of this transition.
  • Click + to add a condition (for example IsWalking = true - it means that we transition from Idle to Walking when IsWalking == true)
  • Uncheck Has Exit Time for the animations to work correctly or you will get an error: “Asset Player: Transition in state AnyState doesn't have an Exit Time or any condition, transition will be ignored”.
  • If you want to transition from any state, simply make a transition from the Any State to your state such as a Death state (you can die any time).

Attributes

Use the RequireComponent attribute to ensure that other components needed by the script are attached.

// The script requires the CharacterController component.
[RequireComponent(typeof(CharacterController))]
public class FirstPersonInput : MonoBehaviour
{
    // ...
}

Use the AddComponentMenu attribute to add the script to the component menu in Unity's editor (the Add Component button at the bottom of the Inspector).

// Add the script to the "Control Script" group and assign it to the "Custom Input" menu item.
[AddComponentMenu("Control Script/Custom Input")]
public class FirstPersonInput : MonoBehaviour
{
    // ...
}

Use the SerializeField attribute on a private field to expose the field in the Inspector but disallow other scripts to access it (works in both the Normal and Debug modes):

public class SceneController : MonoBehaviour
{
    [SerializeField]
    private GameObject enemyPrefab;
 
    [SerializeField]
    private GameObject[] objects;
 
    [SerializeField]
    private double speed;
 
    void Update ()
    {
        // ...
    }
}

Use the HideInInspector attribute to hide a public variable from the Inspector:

[HideInInspector]
public int Counter;

CharacterController.

The CharacterController component makes a GameObject move like a character, including colliding with obstacles. You may want to replace the object's collider with a Character Conroller if the object is your player.

Add a Character Conroller component:

  • Inspector: Click [Add Component] > Physics > Character Controller

Example: Move a GameObject that has a CharacterController component attached:

using UnityEngine;
 
[RequireComponent(typeof(CharacterController))]
public class FirstPersonInput : MonoBehaviour
{
    public float speed = 9.0f;
 
    // A reference to the CharacterController component attached to this GameObject.
    private CharacterController cc;
 
    void Start ()
    {
        cc = GetComponent<CharacterController>();
    }
 
    void Update ()
    {
        float deltaX = Input.GetAxis("Horizontal") * speed;
        float deltaZ = Input.GetAxis("Vertical") * speed;
 
        Vector3 movement = new Vector3(deltaX, 0, deltaZ);
 
        // Limit movement vector's magnitude to the movement speed.
        // Otherwise diagonal movement would have a greater magnitude than movement
        // along the X or Z axis.
        movement = Vector3.ClampMagnitude(movement, speed);
 
        // Transform the movement vector from local to global coordinates.
        // We need the movement vector to be in global coordinates rather than 
        // in local because, for example, the player's left may be a different
        // direction from the world's left.
        movement = transform.TransformDirection(movement * Time.deltaTime);
 
        // Move the character controller by the movement vector.
        // The Move method moves the object in global coordinates.
        // It takes absolute movement deltas as the parameter.
        cc.Move(movement);
    }
}

Coroutines

Coroutines are a Unity-specific way of handling tasks that execute incrementally over time.

  • The statement yield return new WaitForSeconds(n) pauses execution for n seconds, resuming after the interval at the next line.
  • The statement yield return null suspends execution for the current frame, resuming execution at the next line on the next frame.

Example: Draw a sphere and remove it after one second:

// The point variable is the position of the sphere.
StartCoroutine(DrawSphere(point));
...
private IEnumerator DrawSphere(Vector3 pos)
{
    GameObject sphere = GameObject.CreatePrimitive(PrimitiveType.Sphere);
    sphere.transform.position = pos;
 
    yield return new WaitForSeconds(1);
 
    Destroy(sphere);
}

Cursor

Hide and lock the mouse cursor:

Cursor.lockState = CursorLockMode.Locked;
Cursor.visible = false;

Click Esc to unlock the mouse cursor.

Input

Unity docs: “Use the UnityEngine.Input class to read the axes set up in the Input Manager, and to access multi-touch/accelerometer data on mobile devices. The value is in the range -1…1 for keyboard and joystick. If the axis is setup to be delta mouse movement, the mouse delta is multiplied by the axis sensitivity and the range is not -1…1. This value is frame-rate independent; you do not need to be concerned about varying frame-rates.”

The Input.GetAxis method returns a positive or a negative number, depending on the direction of movement.

Examples:

// 'speed' (also called 'sensitivity') is a scaling factor.
 
// Get input from the mouse.
float deltaHoriz = Input.GetAxis("Mouse X") * speed;
float deltaVert = Input.GetAxis("Mouse Y") * speed;
 
// Get input from the keyboard. "Horizontal" and "Vertical" are indirect names for keyboard mapping.
// The left/right arrows and the letters A/D are mapped to "Horizontal".
// the up/down arrows and the letters W/S are mapped to "Vertical".
float deltaHoriz = Input.GetAxis("Horizontal") * speed;
float deltaVert = Input.GetAxis("Vertical") * speed;
 
// Check if the left mouse button has been clicked.
bool clicked = Input.GetMouseButtonUp(0)
bool clicked = Input.GetButton("Fire1");
 
// Get the mouse position in pixel coordinates.
Vector3 point = Input.mousePosition;

Messaging

Use SendMessage and BroadcastMessage methods to call functions by name on other components attached to the same GameObject, regardless of their type. The messaging methods are useful when there are lots of different types of objects receiving the message.

  • SendMessage - executes a method by its name on all components attached to a GameObject.
  • BroadcastMessage - executes a method by its name on all components attached to a GameObject and recursively on all child objects.

Both methods rely on reflection, which can cause performance issues. Because of that, minimize their usage, especially during Update and OnGUI events. Delegates and interfaces offer a faster alternative.

Example: Assume that the TestObject script is attached to a GameObject. The following code invokes Method1 on the TestObject and all components/scripts attached to this GameObject (if they have Method1):

using UnityEngine;
...
public class TestObject : MonoBehaviour 
{
    void Start () 
    {
        // SendMessageOptions defines what happens if Method1 is not present on a component.
        SendMessage("Method1", SendMessageOptions.DontRequireReceiver);
    }
}

See also: EventManager.

MonoBehaviour Events

MonoBehaviour is the base class for script components i.e., the scripts that contain classes that inherit from MonoBehaviour are components.

The Update methods should contain minimal code such as reading player input. All other code should be handled by an event system.

OnDestroy Called when the GameObject is removed.
OnMouseDown
OnMouseEnter
OnMouseExit
The mouse-input and touch-input events. In order to be fired, the GameObject has to have a collider component attached.
OnApplicationQuit OnApplicationQuit is sent to all objects in the scene just before the game exits but before the scene and its contents are destroyed. Sometimes, however, OnApplicationQuit may not be called on devices which don't quit applications but rather suspend them. If you need to receive OnApplicationQuit events on suspension, you need to enable the relevant option:
MainMenu: Edit > Project Settings > Player > Inspector: expand the Other Settings tab for the iOS builds and enable the Exit on Suspend checkbox
OnApplicationFocus OnApplicationFocus is sent to all objects in the scene when the game loses focus.
OnApplicationPause There are two kinds of pause events in Unity:
- An ultimate pause where every activity and every event in a game is suspended; in this state, there is no passing of time and nothing can move forward.
- A relative pause where the game is aware of being in a paused state; it halts some events but allows others to continue such as GUI interaction and user input, which can unpause the game.

The OnApplicationPause event refers to the ultimate pause. This event is called only if the Run In Background option is not enabled in the Player Settings tab, under the Resolution group. This option, when disabled, automatically pauses a desktop game whenever the window focus is lost. This means OnApplicationPause follows an OnApplicationFocus event. In iOS, OnApplicationPause will be called whenever the application is minimized or pushed into the background.
OnGUI Called every frame after the scene is rendered.

Do not rely on the OnApplicationPause event for creating a relative pause. To achieve this, use the Time.timeScale variable or create a more comprehensive system in which you have control over which elements are paused.

The full list of MonoBehaviour events can be found here.

Particle Systems

What How
Create a particle system MainMenu: GameObject > Particle System
Apply a material
(possibly with a texture)
to a particle system
- Select the particle system
- Expand Renderer in the Inspector
- Drag a material onto the Material slot:

- Change the shader for the particle material, for example, to Additive:
Shader drop-down list > Particles > Additive

Many of the particle system properties are controlled by a curve displayed at the bottom of Inspector. It represents how the value changes over time:

Commonly used properties of particle systems:

Property Description
Looping Determines if the particle system keeps playing forever.
Start Lifetime How long the particle exists.
Start Speed How fast the particle is moving.
Start Size How big the particle is.
Start Rotation The orientation of the particle.
Start Color A particle tint.
Simulation Space - Local simulation space is fine for a still particle system.
- World simulation space may be better for a moving particle system.
Emission How quickly particles are emitted.
Shape The shape of the area emitted from. The default is a wide cone.
Size over Lifetime How the particle grows and shrinks over time.
Rotation over Lifetime Hoe the particle rotates over time.
Render How the particle looks like.

Plane Object

  • A plane has no volume.
  • It is a single-sided object.
  • Scale does not work on the Y axis.
  • If you set the negative value for the Y-scale, the plane will face the other direction.

Prefabs

Example: Instantiate a prefab and assign it to a private variable enemy:

public class SceneController : MonoBehaviour
{
    // A variable for linking to the prefab object.
    [SerializeField]
    private GameObject enemyPrefab;
 
    // An enemy object in the scene.
    // Note that this object is automatically added to the scene once instantiated.
    private GameObject enemy;
 
    void Update ()
    {
        // Only spawn a new enemy if there isn't already one in the scene.
        // We compare the enemy variable to null here but keep in mind that this comparison
        // may be changed in the future versions of Unity to something like enemy.isDestroyed
        if (enemy == null)
        {
            // Instantiate an object from the prefab.
            enemy = Instantiate(enemyPrefab) as GameObject;
 
            enemy.transform.position = new Vector3(0, 1, 0);
            float angle = Random.Range(0, 360);
            enemy.transform.Rotate(0, angle, 0);
        }
    }
}

Preferences

// Check if a key is present in preferences.
bool keyFound = PlayerPrefs.HasKey(keyName);
 
// Obtain an integer assigned to a key.
int n = PlayerPrefs.GetInt(keyName);
 
// Obtain an integer assigned to a key. If the key does not exist, use the default value.
int n = PlayerPrefs.GetInt(keyName, defaultValue);
 
// Assign an integer to a key.
PlayerPrefs.SetInt(keyName, n); // n is an integer

Random

// Return a random float between -10.0f [inclusive] and 10.0f [inclusive].
float n = Random.Range(-10.0f, 10.0f);
 
// Return a random int between 0 [inclusive] and 5 [exclusive].
int i = Random.Range(0, 5); // may return 0,1,2,3,4

Raycasting

  • Raycast
  • SphereCast
  • LineCast
  • Mouse picking - the action of picking out the spot in the scene directly under the mouse cursor.

Example (mouse picking): Perform a raycast using a ray originating at the camera and going through the mouse position. This Update method may be called on any object as it obtains a reference to the main camera through Camera.main:

void Update()
{  
    // Check the left click.
    if (Input.GetMouseButtonUp(0))
    {
        // Perform a raycast originating at the camera and going through the mouse position.
        Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
        RaycastHit hit;
        if (Physics.Raycast(ray, out hit, 100)) // 100 is maxDistance
        {
            Debug.Log(hit.collider.gameObject.name); // display the name of an object hit by the ray
        }
    }
}

Another way of obtaining the object that has been hit is to access the transform property:

RaycastHit hit;
if (Physics.Raycast(ray, out hit))
{
    // Retrieve the object the ray hit.
    GameObject obj = hit.transform.gameObject;
}

A ray originating at the object's position and pointing at the object's direction:

Ray ray = new Ray(transform.position, transform.forward);

Example: Perform a raycast using a ray going from the camera through the middle of the screen. This script has to be attched to a camera object:

using UnityEngine;
 
public class RayShooter : MonoBehaviour
{
    private Camera cam;
 
    void Start()
    {
        // Access the camera attached to this object.
        cam = GetComponent<Camera>();
    }
 
    void Update()
    {
        // Check if the mouse has been clicked.
	if (Input.GetMouseButtonDown(0))
        {
            // The point is in the middle of the screen.
            Vector3 point = new Vector3(cam.pixelWidth / 2, cam.pixelHeight / 2, 0);
 
            // Uncomment, if you want the point to be located at the mouse position.
            // Vector3 point = Input.mousePosition;
 
            // Create a ray going from the camera through the point (the middle of the screen).
            Ray ray = cam.ScreenPointToRay(point);
 
            // Perform the raycast. The hit variable contains information about the instersection
            // of the ray with an object the ray hit.
            RaycastHit hit;
            if (Physics.Raycast(ray, out hit))
            {
                // Display coordinates where the ray hit.
                Debug.Log("Hit " + hit.point);
            }
        }
    }
}

Example: The following script determines if a clear line or path exists between two objects. The path is clear if there are no colliders intersecting an imaginary line drawn between the two objects:

using UnityEngine;
 
public class ObjectPath : MonoBehaviour
{
    // A reference to another object to whom a clear path should be tested.
    public GameObject another = null;
 
    // The layer mask specifies a bitmask indicating which layers in the scene the collision test applies to.
    public LayerMask layerMask;
 
    void Update ()
    {
        if (another != null)
        {
            // Determine if a clear path exists between two objects.
            if (!Physics.Linecast(transform.position, another.transform.position, layerMask))
            {
                Debug.Log("Path clear");
            }
            else
            {
                Debug.Log("Path not clear");
            }
        }
    }
 
    // Show a helper line.
    void OnDrawGizmos()
    {
        if (another != null)
        {
            Gizmos.DrawLine(transform.position, another.transform.position);
        }
    }
}

To use the script, follow the steps:

  • Create a new script and name it ObjectPath.
  • Copy and paste the above code to the ObjectPath script.
  • Create two objects (for example, a Player and an Enemy) as well as a few obstacles (for example, walls).
  • Create a new layer and name it Walls.
  • Assign the walls to the Walls layer.
  • Attach the ObjectPath script to the Player object.
  • Assign the Enemy object to the ObjectPath componet's Another slot.
  • Select the Walls layer in the ObjectPath componet's Layer Mask slot.
  • Start the game.

Note that if the two objects themselves have colliders, such as BoxColliders, then the colliders will be included in the collision detection. For this reason, use the LayerMask to include or exclude specific layers.

Example: Perform raycasting with a circumference around the ray. The radius parameter (0.75) determnines how far around the ray to detect intersections:

Ray ray = new Ray(transform.position, transform.forward);
RaycastHit hit;
 
if (Physics.SphereCast(ray, 0.75f, out hit))
{
    if (hit.distance < range)
    {
        // Turn toward a new direction.
        float angle = Random.Range(-100.0f, 100.0f);
        transform.Rotate(0, angle, 0);
    }
}

Tip: When your environment has an uneven surface (for example, a room full of objects on the floor) and you need to raycast from/to the surface, cover the surface (i.e, the floor) with a quad. Use the quad's collider for raycasting. You can remove the quad's mesh renderer component as you don't need to render it, you only need the collider. Also, it's a good practice to keep the surface in a separate layer. Create a new layer (call it “Floor”) and then select it in the quad's layer drop-down. This way you assign your surface to the layer “Floor”.

Scenes

If you need to export your whitebox (aka graybox) scene geometry, you may want to use third party utilities such as Obj Exporter Script or Scene Obj Exporter Editor Extension. The geometry is exported as an OBJ file.

Sound

There are three main audio components in Unity:

  • audio clips
  • audio sources - an audio source plays an audio clip
  • audio listener

To associate an audio clip with a GameObject, use an Audio Source component:

  • Attach an Audio Source component to a GameObject and assign an AudioClip to it.

-or-

  • Drag an audio clip on to a GameObject. Unity will create a new Audio Source on the GameObject and automatically assign the dragged audio clip.

Special Folders

Assets' special folders:

  • Resources
  • Plugins
  • Editor
  • Gizmos

Examples: Load assets from the Assets/Resources folder:

// Load and instantiate a game object.
GameObject obj = Instantiate(Resources.Load("MyObject", typeof(GameObject))) as GameObject;
 
// Load a text file Levels.xml or Levels.txt etc. 
TextAsset file = Resources.Load("Levels", typeof(TextAsset)) as TextAsset;
string body = file.text; // access the body of the text file
 
// Create three instances of a game object MyObject located at the Assets/Resources folder.
for (int i = 1; i <= 3; ++i)
{
    GameObject obj = Instantiate(Resources.Load("MyObject", typeof(GameObject))) as GameObject;
    obj.transform.position = new Vector3(i-1 + i*0.2f, 0, 0);
    obj.name = i.ToString(); // assign a name
}

Textures

2D image file formats supported by Unity:

File type Lossless compression Alpha channel
PNG v v
JPG
GIF
BMP no compression
TGA v v
TIFF v
PICT
PSD no compression v
  • Textures should be sized in powers of 2 for example 256×256.
  • Textures aren't applied to geometry directly. Instead, they can be part of materials and the materials are applied to an object.
  • Textures vs. sprites: Sprites are 2D images displayed directly on the screen, as opposed to textures that are images displayed on the surface of 3D models.
What How
Apply a texture to an object Drop a texture onto an object. A new material is created automatically and this material is applied to the object.
-or-
Apply a texture to an existing material:
Tile a texture Set the tiling properties:
Import a texture Right-click in the Project tab > Import New Asset

Time

Use the Time class to access time in your scripts.

Move this object along its forward direction:

// 'speed' is a scaling factor that represents the speed of movement.
transform.localPosition += transform.forward * speed * Time.deltaTime;

Transform.

The values of Transform properties are measured relative to the Transform's parent. It means that the parent's position is the origin of the child's position. If the Transform has no parent, the properties are measured in world space.

Read this to find out about differences between TransformPoint, TransformDirection, and TransformVector.

Get the Transform component of a GameObject by accessing its transform property:

using UnityEngine;
 
public class MyScript : MonoBehaviour
{
    void Start ()
    {
        // Get the Transform component attached to this GameObject.
        Transform trans = this.transform;
    }
}

Translation / Position

  • Transform.position - The position of an object in world space stored as a Vector3.
  • Transform.localPosition - The position of an object relative to the parent stored as a Vector3. If the object has no parent, it is the same as Transform.position. The value of Transform.localPosition corresponds to the position shown in the Inspector's Transform component.

Move a GameObject along the x axis:

// Example #1
transform.Translate(Time.deltaTime, 0, 0);
 
// Example #2
// direction is 1 or -1
transform.Translate(direction * speed * Time.deltaTime, 0, 0);

Reset a GameObject's position:

transform.position = Vector3.zero;

Place a GameObject in front of another GameObject (obj) and point in the same direction:

// Vector3.forward is a shorthand for Vector3(0,0,1)
// The TransformPoint method transforms position from local space to world space.
transform.position = obj.transform.TransformPoint(Vector3.forward * 1.5f);
transform.rotation = obj.transform.rotation;

By default an object moves in relation to its local coordinate system. You can move the object in the world coordinate system by supplying an additional parameter for Transform.Translate:

// Space.World instructs Unity to use the scene coordinate system.
transform.Translate(0, Time.deltaTime, 0, Space.World);

Rotation

Increment the current rotation:

transform.Rotate(new Vector3(0, 15, 0)); // method #1
transform.Rotate(0, 15, 0); // method #2

Increment the current rotation and make it frame rate independent.

transform.Rotate(new Vector3(0, 15, 0) * Time.deltaTime); // Rotate(Vector3)
transform.Rotate(0, 15 * Time.deltaTime, 0); // Rotate(float, float, float)

Rotate an object in relation to the world coordinate system:

// Space.Local is a default for the fourth parameter.
transform.Rotate(0, 10.0f, 0, Space.World);

Reset the GameObject's rotation:

transform.localEulerAngles = Vector3.zero;

GUI

Immediate Mode

The OnGUI method is called every frame after everything else is rendered.

Draw a label in the middle of the screen:

void OnGUI()
{
    int size = 12;
    float posX = Camera.main.pixelWidth / 2 - size / 2;
    float posY = Camera.main.pixelHeight / 2 - size / 2;
    GUI.Label(new Rect(posX, posY, size, 2 * size), "O");
}

Draw a button:

void OnGUI()
{
    // Specify the button's position, width, height, and label.
    if (GUI.Button(new Rect(10, 10, 40, 20), "Test"))
        Debug.Log("Clicked");
}

Retained Mode

All UI elements must be children of the Canvas element to behave correctly. They are anchored to the Canvas.

Follow the steps to create the visual part of GUI:

  • Import sprite images you will use in your UI. If you work in a 3D project, set the images' Texture Type to Sprite (2D and UI).
  • Create a Canvas object (MainMenu: GameObject > UI > Canvas). An EventSystem object is automatically created.
  • If you work in a 3D project, switch to 2D view mode.
  • Double-click the canvas in the Hierarchy in order to zoom out and view it fully.
  • You may want to turn off the skybox for better visibility.
  • Set the Canvas's Pixel Perfect property.
  • Make sure the Rect tool is selected.
  • Add UI objects (such as images, texts, and buttons) to the Canvas.
  • Attach the sprites to your UI elements by dragging the sprites from the Project view to the element's Source Image slot.
  • Click the Set Native Size button in the elements' Image component to resize the image correctly.
  • If needed, change the elements' anchors using the Anchor Presets or type in exact numbers for the anchor points.
  • Set the elements' padding by adjusting the PosX and PosY properties.

If GUI interferes with your gameplay, check if the mouse pointer is over an EventSystem object:

using UnityEngine.EventSystems;
...
void Update()
{
    if (Input.GetMouseButtonDown(0) && !EventSystem.current.IsPointerOverGameObject())
    {
        // non-GUI game logic goes here
    }
}
What How
Change button's fading Adjust the button's Fade Duration property
Disable keyboard interactions with the UI Deselect the check box Send Navigation Events in the EventSystem component
Assign an event handler to a button - Click the button in Hierarchy.
- Under the On Click() section in the Inspector, click the plus sign:

- Click the selector circle and select a GameObject whose method you want to use as your handler (or drap-and-drop a GameObject).
- From the drop-down list next to the selector, select a component of the GameObject and then a method that will serve as an event handler for the button.

Tip: Make sure that the selected game object has a component whose method you want to use.
Tip: The selection sequence is: GameObject > the GameObject's component > the component's method
Respond to different mouse events Use an EventTrigger component to make UI elements repond to different events:
- Click the [Add Component] button.
- Select the Event section.
- Select the EventTrigger component.
- The EventTrigger component is added to the UI element.
- Click the [Add New Event Type] button.
- Choose an event, for example, Pointer Down.
- An empty panel is created for the event.
- Add an event handler.

Techniques

Arrange Objects

Draw objects in a circle

using UnityEngine;
 
public class DrawObjects : MonoBehaviour
{
    public GameObject obj;
 
    void Start ()
    {
        int numObjects = 12;
        float radius = 4f;
 
        // Draw objects in a circle.
        for (int i = 0; i < numObjects; ++i)
        {
            float angle = i * Mathf.PI * 2 / numObjects;
            Vector3 pos = new Vector3(Mathf.Cos(angle), 0.1f, Mathf.Sin(angle)) * radius;
            Instantiate(obj, pos, Quaternion.identity);
        }
    }
}

Draw objects in a grid

using UnityEngine;
 
public class DrawObjects : MonoBehaviour
{
    public GameObject obj;
 
    void Start ()
    {
        int numObjects = 25;
        float spacing = 2f;
 
        int num = (int)Mathf.Sqrt((float)numObjects);
 
        // Draw objects in a num x num grid.
        for (float y = -num / 2; y < num / 2; y++)
        {
            for (float x = -num / 2; x < num / 2; x++)
            {
                Vector3 pos = new Vector3(x, 0.15f, y) * spacing;
                Instantiate(obj, pos, Quaternion.identity);
            }
        }
    }
}

Draw objects as a wall

using UnityEngine;
 
public class DrawObjects : MonoBehaviour
{
    public GameObject obj;
 
    void Start ()
    {
        int numObjects = 25;
        float spacing = 0.2f;
 
        int num = (int)Mathf.Sqrt((float)numObjects);
 
        for (int y = 0; y < 5; ++y)
        {
            float yf = 0.5f + y + spacing;
            for (int x = 0; x < 5; ++x)
            {
                Instantiate(obj, new Vector3(x, yf, 0), Quaternion.identity);
            }
        }
    }
}

EventManager

One way of implementing an event system is to create a singleton class EventManager that persists across levels and is globally accessible. It accepts notifications of events sent by publishers and then immediately dispatches the notifications to all appropriate subscribers in the form of events. The subscribers need to register themselves with the EventManager for one or more specific events. Then, when the event occurs, the subscribers are notified.

There is also event handling system available online such as CSharpNotificationCenter. This system relies on the SendMessage and BroadcasMessage methods. Another broadcast messenger system is CSharpMessenger Extended.

See also: Messaging.

GameController

A GameController (a.k.a. GameManager or SceneController) usually is an empty GameObject providing the following functionality:

  • Determining if a game is paused.
  • Determining whether the win condition has been satisfied.
  • Tracing the current state of the game.

Levels

Progressing to the next level can be achieved by changing the active scene.

Use the SceneManager.LoadScene method to change the active scene in Unity. There is also a method SceneManager.LoadSceneAsync that loads the scene asynchronously in the background.

SceneManager.LoadScene(sceneName);

Load game levels from an XML file. This example uses LINQ-to-XML:

using UnityEngine;
using System.Linq;
using System.Xml.Linq;
using System.Collections.Generic;
...
List<string> levels = new List<string>();
 
// Load Level.xml file from Assets/Resources.
TextAsset file = Resources.Load("Levels", typeof(TextAsset)) as TextAsset;
 
XElement doc = XElement.Parse(file.text);
 
var query = from setup in doc.Elements("level").Elements("data")
            select setup;
 
foreach (XElement element in query)
    levels.Add(element.ToString());
 
// Just for testing.
foreach (string level in levels)
    Debug.Log(level);
<levels>
<level><levelname>1</levelname><data>2,D,4,45,FR,Q</data></level>
<level><levelname>2</levelname><data>33,TT,G,43,1</data></level>
<level><levelname>3</levelname><data>7,8,E,Q,21,9</data></level>
</levels>

ManualMouseInput

OnMouseDown, OnMouseEnter, and OnMouseExit are the mouse-input and touch-input events. In order to be fired, the GameObject has to have a collider component attached. There are occasions when the mouse events are not launched, even with a collider attached, because other objects (with colliders) are obscuring the objects you need to click. One solution is to assign the foreground objects to an IgnoreRaycast layer. If this is not enough, you may want to manually handle mouse input events.

A rudimentary script to handle mouse/touch input manually:

using UnityEngine;
using System.Collections;
 
public class ManualMouseInput : MonoBehaviour
{
    // A collider attached to this object.
    private Collider col = null;
 
    void Awake()
    {
        col = GetComponent<Collider>();
    }
 
    void Start ()
    {
        StartCoroutine(UpdateMouse());
    }
 
    public IEnumerator UpdateMouse()
    {
        // Is this object's collider intersected? 
        bool isIntersected = false;
 
        // Is the mouse button down?
        bool isButtonDown = false;
 
        // Loop forever 
        while (true)
        {
            // Get the mouse screen position.
            Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition); 
            RaycastHit hit;
 
            // Test the ray for collision against this collider.
            if (col.Raycast(ray, out hit, Mathf.Infinity))
            {
                // Object was intersected. 
                if (!isIntersected)
                    SendMessage("OnMouseEnter", SendMessageOptions.DontRequireReceiver);
 
                isIntersected = true;
 
                // Test for mouse events.
                if (!isButtonDown && Input.GetMouseButton(0))
                {
                    isButtonDown = true;
                    SendMessage("OnMouseDown", SendMessageOptions.DontRequireReceiver);
                }
 
                if (isButtonDown && !Input.GetMouseButton(0))
                {
                    isButtonDown = false;
                    SendMessage("OnMouseUp", SendMessageOptions.DontRequireReceiver);
                }
            }
            else
            {
                // Check if this collider was previously intersected.
                if (isIntersected)
                    SendMessage("OnMouseExit", SendMessageOptions.DontRequireReceiver);
 
                isIntersected = false;
                isButtonDown = false;
            }
 
            // Wait until the next frame.
            yield return null;
        }
    }
}

Mouse-Look

An example of a mouse-look script:

using UnityEngine;
using System.Collections;
 
public class MouseLook : MonoBehaviour
{
    public enum RotationAxes
    {
        MouseBoth = 0,
        MouseX = 1, // horizontal rotation (around y-axis)
        MouseY = 2  // vertical rotation (around x-axis)
    }
 
    // The axes variable is available as a drop-down list in Object Inspector.
    public RotationAxes axes = RotationAxes.MouseBoth;
 
    // Horizontal and vertical sensitivity determines the speed of rotation.
    public float sensitivityHoriz = 9.0f;
    public float sensitivityVert = 9.0f;
 
    // Limits for vertical rotation. They determine how much the view can tilt up or down.
    public float minimumVert = -45.0f;
    public float maximumVert = 45.0f;
 
    // rotationX represents a vertical angle. It's called rotationX because vertical 
    // rotation goes around the x-axis.
    private float rotationX = 0; 
 
    void Start ()
    {
        Rigidbody rb = GetComponent<Rigidbody>();
        if (rb != null)
            // The player's rotation needs to be solely controlled by the mouse, not by physics.
            rb.freezeRotation = true; 
    }
 
    void Update ()
    {
        if (axes == RotationAxes.MouseX)
        {
            // Horizontal rotation.
 
            // The Rotate() method increments the current rotation.
            transform.Rotate(0, Input.GetAxis("Mouse X") * sensitivityHoriz, 0);
        }
        else if (axes == RotationAxes.MouseY)
        {
            // Vertical rotation.
 
            // Change the vertical rotation angle.
            rotationX -= Input.GetAxis("Mouse Y") * sensitivityVert;
 
            // Clamp the vertical angle between min and max limits.
            rotationX = Mathf.Clamp(rotationX, minimumVert, maximumVert);
 
            // Keep the same Y angle (no horizontal rotation).
            float rotationY = transform.localEulerAngles.y;
 
            // Set the rotation angle directly.
            transform.localEulerAngles = new Vector3(rotationX, rotationY, 0);
        }
        else
        {
            // Both horizontal and vertical rotation.
 
            rotationX -= Input.GetAxis("Mouse Y") * sensitivityVert;
            rotationX = Mathf.Clamp(rotationX, minimumVert, maximumVert);
 
            // delta is the amount to change the rotation by.
            float delta = Input.GetAxis("Mouse X") * sensitivityHoriz;
 
            // Increment the rotation angle by delta.
            float rotationY = transform.localEulerAngles.y + delta; 
 
            transform.localEulerAngles = new Vector3(rotationX, rotationY, 0);
        }
    }
}

Find Nearest GameObject

Find the nearest GameObject to a given object in terms of linear distance:

GameObject[] objects = GameObject.FindGameObjectsWithTag(tag);
GameObject nearest = FindNearestGameObject(gameObject);
...
private GameObject FindNearestGameObject(GameObject obj)
{
    GameObject nearestObject = null;
    float shortestDist = float.MaxValue;
 
    foreach (GameObject o in objects)
    {
        // Calculate the distance between two points.
        // The Vector3.Distance(a,b) method is the same as (a - b).magnitude
        float dist = Vector3.Distance(transform.position, o.transform.position);
        if (dist < shortestDist)
        {
            // Exclude the game object the script is called on.
            if (o.GetInstanceID() != obj.GetInstanceID())
            {
                shortestDist = dist;
                nearestObject = o;
            }
        }
    }
 
    return nearestObject;
}

Standard Assets Project

Unity comes with a Standard Assets Example Project. It is located at

C:\Users\Public\Documents\Unity Projects\Standard Assets Example Project

Sample scenes are located at SampleScenes > Scenes.

Tips & Tricks

  • Use an empty GameObject to attach scripts that do not apply to any specific object in the scene. Such a GameObject is often called Controller and the attached script SceneController.
notes/unity3d/engine.txt · Last modified: 2017/10/17 by leszek