The Relevant Unity’s Stuff

The objective of this post is to gather all the fundamental concepts that you and me could want to check out if we get stuck in something. Things like how to loop over all the elements in a dictionary, how to create an enum, the difference between const and readonly and so on.

(The information here is not the most optimal or exact, it’s what I know at this point and time and will be updated. For feedback, please leave a comment!)

Executions

OnValidate()

Gets called when it gets loaded or when a value is changed in the inspector.

LateUpdate()

Executes after all the code inside Update() gets executed, useful when making, for example a Camera Follow, as the camera needs to follow a character after this character was moved in Update().

Awake()

Gets called even if the script is not enabled. (Before Start())

FixedUpdate() vs Update()

Update is called each frame, FixedUpdate is called every x physics step, FixedUpdate should be used when manipulating physics or rigidbodies, specially if those manipulations are over time and not single-framed.

Debugging

Formatted logs

Debug.LogFormat("The age of {0} is {1}, it's {2} that he has a pet", nameString, ageInt, petBool);
    int age = 10;

    Debug.Log($"My age is {age}");

Pretty logs

Debug.Log("<b>This</b> <i>is a word</i>, this is <size=8>another</size> word and so on");

Debug.Log("This is a word, <b><color=green>this is another</color></b> word and so on");
Pretty logs in Unity

Throwing exceptions

    public void ValidateEmail(string email)
    {
        if (!email.Contains("@"))
        {
            throw new System.ArgumentException("Not a valid email");
            // method interrupted if exception is thrown
            Debug.Log("Inaccessible")
        }
    }

Try-catch

        // if it doesn't throw an exception
        try
        {
            ValidateEmail("ea");
            Debug.Log("Email validated");
        }
        // if it throws an exception, define exception type and variable
        catch(System.ArgumentException exceptionVariable)
        {
            // code
            Debug.Log("Unsuccessful attempt, " + exceptionVariable.ToString());
        }
        // optional, always runs at the end of try-catch
        finally
        {
            Debug.Log("Email validator ran");
        }

Syntax

Member naming

// subjective
public int PlayerAge { get; set; }; // properties
public int playerAge; // public variables
[SerializableField] int _playerAge; // serialized private variables
int p_playerAge; // private variables

If’s

Switch statement

      int caseSwitch = 1;
      
      switch (caseSwitch)
      {
          case 1:
              Console.WriteLine("Case 1");
              break;
          case 2:
              Console.WriteLine("Case 2");
              break;
          default:
              Console.WriteLine("Default case");
              break;
      }

Fall-through

// values is an enum
switch (values)
{
    case Numbers.x:
    case Numbers.x1:
        print("For both x and x1");
        break;
    case Numbers.y:
    case Numbers.y1:
        print("For both y and y1");
        break;
}

Ranges in Switch

int age = 25;

switch (age)
{
    // makes it possible to convert switch to an if
    case int n when (n >= 18 && n <=25):
        print("You're between 18 and 25");
        break;
    default:
        print("You're older than 25");
        break;
}

Ternary operator

// condition, if true, return 'Player is alive', if false, return 'Player is dead'
// you can have encapsulated ternary operators
message = health > 0 ? "Player is alive" : "Player is dead";

Loops

For loop

 // define variable, condition, increment var
for(int i = 100; i > 0; i--)
{
    if(i % 2 == 0) // print even numbers
    {
      print(i);
    }
}

Do-while loop

Runs at least once, then checks if the condition is true to run again.

int i = 0;

do
{
     print(i);
     i++; // incrementer
} while (i < 50); 

While loop

bool isDead = false;
// runs while the condition is true
while (!isDead)
{
     Kill();
     isDead = true;
}

Break

Used to stop a loop.

for(int i = 0; i < 100; i++)
{
    print(i);

    if(i == 50)
    {
        break;
    }
}

Continue

Instead of stopping the loop completely, stops the current iteration (based on a condition.)

Loop over array/list with For

// for a list, use .Count instead of .Length
for(int i = 0; i < names.Length; i++)
{
    print(names[i]); // prints each item on names[]
}

Loop over array/list with Foreach

