UnityLearn – Beginner Programming – Creating a Character Stat System – Pt. 03 – Accessing Variables in Our System Part 1

February 12, 2020

Beginner Programming

Creating a Character Stat System

Accessing Variables in Our System Part 1


Beginner Programming: Unity Game Dev Courses

Unity Learn Course – Beginner Programming

Accessing Variables in Our System Part 1

Equipping Weapons

They mostly just created the scriptable object, ItemPickUp_SO, for now so that the base of the EquipWeapon method would work. This method simply adds a designated amount to the character’s base stats.

Equipping Armor

Here they created two separate enums within ItemPickUp_SO to define options for the types of items and options for the types of armor. These enums on the scriptable objects were actually very nice in the editor as they provided drop down lists easily accessible to the designer.

The EquipArmor method in CharacterStats_SO used a switch statement, which goes well with enums. They however performed the same calculations with every single case (adding the different values to the player’s resistances), so I think it would be cleaner to just perform this calculation directly after the switch statement.

Un-Equipping Weapons

Using bool return type methods is useful for equipment systems since you normally want two different events to occur depending on if the player can and successfully equips the item, or if they are unable to equip the item. In their case, they just check if a weapon pickup is the same as the current weapon with the bool field within the bool method UnequipWeapon method. It also checks if there was previously a weapon, and it will destroy the current weapon, set it to null, and return the currentDamage to baseDamage.

Un-Equipping Armor

The UnequipArmor method followed the same idea as the UnequipWeapon method, where it had a return type of bool and returned a value based on whether the item passed in (the item being unequipped in this case) matched the item currently equipped to the player. Since this dealt with armor, they used a switch case again to account for all the various armor types so they could direct the actions at a specific armor slot on the character. This check was similar to what was done in UnequipWeapon again, as it checked if the current slot matched the item being unequipped and then adjusted the current stats (resistance in this case) to the base value and set that item slot to null.

This was repeated for every armor type in the switch case, with the slight difference that they had to change the item slot (specific ItemPickUp field on this particular CharacterStats_SO object). I found it easier to just create a helper method that took in an extra ItemPickUp parameter to determine which ItemPickUp slot was the one being tested and have that method do all the work for that specific slot. This way I only needed that block of code once in the script and only needed a single line for each case in the switch statement.

This ensures everything is uniform (which it currently is according to the tutorial) and keeps the code more organized and cleaner. This may have to be edited if different events are needed for different armor types in the future on unequip, but it’s much better currently.

SUMMARY

  • Enums are very nice on the editor side for scriptable objects since they provide an organized list of options to work with
  • Using bool return type methods can be useful with equip systems
  • Identify repeated code in switch statements to clean/organize code

UnityLearn – Beginner Programming – Creating a Character Stat System – Pt. 02 – Damage, Leveling Up, and Death

February 12, 2020

Beginner Programming: Unity Game Dev Courses

Creating a Character Stat System

Damage, Leveling Up and Death


Beginner Programming: Unity Game Dev Courses

Unity Learn Course – Beginner Programming

Damage, Leveling Up and Death

Encapsulation and Item Pickups

They cover encapsulation again just to explain why they use different access modifiers for different fields and methods within the scriptable objects they are creating to deal with the character’s stats and items.

Stat Increasers

They covered methods that increased the stats of the character. They were pretty basic, where they added values to specific stats and checked if the value would go over the max value to set it directly to max if that was the case.

Stat Reducers

This was very similar to Stat Increasers methods, but with subtraction and checked if the value would go below 0. This made me realize there’s a better way to do this where you check if a value would go below 0 BEFORE actually performing the operation, and then set it to 0 if that is the case. This prevents cases where you make a value negative for any step in the code which could result in weird problems down the road.

Leveling Up and Dying

They took an interesting approach to the level up process. They created a new class within the CharacterStat_SO scriptable object named CharLevelUps. These CharLevelUps objects just contained a bunch of int and float fields for different stats, and would be the amount to increase those base stats upon attaining the next level.

