Accessing Variables in Other Scripts in Unity

May 26, 2021

Accessing Variables

Unity


Title:
How To Access Variables From Another Script In Unity

By:
Infallible Code


Youtube – Tutorial #1

Description:
Goes over many programming techniques within Unity to access variables in one script from another.


Key Concepts

Scripts

C# classes that inherit from MonoBehaviour.

Collaborators

Scripts that require a reference to at least one other script in order to function.

Dependencies

Objects that collaborators need in order to function.

Categories of Resolution

Where the burden of dependency resolution lies.

External Resolution

The responsibility of dependency resolution falls somewhere else.

Public Properties

Expose dependency of a public field or property


Simple and requires no overhead, but can easily make bugs that are also hard to debug. This is because it can be unclear where the dependencies lie.

Editor Properties

Expose serializable dependencies in the Unity editor.
Use SerializeField on attribute private properties.


Similar to public properties, but are much safer.

Unity Event Methods

Functions that are called when specific events occur at runtime.


This specific example covers methods like OnCollisionEnter(). These are functions that are called when specific events occur, and they help resolve dependencies when those events are triggered.

Internal Resolution

The script itself is responsible for resolving its own dependencies.

FindObjectByType() Method

Search for dependencies in the scene based on their type.
FindObjectsByType() returns a list of matching objects.

This can be tricky to use sometimes since you may not be sure of the state of your scene when this is called. It is also relatively expensive. Most consistent to use to find manager scripts, since they should confidently be active in the scene when called.

Static Instances

Expose dependency on a public static property.
Commonly used in the Singleton pattern.

Dependency Injections

A pattern for automating dependency resolution based on a configuration.

It allows you to configure all your dependencies in one place and have them resolve automatically based on that configuration.

Summary

I have used all these techniques before, but it is always good to find condensed lists of them all together to really help organize them. Determining the cleanest and safest way to pass around variables through scripts is something I work on a lot, so laying out these options to go through in an effort to determine the best approach is always helpful. This also led me to some other resources on the topic that I am looking forward to investigating.

via Blogger http://stevelilleyschool.blogspot.com/2021/05/accessing-variables-in-other-scripts-in.html

Singletons in Unity and Many of Their Downfalls by Infallible Code

May 26, 2021

Singletons

Unity


Title:
Everything You Need to Know About Singletons in Unity

By:
Infallible Code


Youtube – Tutorial #1

Description:
Goes over the true properties of singletons in code and why they should be used sparingly in Unity game development.


Intro to Singletons

Singleton Pattern: class that is both globally accessible and can only be instantiated once

Common Example:

Audio Manager – because it is something many scripts will want access to, and you specifically want only one in your game at a time

Singleton Requirement #1: Global Access Specification

Singletons sound promising because they are an easy solution to “how do you access other scripts” or “how do you fill your class dependencies”. Using Singletons for access can hide the fact that a class depends on another, which can lead to difficult to track bugs and difficulties. This may lead to using the singleton even more than necessary because of ease of access, which creates more and more hidden dependencies.

Example #1

Non-Singleton Version

public class GameManager : MonoBehaviour
{
	[SerializeField] private Player player;
	
	privat void OnPlayerDied()
	{
		print($"Final Score: {player.Score}");
	}
}

Singleton Version

public class GameManager : MonoBehaviour
{
	privat void OnPlayerDied()
	{
		print($"Final Score: {Player.Instance.Score}");
	}
}

Because the Singleton pattern accesses a specific class directly, additions of derived classes will require more updates that may also be hard to find because of the hidden dependencies. Again, doing something similar to the Non-Singleton version of Example #1 makes it so that an additionally created derived class could fill the Player dependency with little to no edits.

Singleton Requirement #2: Restricted to a Single Instance

This is generally accomplised by creating a private constructor for the class, and then instantiating a single instance from the getter. This is already difficult in Unity as many classes you create in Unity will be MonoBehaviours, which cannot be instantiated in code with the new keyword. This is because Unity hides their constructor and allows anything to instantiate them through object.Instantiate().

So to help satisfy this, you need an extra check if another instance of the class already exists in the scene in case it was added directly in the Editor or through other object.Instantiate() calls. Once these are located, they may also require being destroyed to make sure there only exists one instance at any given time. This can also lead to strange results, as sometimes destruction prevention is used in Unity, such as with the DontDestroyOnLoad() method. Sometimes you may also get rid of and keep the “wrong” singleton if multiple somehow run into each other.