foreach(var name in names) // foreach object 'name' in array 'names'
{
    print(name);
}

Loop over dictionary

foreach(KeyValuePair<int, Item> item in itemDatabase)
     {
          Debug.Log(item.Key);
          Debug.Log(item.Value.name);
     }

Nested loops

        // starts at 0
        // x doesn't increase until the y loop ends
        for (int x = 0; x < 10; x++)
        {
            // starts at 0
            // then keeps iterating until y < 10
            for (int y = 0; y < 10; y++)
            {
                Debug.Log("x: " + x + " y: " + y);
            }
            // x increases by one
            // y loop repeats for x = 1...9
        }

Methods

Return type

    [SerializeField] int total;

    void Start()
    {
        // Use Sum function
        total = Sum(1, 1);
    }

    // Doesn't return void, instead returns an int type
    int Sum(int a, int b)
    {
        return a + b;
    }

Method overriding

public class Pet : MonoBehaviour
{

    protected string petName;
    
    // virtual means it can be overridden
    protected virtual void Speak()
    {
        Debug.Log("Speak");
    }

    void Start()
    {
        // checks if it's not overridden, if it's not, executes the original method
        Speak();
    }
}
public class Dog : Pet
{

    // overrides the inherited method
    protected override void Speak()
    {
        Debug.Log("Bark Bark!");
    }

}

Invoke

// invokes a method after x seconds (in this case, two seconds)
Invoke("Method", 2);

// invokes a method after x seconds, then repeats every y seconds (in this case, two seconds, and one second)
InvokeRepeating("Method", 2, 1);

// cancels all invokes
CancelInvoke();

// cancels a certain method invoke
CancelInvoke("Method");

Invoking without direct string (nameof)

    void Start()
    {
        // nameof returns a string with the name of something
        // this way you can rename the method (Ctrl+R+R) and avoid
        // forgetting to rename it on Invoke
        // if you want to use methods with arguments, use coroutines
        Invoke(nameof(DebugTest), 1f);
    }

    void DebugTest()
    {
        Debug.Log("No strings attached");
    }

Method overloading

// When accessing Add(), it will show both possible signatures.
public int Add(int num1, int num2)
{
    return num1 + num2;
}

public string Add(string text1, string text2)
{
    return text1 + text2;
}

Extension methods

public static class ExtensionMethods
{
	// using 'this' before Transform
	public static void ResetTransform(this Transform trans)
     {
	    trans.position = Vector3.zero;
	    trans.localRotation = Quaternion.identity;
     }
}
public class AnotherClass : MonoBehaviour
{

	void Start()
     {
	    // now you can access that method from transform
	    transform.ResetTransform();
     }
}

Types, classes and structs

Arrays

string[] names; // most used

string[] names = new string[5];

string[] names = new string[] {  "Jonathan", "Simon", "Alicia", "Lila" };

Lists

// A list is like an array, but you can populate it in runtime.
List<GameObject> enemiesToSpawn = new List<GameObject>();

Add an element

// adds an element to the list, puts it in the last index
list.Add(element)

Insert element at x index

// inserts the element "George" at the index 1 of the list
// increases the index of the elements that come after "George"
list.Insert(1, "George");

Enums

    // declare enum, they're easier to read
    [SerializeField] enum LevelSelector
    {
        Easy,
        Normal,
        Hard // last one without comma
    }

    [SerializeField] LevelSelector currentLevel; // dropdown in inspector

    void Start()
    {
        switch (currentLevel)
        {
            case LevelSelector.Easy:
                Debug.Log("Selected 'Easy' ");
                break;
            case LevelSelector.Normal:
                Debug.Log("Selected 'Normal' ");
                break;
            case LevelSelector.Hard:
                Debug.Log("Selected 'Hard' ");
                break;
        }
    }

Enum value type

// by default each enum item has a underlying type, starting with 0

enum Example = { Alive, Tired, Dead };

// Alive = 0 ; Tired = 1 ; Dead = 2
// You can assign a value to the first item and the next items will increase from that value

enum Example = { Alive = 2, Tired, Dead };