They then created an array of these CharLevelUps objects in the CharacterStat_SO class. You could then see this array in the editor and set a number of them to create (in this case, it would dictate the max level of the player). The designer could then set the individual values for each of these stats for each CharLevelUps object in the array to tell the system how much to increase each stat specifically upon attaining that specific level.

SUMMARY

  • Control your encapsulation when dealing with scriptable objects
  • Make sure to have checks for max and min values when modifying stats
  • Creating new classes that just hold a bunch of fields and then creating an array of those classes can be a very nice modfiable tool for a designer to work with

Unity Learn Tutorials – Artificial Intelligence for Beginners and Intro to Optimization

February 7, 2020

Unity Learn Tutorials

Note: The AI tutorial require Unity Learn Premium to access
Artificial Intelligence for Beginners

Tutorial Series #1 – Link

By: Penny de Byl


Introduction to Optimization with Unity – 2019.3

Tutorial Series #2 – Link

By: Unity Technologies


Notes

These were both tutorial series I saw on Unity Learn that I wanted to note specifically to check out later. The Optimization tutorial is a very small series, so I can look into that rather quickly, but the AI series is a 15+ hour comprehensive class so that will take a large commitment to fully cover.

The AI series however is something I am interested in in general and even covers some topics I am specifically looking into at this time. Some of these topics include: Navigation Meshes, Crowd Simulation, State Machines, and Goal Driven Behavior. This would be very good to follow for my own personal interests as well as possibly being useful for some projects I am looking to work on.

UnityLearn – Beginner Programming – Creating a Character Stat System – Pt. 01 – Overview and Creating Scriptable Objects

January 8, 2020

Beginner Programming: Unity Game Dev Courses

Beginner Programming: Unity Game Dev Courses

Unity Learn Course – Beginner Programming

Project Overview

General Overview of Creating Character Stat System Tutorials

  • Defining and Configuring scriptable objects
  • Accessing data inside of scriptable objects
  • Setting up helpful debugging items
  • Scriptable objects inside of monobehaviours

Creating Scriptable Objects

Scriptable Objects Explained

Scriptable objects are very effective for creating designer tools.

Systems Map: image showing the separate pieces of the game system and how they all connect, in a web-like design

SCriptable Objects: data containers of which, we can have multiplie instances

  • Defined by programmer
  • Treated as assets in Unity
  • Easy for designers to work with
  • CANNOT be added to gameObjects as components

The basic template for turning a script to a scriptable object is adding a [CreateAssetMenu()] header to the top of the script and having the class inherit from SCriptableObject. The CreateAssetMenu header is what adds the scriptable object as an option in Unity’s own Asset Menu at the top of the screen. This also holds the information on how it is displayed there.

Again, it is important to note that scriptable objects CANNOT be added to gameObjects as components. This is worked around in the tutorials however by adding them as a field to a Monobehaviour that is located on a gameObject.

Debugging the Aggro Radius

This segment mostly dealt with importing the project if you had not already, and doing some folder organization for these tutorials. They also added a bool to control showing aggro radius of enemies.

Building Your Scriptable Object

The first scriptable object they create is called CharacterStats_SO.
The CreateAssetMenu tag used at the top of the script is directly used to store this information in an item in the drop down menus of Unity itself. This is why there is a fileName and a menuName.

MenuName: This will be the string seen in the drop down menus directly. You can also add a “/” to create a chain of drop downs where the item will be located.

The fields used for CharacterStats_SO:

  • bool setManually: this will determine if the stats were created manually, or created dynamically
  • bool saveDataOnClose: this will determine if data should be serialized, or written to disk, on close
  • The rest are just general player stats fields (health, damage, resistance, etc.)

SUMMARY

These tutorials were just the tip of getting introduced to scriptable objects in Unity. The rest of the tutorials in this set will delve deeper into the topic and how to incorporate them into other Monobehaviours.