Singleton Criteria:

1) Absolutely required to exist only once
2) Must be accessible from every part of your code
3) Controls concurrent access to a resource

Summary

While I already knew that singletons should be used sparingly, having a better understanding of them fundamentally really helps put that into perspective just how sparingly they should be used. It also helps confirm that it can generally be a bad way of passing around data in Unity, and with a need that comes up so often and has so many options to be accomplished, it is good to generally remove an option from that list. It is also important to acknnowledge that in Unity specifically they are extra difficult to implement when it involves MonoBehaviours, as this again helps narrow down the process of passing around data.

via Blogger http://stevelilleyschool.blogspot.com/2021/05/singletons-in-unity-and-many-of-their.html

Coroutine Fundamentals in Unity

May 20, 2021

Coroutines

Unity


Title:
Introduction to Game Development (E21: coroutines)

By:
Sebastian Lague


Youtube – Tutorial #1

Description:
Covers the basics of coroutines in Unity.


Title:
Coroutines In Unity – What Are Coroutines And How To Use Them – Coroutines Unity Tutorial

By:
Awesome Tuts


Youtube – Tutorial #2

Description:
Covers some intermediate complexity uses of coroutines as well as tying them in with Invoke calls.


Title:
Coroutines – Unity Official Tutorials

By:
Unity


Youtube – Tutorial #3

Description:
Old official Unity tutorial on the fundamentals of coroutines in their system.


Overview

Coroutines appear to be a strong tool within Unity that I still am looking for ways to implement better in my systems. While understanding the basic uses for them with timing related events, I feel like they offer a lot more that I am not utilizing, so I wanted to further my understanding of them by following up on one of my older tutorial blogs and going through the tutorials it offers.

Tutorial #1 Notes

Coroutines are part of the Unity engine, and are not native to C#. They also belong to the Monobehaviour base class, so they class they are used in must inherit from Monobehaviour.

The coroutine return type is IEnumerator. Because of this, coroutines can also be stored in IEnumerator variables. This also allows them to be passed in as a paramter for the yield return statement within a Coroutine.

Use a reference to properly control the starting and stopping of a specific coroutine instance.

Tutorial #2 Notes

You can call a Coroutine from within itself (the same Coroutine). This is one way to cycle a Coroutine continously.

WaitForSecondsRealtime is similar to WaitForSeconds, except that WaitForSecondsRealtime is independent of Unity’s timescale. WaitForSeconds however is affected by the timescale. WaitUntil suspends the coroutine until the given delegate returns true.

Invoke Notes

  • Invoke – call a method after a given amount of time
  • InvokeRepeating – call a method every given amount of time, after a starting given time
  • CancleInvoke – stops an InvokeRepeating call

Tutorial #3 Notes

Coroutines are functions which execute in intervals. They work with special yield statements which return the code execution out of the function. Then, when the function continues, execution begins from where it left off.

Combining coroutines with properties can allow for efficient code which produces execution only when a value is changed, without the need to check for variable changes every frame with the Update() method or something similar.

Coroutine Return Statements

A substantial part of using and controlling coroutines is the various return statements you can use within them to determine when they should run and when they should pause execution. As such I listed several of these options and how they work within the coroutines.

yield return null

waits a frame before progressing to the next step

seems similar to the rate of the Update() method

yield return new WaitForSeconds(float time)

waits the given amount of seconds between each run of the process

yield return WaitForSecondsRealtime(float time)

waits the given amount of seconds, regardless of Unity’s timescale

yield return StartCoroutine(DoSomething())

waits until the given Coroutine “DoSomething” is finished running

yield return new WaitUntil(bool test)

suspends coroutine until the given parameter is true (opposite of WaitWhile)

yield return new WaitWhile(bool test)

suspends coroutine until the given paramter is false (opposite of WaitUntil)

Summary

Coroutines are starting to become a bit more clear to me, and this helped me learn a few more ways to incorporate them into my projects. Just seeing that they are specifically a Unity native concept was helpful to me as I did not think about that before, and that makes their somewhat strange nature make more sense in my head. This made it more clear that Unity is doing a lot of work behind the scenes to accomplish their main goals of determining when to start, pause, resume execution based on their yield values. Also the Unity note on tying them in with properties is very interesting and something I want to experiment with more myself for more efficient code.

via Blogger http://stevelilleyschool.blogspot.com/2021/05/coroutine-fundamentals-in-unity.html