// Alive = 2 ; Tired = 3 ; Dead = 4
// Or you can have non-consecutive values

enum Example = { Alive = 5, Tired = 13, Dead = 2 };

// by default, the underlying type is int, but you can change that

enum Example : byte = { Alive, Tired, Dead };

// you can choose only from: byte, sbyte, short, ushort, int, uint, long and ulong

Casting enum to int

    enum Difficulty
    {
        Easy,
        Normal,
        Hard
    }

    [SerializeField] Difficulty selectedDifficulty;

    void Start()
    {
        // cast enum to int
        SceneManager.LoadScene((int)selectedDifficulty);
    }

Dictionaries

    // use dictionaries when having big lists
    // as each value is associated to a key
    // you don't need to check every item to find
    // a specific one
    public Dictionary<int, Item> itemDatabase = new Dictionary<int, Item>();

    void Start()
    {
        Item sword = new Item();
        sword.name = "Sword";
        sword.id = 0;

        Item bread = new Item();
        bread.name = "Bread";
        bread.id = 0;

        itemDatabase.Add(0, sword);
        itemDatabase.Add(1, bread);
    }

Get value from key

void Start()
{
    // from the key "Juan" get the value (int)
    var value = students["Juan"];
    print(value);
}

Get key from value

// Linq is used later in this post

// Using System.Linq
// remember that the same value can be found twice in a dictionary
// in the dictionary students
// from student 's' where the value of s is equal to 15, get the key
// It gets the first match
// if there's no match returns a default value (null)
// .First() on the other hand will throw an error
var key = students.FirstOrDefault(s => s.Value == 15).Key;
print(key);

// Query Syntax
// from student 's' in dictionary 'students'
// where the value of 's' is equal to 14
// select that student
// and access the key of the first match
var secondKey = (from s 
            in students 
            where s.Value == 14 
            select s).FirstOrDefault().Key;
print(secondKey);

Dictionary collection initializer

// creates a dictionary called students and initializes it with key-value pairs
Dictionary<string, int> students = new Dictionary<string, int>()
{
   {"Juan", 14},
   {"Matilda", 21},
   {"Romero", 18}
};

Use of serialized classes

(Might wanna use a ScriptableObject to store this kind of data instead)

[System.Serializable]
public class Item
{
    public int itemID;
    public string name;
    public string description;
}

public class array : MonoBehaviour
{
    // creates an array of class Item
    // each item in myItems includes itemID ; name ; description
    public Item[] myItems; 

    void Update()
    {
        if (Input.GetKeyDown(KeyCode.Space))
        {
           foreach(var item in myItems)
            {
                print(item.itemID);
                print(item.name);
                print(item.description);
            }
        }
    }
}

Class Inheritance

// This allows you to inherit common properties from a general class, and apply those to more specific class, for example, if you want to have certain properties for each item, you create a general item class, then you can create another specific class for weapons, an inherit the properties from the Item class into the weapon class.

[System.Serializable]
public class Items
{
    // this is a general item class

    public string name;
    public int index;
    public string description;
    public Sprite image;

}
[System.Serializable]
public class Weapons : Items // inherits properties from the Items class
{
    public int damage;
    public int bleed;
}
[System.Serializable]
public class Consumables : Items // inherits properties from the Items class
{
    public int health;
    public int poison;
}
public class ItemDatabase : MonoBehaviour
{
    // an array for each type of item
    [Space(10)] // adds space between
    public Items[] otherItems;
    [Space(10)]
    public Weapons[] weapons;
    [Space(10)]
    public Consumables[] consumables;
}

Abstract classes

They cannot be instantiated by themselves, and they can force the declaration of methods to the classes that implement them.

public abstract class Employee : MonoBehaviour
{
    public string companyName;
    public string employeeName;

    // forces to implement
    public abstract void CalculateMonthlySalary();
}
public class Fulltime : Employee
{
    [SerializeField] int earnedPerDay;

    void Start()
    {
        CalculateMonthlySalary();
    }