UnityLearn – Beginner Programming – Swords and Shovels: Character Controller and AI – Pt. 03 – Finalizing and Extending Systems

Novemeber 21, 2019

Beginner Programming: Unity Game Dev Courses

Beginner Programming: Unity Game Dev Courses

Unity Learn Course – Beginner Programming

Finalizing and Extending Systems

Designing with NavMesh Obstacles

This section just showed how obstacles work with NavMesh. Objects with colliders can be given NavMeshObstacle components to work as obstacles within the NavMesh. The Carve bool lets you determine if they “carve out” parts of the NavMesh to make them inaccessible for agents with NavMesh driven AI.

Finishing the Patrol System

The InvokeRepeating method has a delay paramter as well. This can be given various values between different objects so they are not all on the exact same cycle. This was used to offset the waypoint traveling cycles of the goblin NPCs.

Fine Tuning the Agents and Animation

The logic that was making the player character awkwardly slow down around the stairs and other obstacles is found within the obstacle avoidance part of the NavMeshAgent component. The simple fix was to just turn off obstalce avoidance completely. This is done by setting the “Quality” of obstacle avoidance to “None”.

Conclusion and Extensions

They go back to working with the animator controller for the player character. Going back to the blend tree, they show the two parameters for the animation clips are: threshold and speed. The threshold is the value a parameter must hit to start a certain animation (here that parameter is another Speed). Since animations are being blended here, it changes the weights of the blending at various values of the parameter used to determining blending. Then the clip speed value simply changes how fast an animation clip is run.

They suggest using these animator controller parameters in concert with the NavMeshAgent parameters to keep your gameplay and animations synchronized. Using them together really helps fine tune things so that you can control gameplay exactly how you want, while also having characters and animation look proper.

They then simply show how to extend the level. It was as simple as copy/pasting some of the floor tiles, making sure they had generic colliders, and then rebaking the NavMesh.

SUMMARY

This tutorial section involved working with the NavMesh tools and Animators again. This was again not extremely detailed or involved, but extra experience with those systems are useful. Some of the work with the Animator involved generically good advice to sync blending and animator parameters with gameplay to help provide a nice game experience.

UnityLearn – Beginner Programming – Swords and Shovels: Character Controller and AI – Pt. 02 – NPC Controller

Novemeber 21, 2019

Beginner Programming: Unity Game Dev Courses

Beginner Programming: Unity Game Dev Courses

Unity Learn Course – Beginner Programming

NPC Controller

Creating NPC Patrol

The NPC has two main states: it will patrol within a set of waypoints or it will pursue the player. They create two main methods to control the NPC behavior, Patrol and Tick.

Both of these are called in the Awake method of the NPCController through the use of the InvokeRepeating command. This is done because you can set a time value that needs to pass between each invocation, so basically they can perform these methods in a “slow Update” fashion. Tick is called every 0.5s, while Patrol is called every patrolTime, which was defaulted at 15s.

The Patrol method used an inline ternary method to set the value of index, which is something I am not used to seeing. The line was as such:

index = index == waypoints.Length – 1 ? 0 : index + 1;

I believe this checks if index is equal to (waypoints.Length – 1), then if that is true, it sets the value of index to 0, and if it is false, it sets the value of index to (index + 1) (or index++). This is actually pretty basic waypoint logic, the syntax was just unusual.

This system has some flaws as well already. The InvokeRepeating calls start their timer countdowns immediately upon calling the method. So even though the NPC takes time to travel between waypoints, the countdown to move to the next waypoint has already started as soon as they start moving. This means their travel time must be taken into consideration when setting this value as if it is too low they will start moving to the next waypoint before they have even reached the current one as a destination.

Synchronizing Animation and Creating the Pursue Behavior

This tutorial starts similarly to synchronizing the player character’s speed with its animation. They simply pass the NavMeshAgent component’s speed value into the Animator controller’s speed parameter to alter animation as the character’s speed changes.

