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

Open Closed Principle

October 20, 2020

Open Closed Principle

Programming


Title: SOLID Principles in C# – Open Closed Principle
By: Marinko Spasojevic
CodeMaze – Information
Description: Quick coverage of the open closed principle for programming with C# examples.


Overview

“The Open Closed Principle (OCP) is the SOLID principle which states that the software entities (classes or methods) should be open for extension but closed for modification.” This basically means it is preferable to write code in such a way that when a change or extension is suggested for that original class that it can be done through addition instead of modifying the existing class itself. Many of the examples shown do this by adding new classes to handle the additional functionality required instead of modifying the original classes created.

Example #1 – Salary Calculator

The first example they cover somewhat resemebles the factory pattern tutorial I was checking out recently that introduced me to the open closed principle. In this example, they initially create a single class to handle everything. It has a constructor to take in all the members having their salaries taken into accoutn, and then it has a single method handling all the logic of summing all their salaries. As soon as a request for a modification comes in, that single large method within the only class must be edited to accomodate for the new special cases, which clearly violates the Open Closed Principle.

Their suggested revision of the project is what resembles the factory pattern example I was checking out. Instead of just creating the singular SalaryCalculator class, they create an abstract BaseSalaryCalculator class. Next each salary case is broken down into its own class which inherits from this BaseSalaryCalculator class, and these classes internalize all the logic for calculating their salaries so they can individually deal with the differences. Using this approach, if any new options or exceptions need to be accounted for, a new small class inheriting from that BaseSalaryCalculator class can be made without editing any of the existing classes.

Example #2 – Filtering Lists of Computer Monitors

This example provided a list of computer monitors that each had a name, a monitor type, and a screen type. They then needed to be able to filter the list of computer monitors based on the type of monitor. To do so they made a single MonitorFilter class which used a method to filter out the monitors of that specific type and return a list of them. Later they need to add functionality to filter by screen type as well. They accomplish this by adding another method to the existing MonitorFilter class, which again violated the OCP.

Their OCP solution follows a similar idea to the first example, except instead of breaking it down to an abstract class that is then used for several smaller classes with their own logic they use interfaces instead. They create two interfaces, ISpecification and IFilter. The original MonitorFilter class is modified so that it can implement the IFilter interface and is made to be more generic. ISpecification is the interface implemented for the various classes to fit the various filter requests.

MonitorTypeSpecification implements the ISpecification interface and has a constructor that just sets an internal monitor type so that its internal isSatisfied bool method returns true if it matches the monitor type passed in. This is then used in conjunction with the new flexible filter class that creates a list of objects of only those that satisfy the isSatisfied bool within the ISpecification implementing class. Later for the screen type filtering case they create a separate ScreenSpecification class which again implements the ISpecification interface. The only difference in this class is that the Scree enumerator is used and set with the constructor as opposed to the MonitorType enumerator.

Noted Benefits

When working with an already proven and tested system, extending it reduces the likelihood of impacting the full system as opposed to modifying it. Avoiding modifying the original class significantly reduces the chance of changing something that other parts of the system rely on. Extending this way also makes testing easier as it is more likely the new feature can be isolated in testing with this approach, where as modifying the original class likely means that testing of originally tested features may need to be done again since it was more directly modified.

Summary

OCP is a nice philosophy to follow when possible and is extremely applicable to game development projects. Features are modified and added constantly in the game development process, so using an approach like this could prove very beneficial for cleanly and efficiently dealing with the everchaning project. As someone interested in procedural content generation as well, this concept seems very applicable dealing with all the varied but similar features one would encounter with that type of content.

Factory Pattern Basics for Creating Objects

October 20, 2020

Factory Pattern

Unity


Title: Creating Objects in Unity3D using the Factory Pattern
By: Jason Weimann
Youtube – Tutorial
Description: Introductory tutorial to the concept of fatories and the factory pattern in OOP using Unity.


Overview

This tutorial quickly covers the basic idea behind using factories and the factory pattern in OOP through an example in Unity. The overall concept is pretty simple in that it focuses on using an object to create other objects, but they explore putting this idea to use as well as some of the pros and cons of its use.

Notes

Factory: in OOP, an object for creating other objects; a function or method that returns objects of a varying prototype or class (from Wikipedia)
Some of the benefits noted are that this allows you to put all of your object creation into a single place and that you can extend it quite a bit without modifying the underlying logic (following the Open Closed Principle). You do not even need to know the exact type you are expecting to return with the factory since you can return interfaces or abstract classes as well to cover large groups of object types.

Basic Example

In the examples covered, their common practice is to create an abstract class for the type of objects they will want their factory to return. Then any of the objects you want that factory to be responsible for simply inherit from that abstract class. Then the factory class itself is created that uses some method to identify which specific objects to create and uses that information to return the desired objects.

Advanced Example

While not exactly advanced, this example is more in the direction of something that you would actually use. Again the abstract class is created to encompass the objects the factory will return, but this time a Name string with only a getter method is added to this class to help with identification and selection later in the process.

The factory class is much more involved for this example. They create a dictionary that uses the Name values as the keys and the various specific types of the different objects as the values. They then use an Assembly reference to get a reference to each of the different classes inheriting from the initial abstract class throughout the section of the project they are interested in. Using these together they fill the dictionary with all these type references by going through the list gotten from the Assembly reference and adding them to the dictionary.

They then have a method within the factory class to Get an object from it. This requires a string parameter (as this is what is used for the example as the keys for the dictionary created) and uses that to check if that option exists in the dictionary, and if so it creates and returns an instance of that specific type of object.

Final Example

This final example is closest to something they would actually use in a real world situation. The major difference being that they make the factory class into a static class. They use this approach because they do not want to create a new factory everytime they use it, and they did not want to use a singleton pattern because it does not need instantiated in Unity in anyway. This helps make it accessible for anything else to call into it.

While it is not a singleton in and of itself, it does use some similar redundancy approaches to make sure multiple are not created. It has an isInitialized check so that it is only created if it is used at all, and then afterwards it will not create another of itself. It is a bit over redundant, but it works for a quick coverage of the concept in this situation.

Extending the Pattern

One major benefit of this pattern over very basic ways of creating objects covered is how it follows the open closed principle. This is shown after the advanced example by adding another type of object for the factory to return after everything has been built. They create a new class of object again inheriting from that initial abstract class and show that the factory picks up on this and includes it in its options and creates the new object without ever accessing the factory class again. This was cleanly done by just adding its own new class and not modifying any of the previous classes.

Summary

While a very basic concept overall, breaking it down and covering its pros and cons did help me more clearly understand how to look at designing and developing systems to create objects in OOP. This also seems like a nice simple approach to starting to use systems for projects that are not very tiny in size at least, and could be more useful for larger projects when you have a better understanding of how to utilize it in a more complex manner. On a side note this also exposed me to the open closed principle which makes a lot of sense as something to strive for when looking to create more stable projects.