    public override void CalculateMonthlySalary()
    {
        Debug.Log(employeeName + " will earn: $" + earnedPerDay * 24);
    }
}
public class PartTime : Employee
{
    [SerializeField] int hoursWorked;
    [SerializeField] int hourlyRate;

    void Start()
    {
        CalculateMonthlySalary();
    }

    public override void CalculateMonthlySalary()
    {
        int salary = hoursWorked * hourlyRate;
        Debug.Log(employeeName + " will earn: $" + salary);
    }
}

Interfaces

public interface IDamageable<T>
{
    int health {get; set;} // cannot have fields

    void TakeDamage(T damageAmount); // generic
}
// interface forces implementation
// allow to fake multiple inheritance, you can inherit many interfaces
public class Player : MonoBehaviour, IDamageable<int>
{
    int IDamageable.health { get; set; }

    void IDamageable.TakeDamage(int damageAmount)
    {
        // implement here
    }

Const vs readonly

// value needs to be assigned at declaration
const int maxHealth = 100;

// can be assigned at declaration or from constructor
readonly int maxHealth;

Ref Keyword

To pass arguments as reference instead of value in methods, they need to be initialized before being passed into the method, properties with get and set can’t be used as ref. You’re not obligated to update the reference parameter.

    public static string UpdateDeathCount(ref int countReference)
    {
        countReference++;
        return "Next time you'll be at number " + countReference;
    }

    public static void DebugDeath()
    {
        Debug.Log("Player deaths: " + playerDeaths); // 0
        string message = UpdateDeathCount(ref playerDeaths);
        Debug.Log("Player deaths: " + playerDeaths); // 1
    }

Out keyword

Same as ref, but arguments don’t need to be initialized before passing into the method, but you need to initialize the parameter before returning the method.

    public static string UpdateDeathCount(out int countReference)
    {
        // needed
        // any data passed when calling method
        // is overridden by this mandatory initialization
        countReference = 1;
        countReference++;
        return "Next time you'll be at number " + countReference;
    }

    public static void DebugDeath()
    {
        Debug.Log("Player deaths: " + playerDeaths); // 0
        string message = UpdateDeathCount(out playerDeaths);
        Debug.Log("Player deaths: " + playerDeaths); // 2
    }

Singleton pattern

Useful in cases you only have one instance of a class, for example in managers.

    // reference to this GameManager, static as only one should exist
    static GameManager _instance;
    // reference for external classes
    public static GameManager Instance
    {
        get // only able to get, but not set
        {
            // checks if there's no instance of 'gamemanager'
            if(_instance == null)
            {
                // if there's not an instance, log an error
                Debug.LogError("There's in no GameManager instance");
            }

            // if there is, return this 'gamemanager' to the external reference
            return _instance;
        }
    }

    void Awake()
    {
        // add this object 'gamemanager' to _instance
        _instance = this;
    }

    public void MethodExample(string text)
    {
        Debug.Log(text);
    }
    void Start()
    {
        GameManager.Instance.MethodExample("This is a test");
    }

Lazy Instantiation

        get // called when getting from another class
        {
            if(_instance == null)
            {
                // if it doesn't exist
                // create game object
                GameObject go = new GameObject("SpawnManager");
                // to that game object add the SpawnManager.cs
                go.AddComponent<SpawnManager>();
            }

            return _instance;
        }

The disadvantage of lazy Instantiation is that you will need to find a way to dynamically populate fields in the class if you’re instantiating the manager.

Non-Monobehavior Singleton

// doesn't inherit from Monobehavior
public class GameManager
{
    #region "Singleton"
    static GameManager _instance;
	public static GameManager Instance
	{
		get
		{
			if (_instance == null)
			{
				_instance = new GameManager();
				Debug.LogWarning("There was no " + nameof(GameManager) + " instance, an instance was created.");
			}
			return _instance;
		}
	}
    #endregion

Detecting an interface with Raycast