To create the pursue behavior, they added extra logic to the Tick method in the NPCController class. They added an if conditional to check if the player was within the aggroRange of the NPC, and if so, it would add the player’s position as the NavMeshAgent destination value and increase its speed.

SUMMARY

While the NPC logic was not very interesting, some of the programming syntax used was new and interesting. The use of InvokeRepeating without any coroutines or anything was also a neat way to just get a quick prototype system setup when you want something to run many times but Update is overkill.

UnityLearn – Beginner Programming – Swords and Shovels: Character Controller and AI – Pt. 01 – Overview and Character Controller

Novemeber 21, 2019

Beginner Programming: Unity Game Dev Courses

Beginner Programming: Unity Game Dev Courses

Unity Learn Course – Beginner Programming

Overview and Character Controller

Navigation and Animation Review

This part of the tutorial just gets you familiar with the NavMesh setup they have and the animator they are using on the player character. Some of the items are a bit off since they are using an older Unity version (2017), like where the Navigation tab is located (it is now under Window -> AI).

The player character animator has a blend tree that holds a speed parameter. This parameter appears to determine how much to blend between an idle animation and a running animation, as well as how fast the animation plays.

Controlling Motion with C# and Unity Events

This tutorial portion started with adding a NavMesh Agent component to the Hero character to have it interact with the baked NavMesh in the scene.

The next step was to edit the MouseManager class to actually move the character according to its NavMesh Agent and the created NavMesh. This would require a reference to the NavMesh Agent component. They did not want to use a public field (as this creates a dependancy that can lead to issues down the road), so they turned to making an event.

They needed to create this event, which they did so as a new class within the MouseManager script, but outside of the class. This was done in the following way:

[System.Serializable]
public class EventVector3 : UnityEvent { }

The base class UnityEvent lets it send vector3 information through an event.
They then created a public field of the type EventVector3 named OnClickEnvironment. This was then used to do the following:

if (Input.GetMouseButtonDown(0))
{
OnClickEnvironment.Invoke(hit.point);
}

The public field created a box within the inspector similar to UI events in Unity. You could drag an object in as a reference, and select a component and method to call when that event happened. In this case they used the Hero object, and called the NavMeshAgent.destination method.

Synchronizing Animation with Motion

They start by simply referencing the NavMeshAgent speed and passing that value into the animator’s speed parameter. They then begin to alter the NavMeshAgent component’s properties to fine tune the movement to feel better to play with. They significantly increased the Speed, Angular Speed, and Acceleration, and turned off Auto Braking. This felt much better, but led to an issue where the player would run back and forth near their destination.

The issue is caused by turning off Auto Braking and not having a Stopping Distance. I assume since the character has trouble “perfectly” reaching the value of its given destination, the Stopping Distance of 0 means it will keep trying to get there even though it cannot.

Setting the Stopping Distance to 0.5 fixed this issue. This allows the agent to stop moving once it is within 0.5 units of the given destination. There was still a strange issue where the character would move slowly around stairs, but they said they would cover this issue.

Handling the Passageway

This portion deals with adding code to allow the character to traverse the special passageway section of the level. The passageways have unique colliders to inform the cursor that a different action should take place when they are clicked. These colliders also have direction, which will be used to determine which way to move the character through the hallway.

They then go back to the MouseManager to show where the cursor is detecting the unique collider (currently just to swap the mouse icon with a new icon). This is where they add extra logic that if door is true (a doorway has been clicked) that they want to grab that object’s transform and move a fixed distance (10 in this case) in that object’s z-direction. This is why the collider’s orientation is important.

This setup seems very bad to me. This only works for passageways that are all exactly the same distance since the value is hard coded in. You can still click on areas between the passageway entrances, so the character just gets awkwardly stuck in the middle with the camera pressed up against a wall. There is no limitation when taking a passageway, so the player can click again midpassage and alter their character trajectory which can be weird or buggy.

I think I would prefer a system that somehow links the two passageway entrances so that no matter where they are, when the player clicks one they will enter it and come out of the other one (use their transform with some offset). Player input should also be limited somehow during the passage because there is no reason to do anything else within the passage, this only allows for more errors.

