Before class you should:
Continue work for Brief 3 and note any issues you had/have
Make note of any of the unit's earlier concepts you will be using in your Brief, as listed in Week 9, and if you have any questions about them
Make sure you are up-to-date with your Learning Journal
1. Creating Audio
2. Creating Particle Systems
3. Unity Events: What are they + implementation
4. (Optional) Delegate event systems: What are they + implementation
5. Brief Work:
Brief Work and One-on-One Feedback
A huge part of a game's experience is its SFX, and audio and particles are a simple place to start with them.
The official Unity documentation is here: https://docs.unity3d.com/ScriptReference/AudioSource.html
In Week 8 I included a folder of video run down of Unity basics, but the adjacent videos and steps below provide a refresher: https://www.youtube.com/watch?v=YnIiMCnAf9E https://learn.unity.com/project/beginning-audio-in-unity
Embedded Audio Basics:
Import an audio file into your Unity project (e.g. from here: https://freesound.org/people/GameAudio/)
Add an AudioSource component to a gameObject (your camera will already have an AudioListener attached).
Assign the audio file onto the slot for it on the AudioSource component.
Tweak the settings, e.g. Play on Awake
Press play to hear it.
Audio Activation via Script (see image below):
Untick the Play on Awake option on the Audiosource
Create a new script with a reference to an AudioSource and AudioClip and attach it to the gameObject
Create an OnTriggerEnter() method that when the player touches the gameObject, it starts playing the audioclip.
Extra: If you have done the above, you could:
investigate instantiating objects that have audio attached to them that plays automatically, e.g. projectiles.
investigate your player controller triggering its own AudioSource based on interactions, e.g. when you press specific buttons it selects and plays one from a series of AudioClips.
investigate Unity's AudioMixer implementation, which allows you to have different audio groups, e.g. one for voice, one for music, one for SFX, and one for dialogue, each with their own audio levels, allowing you to 'mix' the audio volumes and effects on the fly.
As with audio, particles is a basic aspect of game FX, and Unity's Particle System has a lot of features for creating effects that may simulate such things as:
Explosions
Fire
Spell FX
Will o' the wisps
Mist etc
The official Unity docs for particles are:
You only need to know the very basics, but the adjacent Brackey video provides a good overview of the component details: https://www.youtube.com/watch?v=FEA1wTMJAR0
Basics:
Create a new gameObject and add a Particle System component to it and make sure it's within the camera's pov.
Tweak the Particle System components, e.g. colour, rate of emmission.
Press play and watch the particle emission at work
Triggered/scripted particles:
Untick the Play on Awake option on the Particle System
Create a new script with a reference to an Particle System and attach it to the gameObject
Create an OnTriggerEnter() method that when the player touches the gameObject, it triggers the Particle System (see below, although you may want to use a method call rather than the IEnumerator Coroutine)
Extra: If you have done the above, you could:
investigate creating FX prefabs, e.g. your player can fire a grenade, which instantiate a grenade prefab with its own audio effect, and when it impacts something the grenade has a script that instantiates a separate explosion prefab with its own a audio and particle effects (e.g. see adjacent Brackey tutorial).
Look at the Visual Effects Graph, a newer way of controlling FX in Unity: https://docs.unity3d.com/Packages/com.unity.visualeffectgraph@9.0/manual/GettingStarted.html
As people were still focused on getting their basic Brief 3 framework in place, we didn't really get to this last week!
You don't necessarily need "event systems" for a game to work, but most contemporary scripting makes use of event-driven systems as they are more robust and extensible (i.e. they are harder to break, easier to fix/debug, and easier for others to add to, especially with larger projects).
An event is the result of an action.
An action could be almost anything: a keypress, a menu interaction, the death of a character, the end of the game, and so on.
An event may be handled in different ways:
Using Unity's default functions in its C# library, e.g. OnTriggerEnter() and OnCollisionEnter(), that are Unity event systems that work with its GameObjects.
Using your own scripted events, by using delegates:
A delegate is a <LIST> of methods that 'subscribe' to an event. Any method can add or remove itself from this list.
When an 'event' happens you invoke the delegate, and in turn all the methods in the list are executed
All methods that subscribe to the delegate event must have the same return type and parameters
Using Unity's UnityEvent system:
a specific implementation of delegates which you can see in action if you look at OnClick() events for Unity UI elements in the inspection.
this lets you add 'listeners' to events in the inspector
This is the Unity Learn documentation for delegates: https://learn.unity.com/tutorial/delegates#5c894658edbc2a0d28f48aee
A very useful document about them is here: https://gamedevbeginner.com/events-and-delegates-in-unity/
This video provides a summary of the key points we'll be discussing, but it uses the old UI system (i.e. OnGUI()) to create a scripted UI button.
Let's say you have a game, and when you kill a boss you need to call a lot of different scripts to:
Destroy the boss's GameObject
Destroy all the enemy boss's projectiles/FX (so the player isn't killed AFTER defeating the boss!)
Disable the spawnpoints associated with the boss's minions (because the boss's death has caused its minions to cease being summoned)
Make all the minions self-destruct (with the boss gone, they disappear back into their dimension!)
Disable some aspect of the PC (stop the player from being able to move for a moment)
Play an animated sequence (the player gets to do some fun dance moves)
Add to the player's xp
and so on...
Solution #1:
By creating a method called 'BossDefeated()' you can directly call all the other methods in all the other GameObject's scripts e.g. all the spawners, all their minions, all the projectiles, the player, the cut scene animator etc).
However the script with the BossDefeated() function in it needs to have a reference to ALL of those other scripts. If that script is attached to multiple objects in one or more Scenes, it's possible you'll forget to assign all those references, or when a bug happens those references will... bug out, and you won't know until you find and look at the GameObject in the Inspector in that Scene(!)
This is an example of 'coupling', where the scripts need to directly know about/talk to one another.
Solution #2:
By using a UnityEvent or delegates, all those other scripts can just add themselves as listeners to (or 'subscribe to') a delegate event called BossDefeated, and when the boss dies you just 'Invoke' the event delegate. All the methods in all those scripts, no matter where they are in the scene, will just take care of themselves and don't need to know about each other.
This is an example of 'decoupling' i.e. making it so scripts don't need to directly know about/talk to one another
We'll run through the Unity UI system to create a sense of what is happening with Events and to prepare for if/when you implementing delegates into a unity project.
This is the official Unity tutorial on UI: https://learn.unity.com/tutorial/ui-components#5c7f8528edbc2a002053b4d7
This is the Unity documentation on UnityEvents: https://learn.unity.com/tutorial/events-uh#
Create a new Unity Project or open one you are working on
Create a UI Canvas (in the Hierarchy, right click, UI > Canvas)
Create a child Image for the UI Canvas (in the Hierarchy, right click, UI > Image) and rename it 'UI Background'
Create a child text field for the Image (in the Hierarchy, right click, UI > Text - Text Mesh Pro) (note: this will prompt you to import the Text mesh Pro essentials, which you should do!)
Experiment with positioning and anchoring the Rect Transform (a UIs version of a Transform)
Rename the UI elements and their text fields, experimenting with the text size, colour and alignment.
Create a new UI Text GameObject and position it at the top of your screen.
Change the text of the child GameObject (e.g. so the UI text says 'I'll change when you take damage!' on the screen)
Create a new script called HealthUI and make sure it has
a variable that references the UI Text component (you can assign this in the inspector so your script knows what text element to update)
a public method that will update's the text of the UI Text component referenced by your variable (e.g. healthUI.text = "you just took damage!").
Add the script to your UI Text GameObject.
Select your player and add the Health.cs script to it (from the adjacent folder).
If you look at the Health script in Visual Studio you will see it has a UnityEvent called objectHasDied.
You can copy and modify this line to create a new UnityEvent called objectTookDamage.
Save the script and look at it in the Inspector: you now will see it allows you to add methods that 'listen' for both the objectHasDied event and your new objectTookDamage event.
Get your health UI element to 'listen' for the event:
Drag the UI Text GameObject with the HealthUI script onto the slot under 'Runtime'.
In the adjacent drop down, find the HealthUI script component, hover over it, and select the method you wrote.
Modify your Health script so that it triggers the event. The easiest way to do this is in OnTriggerEnter(): check to see if the player is being hit by an enemy, and if so make sure you add objectTookDamage.Invoke() to trigger the event
Press play, damage the object until it dies, and then see the method called!
That's a basic event system using UnityEvents!
Create a UI Button
Change the text of the child GameObject (e.g. to '
Create a new script with a public method that destroys on object e.g. public void DestroyObject()
Drag that script onto an object in the scene
Make sure the script has a reference to an object in the scene.
On the UI Button you should see an OnClick() box with an empty event:
Drag the GameObject with the UI script onto the slot under 'Runtime'.
In the adjacent drop down, find the script component, hover over it, then select the method you wrote.
When you press play, the button should 'call' the method on that OnClick() event
Creating a delegate-based event:
Note: you may come across some variants of the third line of code above, e.g. instead of:
public Click click;
it could be:
public static Click click; // this means there is only one instance of the event in the project
or:
public static event Click click; // this provides some added protection to stop another script from 'overwriting' the list of listeners that have subscribed to the event
Getting another method to 'subscribe' (i.e. add itself) to the event.
So, you want to use events in your project but already have it functioning using OnTriggerEnter/OnCollisionEnter or something else? Or, you're not sure where to implement events? Or, not sure if you have to remake your entire gameplay features to get events working?
Look no further—we can make a "game statistics" screen to do that, i.e. some kind of UI screen or panel that shows the player their stats during a game, such as: how many times they fired their weapon, how many times they hit a target, how much healing they delivered to team mates, how much time they spent airborne, how many degrees they turned left right, how many times they sent a message while waiting to respawn...
Or, in the case of a simple 1-vs-1 tank game, how many times the player's tank drove over a boost pad, collided with a mine, collected a power-up, teleported somewhere else on the map, was destroyed by its own shell, how much damage it dealt/received, and so on.
Image 1: Overwatch "Career Stats" Screen as reference
We could set up some kind of "game statistics" screen or UI panel
This could consist of a Canvas object containing multiple Text objects—one per "stat" (Image 2)
We'll also need a script for our game statistics panel, which would have variables/references to each text object in our scene's Hierarchy (see Images 3).
You could make a separate variable for each UI element with a logical name (see Image 4) (or create an array linking to all the UI elements).
Image 2: A mock-up "Game Statistics" screen
Image 3: Our mock-up's hierarchy
Image 4: Game Statistics Screen script containing a reference to all of our hierarchy's text objects
We could then add to our script (or a separate event manager script) variables containing all of the events that we want in our game, named appropriately.
Image 5: A script containing all of the events that could be called during gameplay
We then need to program the actual event handling, in this case we need variables that we are tracking (Image 6) as well as the functions that modify them and actually update the UI elements (Image 7).
Image 6: A script showing the variables we will be tracking.
Image 7: A script containing the functions that will subscribe to the event and 'handle' them
Next we must ensure that our events are "announced" at particular times during the game (such as whenever a player takes damage, or fires a weapon, etc.)
If so, each "announcement" should go onto whichever object is announcing out the event.
For example, when we take damage we may wish to announce it is a TookDamage event
Image 8: A script on a heath script can call the TookDamage event whenever it receives damage!
Lastly, we need to make sure our UI screen is subscribed to these events, and updates when it hears one being announced.
This would likely adding a method, e.g. "TakeDamage()" to the list of methods called whenever the event is announced.
Your method needs to update our UI text when this happens too.
Image 9: Our improved Game Statistics Screen script that is now listening to events and updating our UI accordingly.
Now we have a functioning game statistics screen that tracks all kinds of data and metrics of our player's gameplay!
We can use such a process as above to fill in our game statistics screen, and grant the player the ability to show or hide it during the game or perhaps even afterwards. And if we wanted to take things to another level (or to go even further beyond, as Goku put it), we could even add functionality to save this data on the user's device, and reload it the next time they play the game—keeping track of their stats whenever they play the game—and even export our tracked data to analyse our player behaviours and performance...
...but that's a story for another time.
If you want to think of other ways to implement events in your game for Brief 3, some ideas are:
You must create a C# Unity game project that extends your understanding of Object-Oriented Programming (OOP), including:
Proper use of documentation (see right, from last week) which should let you see how you are implementing:
The coding functionality used in Briefs 1 (i.e. variables and basic game logic) and Brief 2 (i.e. object-oriented programming (OOP) techniques)
A simple, explorable, interactive blockout environment
Proper use of collisions and/or triggers and related events (i.e. some combination of OnTrigger/OnCollision events, UIEvents, UnityEvents, and/or delegates with listeners).
Documents: planning doc, stating goals, objects, events, other mechanics, flow chart, level sketch, pseudocode + class diagrams (aka UML)
Simple Unity Scene with simple coloured shapes (ground plane, cubes of PC, NPC and obstacles) to get started
Basic movement scripts
Basic collision/trigger scripts
Level blockout
Basic UI
Event scripts
Enemy patrol/vision
Audio
Particles
Animation (we'll do this next)
Be prepared to show your Learning Journal to show your weekly reflections and Brief Pages
Find tutorial videos or example scripts that will give you the knowledge you need to code each of the mechanics that you have planned for your game.
For each planned mechanic, make sure that you have found resources that will show you how to create this mechanic with code.
Your solutions may be messy or hacked together. This is OK at this stage of development.
As usual, do your post-class reflection, updating your LJ Weekly Reflection and Brief Pages.