 void Update()
    {
        if (Input.GetMouseButtonDown(0)) // if you press left click
        {
            // ray origin to mouse position
            Ray rayOrigin = Camera.main.ScreenPointToRay(Input.mousePosition); 
            RaycastHit hit;

            if(Physics.Raycast(rayOrigin, out hit))
            {
                // get component IDamageable from object hitted
                IDamageable obj = hit.collider.GetComponent<IDamageable>();

                // if the object inherits from IDamageable
                if (obj != null) 
                {
                    // call the function take damage from that object
                    obj.TakeDamage(509); 
                }
            }
        }
    }
public class Player : MonoBehaviour, IDamageable
{
    int IDamageable.health { get; set; }

    void IDamageable.TakeDamage(int damageAmount)
    {
        this.GetComponent<MeshRenderer>().material.color = Color.red;
    }
}

Delegates

    // delegates acts as variables who can store multiple methods
    // creates a public delegate with signature Name(Color name)
    public delegate void ChangeColor(Color newColor);
    public ChangeColor onChangeColor;

    // creates public delegate with signature Name()
    public delegate void OnComplete();
    public OnComplete onComplete;

    void Start()
    {
        // assigns 'public OnComplete onComplete' to UpdateColor method
        onChangeColor = UpdateColor;
        // calls UpdateColor from delegate
        onChangeColor(Color.green);

        // adds 3 methods to OnComplete delegate
        onComplete += Task;
        onComplete += Task2;
        onComplete += Task3;

        if(onComplete != null)
        {
            // calls onComplete, Task, Task2 and Task3 are called
            onComplete();
        }
    }

    // creates public method with same signature as ChanceColor delegate
    public void UpdateColor(Color colorToUse)
    {
        Debug.Log("Changing color to: " + colorToUse);
    }

    // creates public method with same signature as OnComplete delegate
    public void Task()
    {
        Debug.Log("Completed Task 1");
    }

    public void Task2()
    {
        Debug.Log("Completed Task 2");
    }

    public void Task3()
    {
        Debug.Log("Completed Task 3");
    }

Events

    // creates delegate
    public delegate void ActionClick();
    // allows to be subscribed or unsubscribed from other classes
    public static event ActionClick onClick;

    public void ButtonClick()
    {
        // checks if other classes are 'listening'
        if(onClick != null)
        {
            // calls the event
            onClick();
        }
    }
    void OnEnable()
    {
        // adds to event when object is created or enabled
        Main.onClick += TurnRed;
    }

    // method with same signature to add to event
    public void TurnRed()
    {
        GetComponent<MeshRenderer>().material.color = Color.red;
    }

    void OnDisable()
    {
        // unsubscribes from event
        Main.onClick -= TurnRed;
    }

Actions

using System; // needs to use System namespace

public class Actions : MonoBehaviour
{
    // cleaner
    public static Action<int> OnDamageReceived;

    // =
    //public delegate void OnDamageReceived(int health);
    //public static event OnDamageReceived onDamage;

Func

    // Func<parameter, return type>
    public Func<string, int> CharacterLength;

    void Start()
    {
        // assigns GetCharacter to Func
        CharacterLength = GetCharacters;
        // assigns return from method to int
        int count = CharacterLength("Simon");

        Debug.Log("Number of characters: " + count);
    }

    // return type method
    // matches Func<string, int> signature
    int GetCharacters(string name)
    {
        // returns int with number of characters in string 'name'
        return name.Length;
    }

Lambda expressions

    public Func<string, int> CharacterLength;

    void Start()
    {
        // lambda operator, parameter string 'name' '=>' 
        // goto 
        // return type int 'name.Length'
        CharacterLength = (name) => name.Length;
        int count = CharacterLength("Jonathan");

        Debug.Log("Number of characters: " + count);
    }

Void delegate with parameters

    public Action<int, int> Sum;

    void Start()
    {
        // avoid creating methods with lambda
        // brackets useful when there's no return type
        Sum = (a, b) =>
        {
            int total = a + b;

            Debug.Log(total);
        };

        Sum(2, 2);
    }

Void delegate with no parameters

    public Action onName;

    void Start()
    {
        onName = () => Debug.Log("Name: " + gameObject.name);

        onName();
    }

Return delegate with no parameters

    public Func<int> GetName;

    void Start()
    {
        GetName = () => gameObject.name.Length;
        int nameLength = GetName();

        Debug.Log(nameLength);
    }

Return delegate with parameters