Note:

The MouseManager uses a lot of if/else conditional statements, and from the previous lessons, I think it could possibly benefit from having a small state machine setup. While I think it is just on the edge of being ok in its current setup, the Update method for the MouseManager is already looking a bit bloated and any more complexity may start to cause issues.

SUMMARY

I think this tutorial was mostly focused on just getting you used to finding and using some different features of Unity, such as NavMesh and the Animator. Nothing here seemed to be that deep or well done, so it felt very introductory. Getting a refresher on NavMesh agents was definitely useful at least, and working with animation never hurts.

UnityLearn – Beginner Programming – Finite State Machine – Pt. 02 – Finite State Machines

Novemeber 18, 2019

Beginner Programming: Unity Game Dev Courses

Beginner Programming: Unity Game Dev Courses

Unity Learn Course – Beginner Programming

Finite State Machines

Building the Machine

This part of the tutorial has a lot more hands on parts, so I skip some of the sections for note purposes when there is not much substance to them other than following inputs.

A key in finite state machines is that an object can only ever be in exactly one state at a time. This helps ensure each state be completely self contained.

Elements of a Finite State Machine:
Context: maintains an instance of a concrete state as the current state
Abstract State: defines an interface which encapsulates behaviors common to all concrete states
Concrete State: implements behaviors specific to a particular state of context

To get started in the tutorial, they created a public abstract class PlayerBaseState which will be the abstract state for this example. PlayerController_FSM is the context. They note that while in this case all the abstract state methods take the PlayerController_FSM (the context) in as a parameter in this case, that does not necessarily have to be the case for the general FSM pattern.

Concrete States

It is noted that the context in a FSM needs to hold a reference to a concrete state as the current state. This is done in the example by creating a variable which holds that of the type that is our abstract state, which is PlayerBaseState in this case. They then create a method called TransitionToState which takes a PlayerBaseState in as a parameter. It then sets the currentState to that parameter state, and then calls the new state’s EnterState method (all states have this method as it is dictated by the abstract class they all implement). This determines what actions should be done immediately upon entering this new state.

Example:

public void TransitionToState(PlayerBaseState state)
{
currentState = state;
currentState.EnterState(this);
}

The tutorial also shows a way to take control of the context’s general Unity methods and pass the work on to the concrete states instead. This example did this with Update and OnCollisionEnter. The abstract state, and in turn, all of the concrete states, have their own Update and OnCollisionEnter method. The context, PlayerController_FSM, then simply calls currentState.Update(this) in its Update method, and currentState.OnCollisionEnter(this) in its OnCollisionEnter method, so that the current concrete state’s logic for these methods are used without flooding the context itself with any more code.

Since it is necessary that your context has some initial state, they do this by simply calling the TransitionToState method within the Start method and entering the IdleState. IdleState is the initial state for this case.

Beginning the Implementation

Important benefits seen using this system:
While working on the concrete classes themselves, we never needed to go back to the PlayerController_FSM class (the context) to modify any code there. The entire behvior is handled within the concrete states and is abstracted from the character controller (the context). Setting expressions was much easier as no checks are needed and this can just be set in the EnterState method of each concrete state.

It is already clear that this method removes a lot of boolean checks from the overall code, and helps organize the code by ensuring any logic about a state is contained within the class for that state itself (with less bleeding into the code of other states).

Continuing the Implementation

It is worth noting that PlayerController_FSM holds a reference to every concrete state except the spinning state. This was done because they actually have the jumping state create a new spinning state on transition each time it is invoked. They apparently do this so that the local field for rotation within the spinning state is reset to 0 each time it is called, but it seems like there would be other ways to do this that seem less wasteful (such as resetting it to 0 when exiting the state). I am also not sure if this is intended behavior, but the spin also immediately cancels upon contacting the ground (resetting the player rotation to 0) with this setup, where as in the previous behavioral setup the spin completed even if the player contacted the ground.

