UnityLearn – Beginner Programming – Pt. 02

October 8, 2019

Beginner Programming: Unity Game Dev Courses

Beginner Programming: Unity Game Dev Courses

Unity Learn Course – Beginner Programming

Understanding Types

What is a Type

Overview: Types of types, “var” keyword, enumerations, generics
Data Type: data type or simply type is an attribute of data which tells the compiler or interpreter how programmer intends to use the data.

Types of types: Value types and Reference types
  • Value Types: Memory is allocated directly and inline on the stack
  • Examples: int, byte
  • Reference Types: Memory is allocated randomly on the managed heap
  • Examples: Classes, delegates, interfaces
Memory Allocation by Type
  • There are two main memory storage locations we are concerned with, the stack and the heap.
  • Value types and pointers to reference types are located in stack (pointers not directly used in C#)
  • Reference types are located in heap
Stack vs. Heap
  • Stack
    • Allocated when compiled
    • Data are stored sequentially
    • Variable size must be known
    • Not subject to garbage collection
    • Very fast
  • Heap
    • Allocated during runtime
    • Data are stored randomly
    • Variable size can be unknown
    • Subject to garbage collection
    • Slower

The var Keyword and Anonymous Type

The var keyword allows for implicit typing but must be used within method scope. C# can use the value of a var variable it is initialized to to determine what the type should be. This is implicit typing. This also means we cannot declare a var variable without intializing it. Type inference occurs at the compiler level, so it does not effect performance.

Anonymous Type
  • An unnamed container for properties
  • properties are read only
  • no type name available to source code
  • type of each property is inferred

Enumerations

Enumeration: value type that represents a set of related named integral constants; declared using enum keyword
The underlying type of each element of an enum is int. These default to having values of 0, 1, 2, etc. The underlying int values can be set for each element individually however if wanted.
Benefits of Enumerations:

  • value types
  • express and limit available options for a variable’s value
  • named values provide readability
  • leverage Intellisense

Generic Types

Generics allow you to defer type declaration until use.
Benefits of Generics:

  • flexibility
  • code reuse
  • performance
  • type safety

Generics are used with the syntax of , where T is a type parameter. An example is shown below:
private void Evaluate(T suppliedValue)
{
Debug.LogFormat(“the Type of {0} is {1}”, suppliedValue, typeof(T));
}

Generics can be used in similar scenarios where overloaded methods would make sense, but would clutter things because there are many different types you would like a similar method to be able to use. Since you are also using a single method for vaarious types, it will also be performing the same actions on those types, so overloaded methods may be better if you want different actions for different types.

Working with Groups of Types

Module Introduction and Setup

Overview: arrays, generic lists, dictionaries, Queues & Stacks

Arrays

There was nothing new here.

Generic Lists

Lists are part of the System.Collections.Generic namespace. Lists are a generic class that use the type T type parameter.

Dictionaries

Dictionary: in C#, it is a collection type defined in System.Collections.Generic namespace that can store any data types in the form of keys and values.
Similar to other type parameters, they are declared between angle brackets for dictionaries. In arrays and lists, the elements are referenced by an integer index. A dictionary allows you to use a different type of index to reference your collection elements.

Queues and Stacks

Queues and stacks are used for more transient data, as opposed to arrays, lists, and dictionaries which are for more persistent data. The main difference between queues and stacks is the order in which data is removed. Queue is FIFO (First in, First out), where stack is LIFO (Last in, First out).

Coroutines

Introducing Coroutines

Coroutine: In Unity, a function declared with a return type of IEnumerator that allows execution to be suspended and resumed using the yield keyword.
Coroutines allow execution to be suspended and resumed using the “yield” keyword. The execution will run until it hits this keyword, then suspend this operation based on the value returned. This lets Unity continue any other operations going on while retaining its place in the coroutine. The value returned determines how long execution will be suspended. For example, returning null simply suspends execution until the beginning of the next frame. This ability to suspend and resume coroutines is what makes them so useful. The fact that coroutines must be of the return type IEnumerator dictates what values it can return, which means it dictates what values can be used to determine the time its routine execution can be suspended.

Delaying Execution

Coroutine Wait Types:

  • WaitForSeconds(): waits using scaled time
  • WaitUntil(): suspends execution until supplied delgate evaluates to true
  • WaitWhile(): suspends execution until supplied delgate evaluates to false
  • WaitForEndOfFrame(): waits until the end of the current frame
  • WaitForFixedUpdate(): waits until the next fixed frame update
  • WaitForSecondsRealtime(): waits for some time, but uses unscaled time
Time.timeScale

This can be used for a slow motion effect, or even pausing the game in Unity. Setting this to 0 is a common technique for pausing a game. Since WaitForSeconds uses scaled time, this will also stop any coroutines using that as the return type. However, if the coroutine uses WaitForSecondsRealtime(), which uses unscaled time, it will continue to run even at a Time.timeScale of 0. This difference can be useful if you want certain UI elements to perform some action for example while your main game is paused.

IMPORTANT: Yield statement does not return out of the function, it simply suspends execution. When it resumes, it picks up right where it left off.

Running in Parallel

Coroutines can be used for parallel processing. Coroutines allow processes to be split across multiple frames. This can also be used to let them be run in parallel with other game logic to minimize impact on frame rate.

Module Summary

Stopping a Coroutine:

  • StopAllCoroutines(): stops all coroutines running on a MonoBehaviour
  • StopCoroutine(): stops a specific coroutine running on a MonoBehaviour

The way coroutines are stopped depends on how they were started. If started with a string, they must be ended with a string. If started with a reference to the routine, it must be stopped using that reference.

Coroutine Caveats

  • can result in unexpected behavior (it is very easy to accidentally run a coroutine more than once)
  • can make debugging more difficult, since one or more coroutines can be running at a time along with update
  • can be complex and difficult to manage