    // third 'int' is return type
    public Func<int, int, int> Sum;

    void Start()
    {
        Sum = (a, b) =>
        {
            //code
            // return needed if braces
            return a + b;
        };

        int total = Sum(5,5);

        Debug.Log(total);
    }

Protected

They’re only accessible to the same class and the classes who inherit from that class.
The difference between protected and private, is that protected allows classes to inherit and access those variables.

Static

They’re shared with all instances of a class, static variables can be accessed without instantiating an object of the class, if you change a static variable in one class, it will change in all of the others.

Statics to create Utilities

public static class UtilityHelper 
{
    public static void CreateCube()
    {
        GameObject.CreatePrimitive(PrimitiveType.Cube);
    }

    public static void SetPositionZero(GameObject obj)
    {
        obj.transform.position = Vector3.zero;
    }
}
void Update()
    {
        if (Input.GetKeyDown(KeyCode.Space))
        {
            UtilityHelper.CreateCube();
        }

        if (Input.GetKeyDown(KeyCode.E))
        {
            UtilityHelper.SetPositionZero(this.gameObject);
        }
    }

Properties

They can be read (get) only, write (set) only, or both, they can be accessed as public, at the same time, you can make them run code when you read or write a property. You cannot initialize them, you cannot see them in inspector (unless you’re in debug mode.)

public bool IsGameOver { get; private set;} // can be read in all classes, but only written in this class, same goes for protected.
bool isGameOver

public bool IsGameOver // first letter has UpperCasing
{
	get
	{
		// script to execute when reading property
		return isGameOver;
	}
	set
	{
		// script to execute when writing property
		isGameOver = value; // value depends on the value type of property
	}
}

Namespaces

using System.Collections; // namespace
using System.Collections.Generic; // namespace
using UnityEngine; // namespace

Useful to organize code, and to avoid conflicts when you have two classes with the same name.

namespace WeaponPack.Magic
{
	public class Weapon : MonoBehaviour
     {
     // code
     }
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using WeaponPack.Magic;

public class Weapon : MonoBehaviour
{
// code
}

Trigonometry and Math

Vectors

A vector is a struct that stores three variables (x,y,z), it has a direction and a magnitude.

The magnitude is calculated with Pythagoras theorem:

Vector2(-4, 3)
Magnitude of that vector = sqrt((-4)^2 + 3^2)
Magnitude = 5

To keep the direction of the vector, but with magnitude of 1, you need to normalize it:

Vector2(-4, 3)
magnitudeOfTheVector = 5
x1 = -4 / magnitudeOfTheVector
y1 = 3 / magnitudeOfTheVector

Normalized vector = sqrt(x1^2 + y1^2)
Normalized vector = sqrt((-0.8)^2 + 0.6^2) = 1

Let’s say we want to create a vector from VectorA to VectorB:

VectorC = VectorB – VectorA

You can get the distance between VectorA to VectorB by calculating the magnitude of VectorC. And you can get the direction from VectorA to VectorB by normalizing VectorC.

Raycasting

    // Info taken from a tutorial from Sebastian Lague
    // The layers the ray can interact with
    [SerializeField] LayerMask mask;

    void Update()
    {
        // creates a new ray from the position of this object
        // with a direction of the forward vector of this object
        Ray ray = new Ray(transform.position, transform.forward);
        // creates a variable to store what's hitted by the ray
        RaycastHit hit;

        // returns true if something has been hitted
        // first, the ray that we created
        // 'out hit' to store what's been hitted 
        // (it will only hit objects with collider)
        // we can choose a mask to filter
        // we can determine a range, '25' for example
        // we can choose if we want to ignore colliders that are triggers
        if(Physics.Raycast(ray, out hit, mask, 25, QueryTriggerInteraction.Ignore))
        {
            // we can store the hitted object in a GameObject
            // and manipulate that object
            GameObject hitted = hit.collider.gameObject;
            // we can draw line in the Scene View 
            // from the origin of the ray, to the point the ray hits, red color
            Debug.DrawLine(ray.origin, hit.point, Color.red);
            Destroy(hitted);
        }
        else{
            // if nothing has been hitted
            // we can draw another line
            // from the origin of the ray
            // towards the direction of the ray multiplied by a range
            // with color green
            Debug.DrawLine(ray.origin, ray.origin + ray.direction * 100, Color.green);       
        }
    }

Important stuff

For a Layer Mask to work properly when Raycasting, you need to assign also a maxDistance, this is because the layer mask is treated as an int, therefore Unity can mistakenly use the layer mask as a max distance, to set an infinite max distance, use Mathf.Infinity.

Angles n’ stuff

Vector math

Vector math

Lerp