Module Conclusion

Benefits of FSM:

  • More modular
  • Easier to read and maintain
  • Less difficult to debug
  • More extensible

Cons of FSM:

  • Take time to setup initially
  • More moving parts
  • Potentially less performant

Just something very notable with this approach, it seems much harder for me to break than the naive implementation. If I spammed key presses (like pressing jump and duck a lot) with the naive approach, sometimes I could break the system and have the player stuck in the duck position or be ducking while jumping. I have not been able to break it at all with the full FSM setup, which makes sense since transition behaviors solely exist within the states themselves so these inputs cannot be jumbled in any way.

SUMMARY

Using state machine systems appear way easier to use and build on than the “naive” approach of basic boolean behaviors (with lots of if statements and boolean checks). Not only was I very excited about how much easier this appears to work as a scalable option, it also just worked better and more cleanly when it was all put together.

The other version had small bugs that would pop up if you spammed all the different action keys (such as getting stuck ducking or being ducked in a jump), which were possible just because the key presses would get recorded before reaching the bools or if statements that should be telling them that they are not proper options. These very separated states make that type of error impossible as it is only concerned with a single state at a time.

This type of system just seems much cleaner, more organized, and less error prone than what I have done before and I am very excited to try and build a system like this for my own project (for both players and enemy AI).

UnityLearn – Beginner Programming – Finite State Machine – Pt. 01 – Managing State

Novemeber 15, 2019

Beginner Programming: Unity Game Dev Courses

Beginner Programming: Unity Game Dev Courses

Unity Learn Course – Beginner Programming

Managing State

Project Overview

This part of the tutorial has a lot more hands on parts, so I skip some of the sections for note purposes when there is not much substance to them other than following inputs.

The basics covered in this section are:
What is state and how to manage it
Finite State Machine pattern
Build your own finite state machine

Introduction

State: condition of something variable
State Examples: Game state, Player state, NPC State

Finite State Machine: abstract machine that can be in exactly one of a finite number of states at any given time

Parts of a Finite State Machine:
  • List of possible states
  • Conditions for transitioning between those states
  • State its in when initialized (initial state)

Naive Approach to Managing State

This naive approach focuses on boolean states and if staements. It uses a lot of if and else if statements in the Update method to determine what state the player is in and if/when/how they can switch to another state. Even with two states this becomes tedious and somewhat difficult to read. This example is just to emphasize the use of proper finite state machines.

Actions, Triggers, & Conditions

Look at your actions as a set of: actions, triggers, conditions.
Example for Arthur jumping:

  • Actions: Arthur jumps; jumping expression
  • Triggers: Spacebar is pressed
  • Conditions: Arthur is not jumping

Continuing to follow the naive state management approach, we see that everytime we add a new state it makes all snippets about other states more complex and harder to follow. This is very clear that this will become unmanagealbe with only a few states even.

Module Overview

The biggest issue with the naive approach is the interdependent logic of the various states. It makes each state exponentially harder to work with with every state that is added, so it is very limited on its scalability. This does not even come with a benefit to readability, as it also becomes difficult to read quickly.

SUMMARY

Using the naive approach (boolean fields and if/else statements) to manage state is only really useable for extremely simple cases. As soon as you reach 3 or 4 states with even small amounts of logic to manage them, this approach becomes very awkward and unwieldy. Fininte State Machines should hopefully open up a better way to manage more states with better scalability and allow for more complexity with better readability.

UnityLearn – Beginner Programming – Observer Pattern – Pt. 03 – The Observer Pattern

Novemeber 7, 2019

Beginner Programming: Unity Game Dev Courses

Beginner Programming: Unity Game Dev Courses

Unity Learn Course – Beginner Programming

The Observer Pattern

The Observer Pattern

Observer Pattern: software design pattern in which an object, called the subject, maintains a list of dependents, called observers, and notifies them of any state change, usually by calling one of their methods

Observer Pattern Anatomy

