What is a prefab?
What is the Event Loop?
How do we expose member variables to the Editor?
private void Foo(ref int a, out float b) {
b = a;
}
//...
void Start() {
int a = 10;
// This
float b;
Foo(ref a, out b);
// Is the same as
Foo(ref a, out float c);
}
Inherit or override funtionality from a specified base class.
In C# you can:
Provide a contract. Any implementing class or struct must adhere to the contract.
interface ISomeInterface {
void ImplementMe();
}
//...
class Implementer : ISomeInterface {
void ImplementMe()
// Method implementation
}
static void Main() {
ISomeInterface foo = new Implementer();
foo.ImplementMe();
}
}
Provide convenient encapsulation
class MyClass {
private string myField; // field
public string myProperty { // property
get { return myField; }
set { myField = value; }
}
public string myAutoProperty { // property
get;
private set;
}
}
A namespace provides a way to keep one set of names separate from another. The class names declared in one namespace do not conflict with the same class names declared in another.
// Include namespaces
using System;
// Use static methods without a type name
using static UnityEngine.Mathf;
// Type aliasing
using MyType = Some.Other.Type;
"Code templates" that allows developers to define type-safe classes and methods without committing to an actual data type.
class MyClass<T> {
private T mymember;
//...
public void Foo<U>(T classGenericType,
U methodGenericType) {
//...
}
}
Add constraints on generic type parameters
class MyClass<T> where T : class,
IComparable<T>,
new() {
// Not possible without the new() constraint
T item = new T();
}
Add methods to existing types without creating a new derived type, recompiling, or otherwise modifying the original type. Useful when you don't have the source code for the type.
public static class Extender {
public static void Reset(this Transform transform) {
transform.position = Vector3.zero;
}
}
int a = 1, b = 2;
// Instead of this
print("a = " + a + "; b = " + b + ";");
// You can do this
print($"a = {a}; b = {b};");
C# | C++ | Java | |
---|---|---|---|
Dynamic Array | List<> | vector<> | ArrayList<> |
Linked List | LinkedList<> | list<> | LinkedList<> |
Hash Set | HashSet<> | unordered_set<> | HashSet<> |
Hash Map | Dictionary<> | unordered_map<> | HashMap<> |
Tree | SortedSet<> | set<> | TreeSet<> |
Lets the compiler determine the type for you
// Explicit typing
Dictionary<int, int> dict = new Dictionary<int,int>();
// Implicit typing
var dict = new Dictionary<int, int>();
Instantiate an object and perform member assignments in a single statement
MyClass obj = new MyClass(arg, ...) {
memberVar = data,
...
};
// Anonymous type
var obj = new {
memberVar = data,
...
};
Like object initializers but for collections
//Has to implement IEnumerable and an Add function
List<int> list = new List<int> { 0, 1, 2 };
int[] arr = new int[] { 3, 14, 15, 92, 6 };
// This
for (int i = 0; i < arr.Length; ++i) {
print(arr[i]);
}
// Is the same as
foreach (var number in arr) {
print(number);
}
Reflection provides type info objects for everything in the language. It can, for example, be used to invoke private methods.
using System.Reflection;
//...
MethodInfo updateMethod = typeof(MyClass)
.GetMethod("Update",
BindingFlags.NonPublic | BindingFlags.Instance);
if (updateMethod != null) {
updateMethod.Invoke(this, null);
}
Provide metadata for code
[SerializeField]
private float speed = 5;
//...
FieldInfo speedField = typeof(MyClass)
.GetField("speed",
BindingFlags.NonPublic | BindingFlags.Instance);
if (speedField.GetCustomAttribute<SerializeField>() != null) {
speedField.SetValue(this, 6);
print($"The new values for speed is {speed}");
}
using System;
//...
Func<in argument, ..., out result> myFunction;
// For void methods
Action<in argument, ...> my Action;
// In the Player script
void ReceiveDamage(int damage) {
health = Max(health - damage, 0);
uiScript.ScaleHealthbar(health);
aiScript.TestAgro(health)
}
// In the Player script
public static Action<int> OnTakeDamage;
if (OnTakeDamage != null) {
OnTakeDamage();
}
// In the UI script
private void ScaleHealthbar(int health) {...}
Player.OnTakeDamage += ScaleHealthbar;
// In the AI script
private void TestAgro(int playerHealth) {...}
Player.OnTakeDamage += TestAgro;
// Always unsubscribe from the delegate when disabled
// This will prevent memory leaks
private void OnEnable() {
Player.OnTakeDamage += ScaleHealthbar;
}
private void OnDisable() {
Player.OnTakeDamage -= ScaleHealthbar;
}
Multicast delegates that restrict outside classes to only be able to subscribe and unsubscribe from the delegate
// Syntax
public static event Action<int> OnTakeDamage;
Execute your functions in parts by wraping them in an iterator
IEnumerable<int> GetCollection() {
List<int> collection = new List<int>();
for (int i = 0; i < 10; ++i) {
collection.Add(i);
}
return collection;
}
// Omits the collection declaration
IEnumerable<int> GetFancyCollection() {
for (int i = 0; i < 10; ++i) {
yield return i;
}
}
Regular functions execute within a single frame. Coroutines are special functions that can execute over several frames.
void Start() {
StartCoroutine(MyCoroutine());
}
IEnumerator MyCoroutine() {
while (true) {
//...
yield return new WaitForSeconds(3);
}
}
Coroutine c = StartCoroutine(...); // Starts the coroutine
StopCoroutine(c); // Stops the coroutine
yield return null; // Pauses until next frame
yield return new WaitForSeconds(x) // Pauses for x seconds
yield break; // Exits the coroutine