    Light _light;

    void Start()
    {
        _light = GetComponent<Light>();
    }

    void Update()
    {
        // smoothly increases the intensity of the light overtime
        // from the value of _light.intensity to 8f
        // at a 50% rate per second (Time.deltaTime)
        _light.intensity = Mathf.Lerp(_light.intensity, 8f, 0.5f * Time.deltaTime);
    }

Input

GetKey

        //GetKeyDown detects when you press a key
        if (Input.GetKeyDown(KeyCode.Space))
        {
            print("Space has been pressed");
        }

        //GetKey detects when you're holding down a key
        if (Input.GetKey(KeyCode.Space))
        {
            print("Space is being pressed");
        }

        //GetKeyUp detects when you release a key
        if (Input.GetKeyUp(KeyCode.Space))
        {
            print("Space has been released");
        }

Transform manipulation

Assign position

    Vector3 startingPos = new Vector3(0, 0, 0);

    void Start()
    {
        transform.position = startingPos;
    }

Using Axis

    float speed = 5f; // Speed variable

    void Update()
    {
        // Storing the value of the axis input to a float (-1 = left ; 1 = right)
        float horizontalAxis = Input.GetAxis("Horizontal");

        // Storing the value of the axis input to a float (-1 = down ; 1 = up)
        float verticalAxis = Input.GetAxis("Vertical"); 

        // Translate the object using the axis, multiplying by the variable of speed and multiplying by deltatime
        transform.Translate(new Vector3(horizontalAxis, verticalAxis, 0) * speed * Time.deltaTime); 
    }

Time

Pause (Time.timeScale)

	bool isPaused;

	void Update()
	{
		if (Input.GetKeyDown(KeyCode.Space)) 
		{
			// inverts pause
			isPaused = !isPaused;
			// if is paused is true, timeScale is 0
			// if not, timeScale is 1
			Time.timeScale = isPaused ? 0 : 1;
		}
	}

Linq

Contains

using System.Linq; // needed for linq use

public class UsingLinq : MonoBehaviour
{
    public string[] names = { "jon", "marcos", "aurelio" };

    void Start()
    {
        // determines if element exists or meets condition
        bool nameFound = names.Any(name => name == "aurelio");

        // returns true or false
        Debug.Log("Name found: " + nameFound);
    }

// or

    void Start()
    {
        bool nameFound = names.Contains("jon");

        Debug.Log("Name found: " + nameFound);
    }

Distinct

    public string[] names = { "jon", "marcos", "aurelio", "aurelio", "marcos" };

    void Start()
    {
        // create new collection only with unique names
        var uniqueNames = names.Distinct();

        foreach(string name in uniqueNames)
        {
            print(name);
        }
    }

Where

    void Start()
    {
        // creates new collection with names that meet a condition
        var longNames = names.Where(name => name.Length > 5);

        foreach(string name in longNames)
        {
            print(name);
        }
    }

Order

        var passingGrades = grades.Where(grade => grade > 69);
        var orderedGrades = passingGrades.OrderByDescending(grade => grade);

        foreach(int grade in orderedGrades)
        {
            print(grade);
        }

Query syntax

        // query syntax
        var scoreQuerySyntax =
            from score in scores
            where score > 80
            select score;

        // is equal to