Subject

  • collection of observers
  • method: AddObserver
  • method: RemoveObserver
  • method: NotifyObservers

Observer

  • method: Notify (what to do when notified)

An interface is a good way to ensure observers have the types of methods you need to exist when notified by the subject.

Implementing the Observer Pattern

This was an example of how to setup a basic observer pattern using the example project. First they created the interface for the observers, which was named IEndGameObserver. This simply held an empty method named Notify().

They then used the GameSceneController script as the subject for the observer pattern. To do so, they created a list of IEndGameObserver objects, created a method AddObserver which could add to that list, created a method RemoveObserver which could remove from that list, and finally a method called NotifyObservers that was a simple foreach loop that went through each IEndGameObserver in the list and called their Notify() method.

NotifyObservers then just needs to be called when the event or state is reached where the observers need to be informed that a change has occurred. Since this example was to inform objects of when the game ended, NotifyObservers was called within the GameSceneController script when the player ran out of lives.

Concrete Observers

Concrete Observers: just objects that implement the observer interface

This example showed that with this setup, either the observer or the subject can be used to add observers to the subject’s collection of observers. The HUDController already had a reference to the GameSceneController (subject) so it made sense to just have it add itself to the observer collection through that reference.

While the EnemyController and PowerupController do not have reference to the GameSceneController, the GameSceneController has references to them created on instantiating them. These could then be used with the GameSceneController to add them to the observer collection upon instantiation.

***Could possibly use this observer pattern in my thesis project to easily collect all of the various Create classes into the Scenario classes (and potentially again to collect the Scenario classes into the SystemManager).

Removing Observers

To avoid nullreferenceexceptions, you must ensure that observers that are destroyed are also removed from the subject’s collection of observers.

To apply this to the example, we added a method called RemoveAndDestroy to both the PowerupController and the EnemyController class which both removed it from the observer collection and destroyed it. This method was exactly the same for both, so this could indicate that it should be added to the interface itself to keep it consistent amongst all observers using this interface.

The Observer Pattern and C# Events

Example Problem with C# Events:
The ProjectileController has an event that occurs when it reaches the bounds of the screen. The PlayerController subscribes to this event by adding one of its methods to this event. There are some cases where the projectile exists while the player is destroyed, so when the projectile goes out of bounds (and calls its event), it tries to invoke a method from the destroyed PlayerController class, which gives an error.

Similar to the observer pattern where we want to remove observers from the collection when they are destroyed to prevent errors, we want to unsubscribe classes from events when they are destroyed. This ensures that they are revmoved from the events invocation list so that they do not try to call methods of destroyed objects which will create errors.

The Publisher Subscriber Pattern

Publisher Subscriber Pattern: similar to Observer Pattern, but adds another entity, the broker, which is in charge of keeping track of and notifying subscribers.

Observer Pattern has a single subject, but publisher subscriber pattern can have any number of publishers. Each of these publishers has a reference to the broker to notify it when a noteworthy happening occurs.

Example:
They created a class called EventBroker that just contained a public static event ProjectileOutOfBounds and a public static method named CallProjectileOutOfBounds (which just checked that the event wasn’t empty and then called that event). The PlayerController subscribes to this event by adding its EnableProjectile method to it on Start (and removes it OnDisable), both of which are very direct since everything is public static in EventBroker. Finally, the ProjectileController invokes the event CallProjectileOutOfBounds when the projectile leaves the screen, which now invokes the PlayerController’s EnableProjectile method.

This setup allows the ProjectileController to effectively communicate with the PlayerController without references to each other in anyway by going through the EventBroker.

SUMMARY

Using delegates, events, and actions can provide a clean and effective way to communicate important happenings throughout many objects and have them act accordingly. The Observer Pattern uses a single subject and multiple observers which receive information from the subject when to do something. The Publisher Subscriber system is similar, but adds a middle man entity of a broker which communicates the information between the publisher and subscribers without the publishers or subscribers needing any direct reference between each other.