        var scoreQuery = scores.Where(score => score > 80);

Attributes

[Range(x, y)] – Before a variable to get a slider. (min, max)

[Space] – Adds a space in the Inspector

[Header(“text”)] – Adds a title in Inspector

[Tooltip(“text”)] – Adds a description in Inspector while hovering the mouse.

[HideInInspector] – Hides variable in Inspector

[SerializableField] – Shows private variable in Inspector

[TextArea] – Adds a text box (attribute before string variable)

[Multiline] – Similar to TextArea but places the text box at the right side of the label.

[ContextMenu(“MethodName”)] – Makes it possible to call a method from Inspector’s component contextual menu

[ContextMenuItem(“Text to show”, “MethodName”)] – Before a variable, makes it possible to call a method in the context menu of that variable

Context menu: the contextual menu is the menu that appears generally when right clicking.

Class Attributes

[DisallowMultipleComponent] – You cannot add two of the same class to the same game object.

[HelpURL(“https://www.google.com/”)] – Adds the possibility to access documentation to your own class from the Inspector (the little blue book)

[RequireComponent(typeof(Class))] – Forces the component of type Class to be in the same game object as the class where the attribute is applied.

[ExecuteInEditMode] – Executes the code in the class without the need of playing.

[SelectionBase] – If added for example to a class attached to the parent, it selects the parent in Scene View instead of the children.

Other Attributes

[UnityEditor.MenuItem(“Menu/Sub menu/Sub menu”)] – Adds a button in Unity’s menu so you can call a method, this attribute needs to be added before the method.

Other

Saving data

PlayerPrefs: Stores and accesses data between game sessions, in Windows it’s stored in the registry (where it’s stored depends on the Game name and Company name set up in Project Settings)

	[SerializeField] int number = 10;
	[SerializeField] string name = "simon";

	void OnValidate()
	{
		PlayerPrefs.SetInt("Saved int", number);
		PlayerPrefs.SetString("Saved string", name);
		PlayerPrefs.Save();
	}

number was changed to 12 in the inspector, and name was changed to ‘test’

Json

      [SerializeField] int number = 10;
      [SerializeField] string nametest = "simon";
      [SerializeField] Color color;
      [SerializeField] AnimationCurve curve;

	// when you enable the object again loads the data
	void OnEnable()
	{
		// replaces the data from an object with a json
		// first parameter is the json file
		// in this case we're calling the json written to registry by PlayerPrefs
		// and we're assigning the data to this object
		JsonUtility.FromJsonOverwrite(PlayerPrefs.GetString("json file"), this);
	}

	// when you disable the object in play mode saves the data
	void OnDisable()
	{
		// sets a key in registry with the name json file
		// the value is JsonUtility.ToJson()
		// it generates a json of the public fields
		// or fields marked with [SerializeField]
		// first parameter is the object to save the parameters from (this script)
		// second parameter indicates if the data in the registry should be easy to read
		// by default is false, which makes files smaller.
		PlayerPrefs.SetString("json file", JsonUtility.ToJson(this, true));
		// writes modified prefs to disk
		PlayerPrefs.Save();
	}

Optimization tips

Code for readability first, profile, and then optimize.

  1. Eliminate calls to empty Update functions (they still use resources)
  2. If you need to check something every frame, use Update(), if not, try using Coroutines or InvokeRepeating, as this is more performant.
    1. Instead of using ‘yield return new WaitForSeconds(x)’, cache that WaitForSeconds(x) to avoid garbage.
      WaitForSeconds wait = new WaitForSeconds(1f); 
  3. Move things out of Update when they don’t need to be called or calculated every frame, for example, avoid getting components in Update or searching for objects.
  4. Avoid calculating the same thing twice or more times, put it in a variable.
  5. Use sqrMagnitude to calculate distances and use that value to compare distances if needed, you save a square root calculation.
  6. Cache the main camera and transforms, Unity performs a get function when you use Camera.main directly, same with transform.
  7. Avoid instantiating or destroying objects during gameplay, instead, use object pooling.

You can use Samples to make things more clear in the Profiler

using UnityEngine.Profiling;

public class MyClass: MonoBehaviour
{
    void Update()
{
            Profiler.BeginSample("DEBUGING");

            Debug.Log("object destroyed");

            Profiler.EndSample();
           }
}

Share this

Leave a Reply