Transcripts
1. Course Introduction: Hi. Welcome to
this Unity course. I'm Rome Faze, and I've been a game developer and treaty
artist for many years. I've taught for over 9,000 students how to develop
games in Unity. In this course, we
are going to develop a rail shooter game akin to fear Talk Op or House
of the Death series. With over then 7 hours content, this will be a
complete course with a fully finished
and polished game at the end of the course. I'm going to cover
best practices of Unity game development, including object
oriented programming, such as inheritance
and interface, events, for script communication, and two post processing to
enhance the look of the game. For these projects, I'm
going to use Unity 2019 0.2, but any version from
2018 should be okay. So if you are a Unity developer and on you upgrade your skill, you'll have a blast
in this course. So enroll today and
let's learn together.
2. 01 Project Setup: Okay, so now what we want to do is we want to
create a new project. And this is actually the revised lessons for
this project setup. In this case, I'm going to press the new project button
here and I'm going to use the latest 2022 0.3 0.62 F two. And for the rest of the lessons, it's going to be using
a different version because those are
still the old videos, but it should work
okay. I've tested. Okay, so for the project here, we want to use the three D
built in render pipeline, and let's set up a folder there. I'm going to call this. This is going to be
a new local project because with the new Unity Hub, you can decide if you want to
create a project that gets registered to your Unity
Cloud or a new local project. In this case, I want just
to be a local project. And let's just call this. Let's call this virtual
club style project. And let's press Create Project. This will create a new project and prepare everything
for us to start. Okay, so now we have
this new project open. First thing that we
want to do is we want to go to the lighting tab here, and if you don't have
this, you can just go to the window rendering lighting. And that menu will
open this tab here, and we want to disable
the autogenerate. So if the autogenerate
is disabled, then good. Otherwise, if this is enabled, just uncheck this so
we don't generate lighting every time
we change the scene. And the next thing that
we want to do is we want to install a couple of plugins add ons and assets to be used as we go with the
lessons for the course. So here you go to the
window package manager, and we want to select
the Unity registry here. And we can search
for Pro Builder, and this should list
the Pro Builder there. And once we have this,
just press Install, and this will install
the Pro Builder package so that we can build
di custom geometry and unity to create our level. Okay, so now we have the
P Builder installed. The next thing that
we want to do is we want to install the Pgrids, but that one is still
on preview package. So what we need to do
is we need to enable the preview package overt
here, project settings here. And now we want to enable
pre release package. Okay, so now that once we
have this here enabled, we should be able
to refresh this. If for some reason the
progress is still not there, then what we want
to do is we want to install it by package name. So basically, we can just type com dot Unity dot Progress. And this basically we'll
install the progrets there. And since this is an
experimental package, it's hidden by Unity. But basically, we can add
this by typing its name here. So now that we have
this progret installed, let's just close this. Now in the scene here, we
should have the Pgrits options. Okay, so that's
good. The next thing that we want to install is the post processing
add ons from Unity. So back to the package
manager there, we can type post. And this should filter
the post processing. Okay, no result there. So let's just delete this. And we need to go
to Unity Registry first and search for
post processing here, and we want to install this post processing
package from Unity. So it's under Unity Registry and just search for
post processing. At the time of this
video recording, it's on version 3.4 0.0. Okay, so we have post
processing installed. Next, we want to install a couple of assets
from the Asset Store. But since 2020 or 21, Asset Store is not available
in the Unity anymore. So what we want to do is we
can go to our browser here, and I've already set up I've
already put the links here. So the first that we want to
install is this robot Kyle. You can search, and
I've put the link in the second content
of this course, second lesson. You can
open that link there. And if you haven't added
this asset to your account, this button will says
add to my assets. So just click that. You will be asked to accept
their terms of service. Once you have added, we can download it from
the package manager. And from the package
manager here, we can go to my assets, and this will list all
of the assets that we have added to our account
from the asset store. So we want to import
this robot Kyle, and then we want to import
this particle pack. This particle pack link is
also on the second lesson, the Sci fi gun light.
And the barrel. On this course, I'm going to
use these Sci Fi barrels for the example for the props that
we can shoot at later on. But unfortunately, this
has been deprecated. So I've put a new link, which is this one, and
this should work as well. It's just a different model, so feel free to use
anything else if you want, but you can also use this one to follow
along this course. And then the last one
that we need to import is this kinotch addons for creating the glitch effect
later on this course. So, okay, so here, I'm going to search
for the robot Kyle. And once I've had this, I'm going to download it and
install it into the project. Okay, so now it has
been downloaded. I'm going to import
this. I think they can safely skip this here. So they just skip this. And we don't need to import
everything here. So here, let's just
disable everything. And what we want to do is we want to import
only the model here, so we can just press
the space robot Kyle. And I don't think we need
the sound effects as well. Feel free if you want to
import the sound effects, but we need the texture,
we need the models, the materials, and the
animations as well. Okay, so now that we
have selected this, we can just disable all of the other assets and
we can press Import. And the next one is going
to be the particle pack. So I'm going to
search for particle, and there should be a
Unity particle pack here. This one here. And
this is quite big. So it might take a
while to download. Okay, so now the particle pack has been finished downloaded. We can just import
this into our project. Let's just skip this here. And let's check a
couple of things here. If there are some script
that we don't need, we can just here, I think we can just
remove the spawn effects. Shaders, we need it,
textures we need those. It's just a script
that we don't need it. And I don't think we
need the scenes as well. So just check the scene there and the shared folder
as well because those are the material for the
scenes as you can see here. And text Mesh Pro. Yeah, we can install this
or we can just import this from the package later because basically UNIT already
has this text Mesh Pro. So this is just support files. And as well, we can just
unselect this here. Okay, so make sure that
these are unchecked. I don't think we need
to read me as well. We just need the
particle pack folder here. So let's import this. So next, let's import
the Sci Fi gun. So this is the Sci Fi gun light. I'm going to download this and import into the project as well. Let's remove the
scene. We don't need that let's import
everything else. And let's import the Scifi
barrels on my end here, so we have the barrels
that we can use later as a explodable barrels. And we can just remove
this example scene here. So here, let's just organize let's better organize
this project here. I'm going to create a new
folder in the assets, and I'm going to call this game with
Underscore as a prefix, so it's going to
be sorted on top. It's easier to search
this folder and I'm going to move everything
into this folder here. And I'm going to create a
subfolder called model. And I'm going to put
the Sci Fi barrels, the Sci Fi gun light, and also the robot space
kyle into that folder here. And for the particle pack, I'm going to direct the effect examples and
put it on the game here. And now I can just remove this folder Unity
technologies folder. That way, it's going
to be easier to follow through to the
other lessons as well. And the last thing that
we want to import is the grid textures and a map textures that we are going to use to
create our level. So in order to do
that, let's create a folder first inside
the underscore game. Let's call these textures,
open that folder. And now I'm going to direct those textures
into this project. And this texture is provided
on the second lesson, along with the links
to those assets that we are importing in
the project setup lessons. So yeah, this concludes
the project setup, and we are going to continue
on the next lesson.
3. 02 Environment Modelling: So now with the sample
scenes is open, let's create the guide
for our scene first. In order to create the guide, we want to create a
new plane object. And this should be
ten grids wide, I think, but it's
apparently bigger. Let's just, it is ten. It's ten by ten, but we want to increase the dz size to 1.3, so it's then and I'm going
to set the snapping to half, so and then back to one and set the y position to also zero. Now we have this plane here, we can create a new material. Let's just create a
new material folder. Under the material folders, let's create new material. This will be our guy
and for the guide, we want to set the
albedo texture to the map texture that we've imported and then
direct this material. Now we have a correct
representation of our C in here. The way rod it is 13
units by ten units. Now let's create a new
shape from the pro builder. If you don't have
this window here, just go to the tools
the pro builder and then press the
probable window. It will be floating
by default like this, and we can drag the
name of the tab and then put it besides
the project panel. Now that we have this and also when we install
the progrit, it will show the settings
of the progrid here, and we can disable or
enable the snapping. We can also change the value of the snapping increment
by using the negative and the positive
sign on the keyboard. I'm going to set this 21 first, but if we need this, we can also make this smaller. Next thing that we want to do is we want to
create a new shape. Press plus besides the
new shape button here, and we will have this, and now we want to create
this box first here. It's four by two, and let's just set this four. No, sorry, I should be two
on the x and four on the z. Let's just set this
to three on the y. So it's high, it's a wall, and then press built. Now we can position this
and then move this up. There you go. The next thing that we want to create
is another cube here. We can set this on the z, but here we have one,
two, three, four, five, six, it shod
be six on the z xs. Let's just position
this here and then move this up. There you go. Now we need to create
another shape, and this should be
three on the x axis and also six on the z axis, and by pressing built, we can insert this object here and reposition accordingly. As you can see, I've miscounted the grids here, it's okay. W probilder, we can
modify the object here by selecting the sub
object on the top here. So here is the vertex, and this one is the edge,
and this is the phase. In vertex, you can
select the vertices, and then you can move
this. You can modify it. The UV mapping will conform, it won't stretch,
it will expand. So that's good for
creating the level. I'm going to use this
phase selection and select this phase here and then move
it one grid to the side. It's conform correctly
with our map guide. Now once we already
satisfied with the sizing, we can go back to the object
mode object selection, and now let's create
another shape. And this will be on the x, how many is this? We
need to count this. One, two, three, four, five, six, seven, it
should be seven on the x xs and then on the z xs. Yeah. Now I'm going to reposition this and
then move this up. We need create three
more wall here. Let's just create new shapes. This shod be on the
x and one on the z. And then move it up,
and then press build. Let's create another one. But this is wrong
in terms of size so I'm going to delete that one, and then I'm going to
create this one here, small one, it's one by one. But I'm going to set
the y value to two, so I'm going to
press or it's here. Now move it up and then
press built. There you go. Let's add this is three b one. It should be one on
three under z axis, and we can two, it's okay, and then move this here, and then move it There you go, we have the scene created. Now the last object that we
want to crate is the ground. Let's just create the ground, it should be ten by 13 on the z, and I'm going to
change the y20 0.1. Let's just move this here, and I'm going to
set the y value 20. And I'm going to
align this here. There you go. Now we
have the scene here. We can just hide the guide as we won't be needing
this for now. The next thing that I want
to do is I want to create a new material for
this probilar object. Here I'm going to
create a new material, and I'm going to call
this environment grid. I'm going to enable
the emission material. Here, let's just use the grid
texture that we've created, black and white grid texture. You'll see we'll
have an emissive of white because we set the color of the
emission is white, and we can set the base color
to a probably dark blue. Something like this, yes, and change the grid to
something orange. With this settings,
you'll see that when we going to use the post
processing effect later, the line will be glowing. Now we have this here. Let's just apply
this to the object. I'm going to select
the material editor, and then I'm going to assign the environment material here, and then I'm going
to apply this. There you go. Now we have a very nice looking grid
for the environment.
4. 03 Camera Path Movement: So in this video, we
are going to create the camera movement
using the Unity Spline. And with Unity Spline, we can create a series of nodes that will be
connected like a spine. We can adjust its curvature
with busier style handling, and we can move any
objects along that spine. So in order to use
Unity spine, first, we need to import it
from package manager. If we go to Unity Registry, we should be able to search
for spline and here, let's just install
the latest one. Currently, is at 2.8 0.2. Okay, so spline is
finished installing. Next, we can start
creating a new plan. But before that, let's just
a couple of things here. So here let's go to
Perspective view by pressing this box cube here, and we want to use the we want to move
the map object here. Because it's being occluded
by the ground here, so we can just
enable the progress. And if we move this, it
will move by one unit, and we want to reduce this. So here, we can just change
the SNAP value to something small like 0.125, for example. So if we move this, it will
move at that increment. So yeah, for now, we
can just position this here so we can see
it from the top view. And the next thing
that we want to do is let's just put this back to 0.5 for the snap value. Here, I want to move the camera, and this is going to be
our start position here. So let's just move the
camera around that position, maybe here and let's rotate
the Y axis by 180 degrees. So facing to the scene here, and we want to increase
the Y position. Maybe around 2 meters should be good or if we want to overwrite this,
we can just type this. I want to put 1.75. And we should have this
view as the first frame. And now we can create the spine. In order to create
the spine here, we can press this button here. There is a pencil and wrench. And if you press the
editor tool here, we can press Create spine. And this will allows
us to create a spine. So I'm going to start
from this position and then go to this green point here and maybe at another point here so we can decide
the turning point. And move along this point, maybe around this
position here and we can so let's move this few. And yeah, this should be the position or we can just press Escape
to cancel the last one. So this is the position. And we can rotate this if
we want to rotate this. We can just set up the bezier point Yeah, we can just direct
the busier here. There is a node here
and make sure that this last node is facing
to this direction here. Okay, so we have
set this up here. We can go to the side view. Now let's select the spline and let's disable the
spline tool here. And now we should be oh, sorry, this is enabled, and now we should be able
to move everything. So I'm going to move
the game object, the splaying game object to be around the
camera hive there. And maybe now we can
just press this again, and then for the first, can
move it slightly above here. So it's higher than
the starting position. And yeah, this is our
spline here for the move. Okay, so let's save
the scene here. I'm going to change this order, put it above the cube here. And let's reorganize
the scene a bit. Here, we have a bunch of cube. So let's create a new
anti game object. Let's call this environment. Let's reset the transform, so it's centered in the origin and select every cube that
we have on our scene. We can just drag this into the child of this environment
and we can collapse this. Okay, so Max, what we want
to do is we want to create the script for the camera so that the camera can
move along the spline. Here, inside the game folder, let's create a new
subfolder called scripts. And inside this
script subfolder, let's create a new shap script. And let's call this
script player move. And let's open this script here. Okay, so now we have
the script opened. We need to start creating variables that we are
going to use here, field. So let's just create
a serious field, and this is going to
be a private field. And we can search for
spline container, and spline container
is basically the component that we use for creating the spline
here, spline container there. And let's just call this path. And we want to create
a new enum for this. So let's just create
a path loop option. And let's just
call this NO path. And for that um,
we want to create a new num and we can just create that um in
the same file here. So let's just type public
num pathop option. And for the option, we want to have a loop options
and then a stop option. And make sure this num outside
of the class player move. Okay. So next, we want to create
a new private float, and this is going
to be the speed. Let's set this 23 by default. And that is going to
be a speed in which the camera will move
using that spit and it's in meter per second. So it's going to
be 3 meter/second. And we want to have a bullion, and this is called Is moving, and this will dictate whether if the camera or the player should move along
the spline or not. Let's create a couple of debug fields that we
can use to test some of the options or positions
in the edit mode instead of running the
game to see the position. So this is going to be a
private float preview distance. And this is going to be a bullion where we can
enable or disable the debug. We will also need to have
a couple of private field. I'm going to call this
distance travelled. And for variables or fields that are not
exposed and it's private, I always use this underscore. It's a good
convention. One thing to note on other video lessons, it will not be using
this convention as well, since those are old videos from when this course
was initially created. I apologize for any confusion, but please pay attention
to the name of the fields and feel free to follow which
style suits you the most. For the other private flout, this is going to be
the spline length or the path length,
in this case. And in start, we want to get the spine length or we want
to cache the spline length, so type path, and then
we can get the spine. And here we can get the
length of the spine. And basically, spine container
can have multiple spine. But since we are only
creating one spline, then we can just grab the spine. Otherwise, you can use the spline objects and
then add an index here, like an array or a collection. But in this case, we just
need the main spine. So this should be good. And here inside the update, we want to check
if the path is not null means that we are
assigning an object, a spline container to the path. And then if it's moving as well, then we want to run
the update code, which we are going
to use to move the camera along the path. So here we want to
increase the distance traveled by its speed, but we want to multiply
this by time Delta time. And basically, we
want to make sure that the value is
not frame dependent. So we want to use time
Delta time so that this value changes
is time dependent, not frame dependent, which is good because on
different devices, the FBS can run at
a different rate. And then we want to have a
normalized distance travel. And here we want to
create a function or method called calculate
normalized distance. And in this case, we want to use we want
to pass to float, distance traveled and
the splint length. Now we can generate this
method by pressing Control or command on Mac and
then dot the keyboard, and it will give us options
to generate the method there. So now here we can start
defining the method here, and basically we want
to return a float. So here in the calculate
normalized distance here, we want to create a
normalized value, and this is the distance traveled divided by
the spline length, but we want to change this
normalized value based on the path loop options or the end of path options
that we have here. So we can have a stop. Whenever the camera arrives on the last point of the curve, it will stop the camera or
else it will loop the camera. So here if the end
of path is set to stop Then we want to clamp the normalized value F
with a lowercase F there. And then we want
to clam function and we want to pass the
normalized value here, but we want to clamp
this to zero and one. Else if end of path is loop, then we want to
loop the movement. So basically here, we
want to normalize we want to run a modulo operation using this percentage
sine equal, and then we want to
modulize with one. So basically, it will loops
the value to zero if pass one there and then because basically it's
getting the reminder when we dividing this. So if it's a fraction like 1.1, then if we divide it by one, that the reminder is going to be the decimal values there, so it will loop the value
over and over again. And then we want to check if the normalized value
is for some reason, below zero, meaning that we scrub it
into a negative values. Then we want to normalize we want to
set this value to one, and then we want to add
with the normalized value. So we want to invert
the increment, and then here we want to
return the normalized value. So this should handle different pass loop
options behavior. And then we want to create a new function called
reposition camera here, and we can pass the
normalized distance. So here, let's just
generate this using the Control dot key
combination there. And now here in the reposition
camera, it's very simple. Actually, we can just
access the path object, which is the spine container, then we can evaluate
and evaluate takes a normalized distance
here so we can just pass the normalized distance
and then we can output the position and the type that is being outputted
is flow tree. It's not a vector t,
but we can easily use this into a method that uses vector three
because it's compatible. So we want to output
the position, and then we want to output
the tangent which is the forward of the
curve direction, and then we want to
output the up vector. Okay, so this should do. And then we want to set the
camera position to position. And for the rotation here, we want to use the
quaternion rotations. And as you can see, Fisostudio already give us suggestions,
which is correct. We want to use the
look rotation and forward using the forward
value and the up value. So we get the correct
rotations along the path. And we can just remove all of this system collections name
space that we are not using. And the last thing
that we want to create is a validate method, and Unity has this built
in on validate method. And basically, this method is being called whenever we are changing value
in the inspector. This is good for
debugging stuff, so we can just check if
the enable debug is true. Then also, we want to check
if the path is not null. Then we want to create
a normalized distance. We can use the calculate
normalized distance method. In this case, we want to
use the previous distance, not the distance traveled, because this is
the value that we can see in the inspector
that we can debug. And then we are not going to use the spine length because
at the edit time, we won't have spline
length available for us since we are getting
this value on start. So what we want to do is we know that the path is not
known because we checked here. So we can just type path,
spline, get length. And this should do and
what we want to do here, we want to reposition the camera using the
normalized distance. Okay, so now we are done
with the player move. Let's go back to Unity and let's apply this
to our camera here. Here in the camera, we can just direct the player
move component here, and we should be able to
direct the spline into the spline container here and
we can save the scene here. Now if we enable the
bug here, for example, you see the camera
snaps to that position, and if we scrap this,
the camera moves. So this is useful
later when we are going to define our
shootout point. Shootout point is a
position where we want to stop the camera and then we
want to spawn the enemies, so we can scrap and see which distance is
going to be the best for this camera position. Okay, so let's
just zero this out and enable the bug here. And now if we go to
GameView we can press play, and then we can
see the movement. Okay, so it's running at
three units per second. And I think we might need
to lower this to 1.5. So let's just see if it's
nice enough, the speed. Yeah, I think this is nice. And here with the end of path
options here, basically, we set this to stop and enable the debug here,
camera won't move, but if we move this in a
positive values that it moves. But if we set this to loop
and we scrap this two, it will loop to the
end of point there. So we can create the
looping movement by changing this end of path. Another thing that
we can test is the moving and stopping the
movement of the camera. So let's enable the bug field first and let's run the game. So now if I uncheck
this is moving, it will stop and if
we check this back, it will continue move. We can use this to stop the player on a position we are engaging
with the enemies. Yeah, that concludes our
player move lessons.
5. 04 Creating Shoot Out Point: In this video, we are going to start implementing
the shootout point. And this shootout point
basically a position where the camera will stop and the
enemy will start spawning. And when the timer runs out, or when the player has eliminated or kills
all of the enemies, then the camera
will start moving again following
further to the path. So with the camera here, basically we have
this distance, right? And we can create a series
of shoot out points that have a distance value that dictates whenever the
camera needs to stop. So when the camera
arrive at that distance, then we are going to stop it. So in order to do that, we need to create a
new script here. So inside the Asset
game scripts folder, let's create a new sharp script, and let's call this
heed out point. And let's open this script. Okay, so here in the
script here, first, we want to create a new public boolean
properties called area clear and this for marking if the shoot out point
has been cleared or not. And then the next
one that we want to create is private boolean, and this is the active point. And this will dictate if this current shoota point or this current instance
of a shoot up point is the active one or not. And we need to also cache
the player move script. Let's just put this
under the player move. Variable here, I'm
going to add a dash or an underscore for
this active point since this is a private field. Now first, we need to create a public method and this for initializing this
instance of shoot off point. We want to pass a
player move object. And whenever this
method is called, we want to pass the
player move and cache this player move into
our underscore player field. And we don't need
the start method, so we can just safely
delete this here. And the next one that
we want to create is we want to create
another public method, and this is for
starting the shootout. And when starting the shootout, we want to set the active point through here in the update, we want to do some
sort of the bug here. So I'm going to add an
input get key down, and this is just for simulating the shootout point when we are
testing it in this lesson. So when space is pressed, we want to stop the
player movement, but we don't have that yet. So let's just type
player move here, and then we can just type
the method that we want to create and we can
pass a bullion here. Now that we have this created, we can press Control
and dot here, and basically, we can generate
the set player movement. And this will generate a public method here
or an internal one. So we can change this to public, and then we can
call this Enable. And we want to set the
moving variable to enable. So when this method is
called and false is passed, then we are setting
that is moving tube false here
like this one here. And here we want to do
another get key down, and I want to use Return
for now or Enter on PC. And here, I'm going to set the player move set
player movement to true. So we want to continue. We want to simulate whenever a shutle point is clear here, and we can mark this
area clear to true, and we can set the active
point to falls here. Other thing that we
need to check here is whenever we are
continuing here, we want to make
sure that this is the current active point. Otherwise, when we are continuing from the
first shootout point, the second will also be clear if we don't have
this active check. Let's save this now let's
work on the player move here. Here in the player move script, we want to check whenever we have arrived in
the shootout point. But before that, we need to
create a custom class here. Let's just call this
shoot out entry. And we can mark
this serializable. And this is coming from
the system namespace. So if you haven't had
this using system, then you should add it first, and then we can mark
this as serizable. And this will allow this class to be shown in an
inspector later in unity. And here we need to
create a public field, and this is going to
be the shootout point. So we can assign a shootout point to this
variable or to this field. And we want to add
a float distance, and this is going to be the distance that we are
going to test against the camera position
to whether we need to stop the camera
or not or the player. So here inside the
uptick method, and when we are moving here, we want to add a loop here, and we want to look through a field that we
need to create first. So here, let's create
a new series field. And this can be a private. And this is going to be
the shootout entry array. And let's just call
the shootout entries. And here we can look through
the shootout entries length, and we can create a
local variable here and cache the current
entries based on its index. And then here, we can
check the position. So we want to use the
path, evaluate position. And we can use the entry distance value divide by the spine length and to
get the normalized distance. Then here we can substract it with the transform
the position. But since a valuate position
returns a flow tree, we need to cast this
into a vector three. Since we are doing operations
with a vector three, which is the transform
the position. So we need to cast this
to a vector three. That's at a parenthesis here, and now we should get the, we need to check
for the magnitude, but square magnitude is faster because we don't do square root operations
with square magnitude. And to know if this
is quite close, then we can just
compare this against a very small value
which is like 0.01. And if this is true, then it means that the
camera has arrived in this distance position here. Okay, so now when this happens, we want to check if the entry
area is cleared, sorry, shootout point area is cleared, and this is why are we created this area cleared as
a public properties, so the player move
can read that value. And if it's cleared, then we
want to continue this loop. So it goes to the
next iteration. And here we want to
check if it's moving. Then we want to stop by going
to the shootout point and then call the start shootout method that
we've created earlier. So now, save this. Another thing we need to add
before testing this out, we need to initialize
every shootout point here. Here in the start method. We want to add for each loop, and we want to look through
the shootout entries here. And let's call this item entry. And for each entry, we want to access
each shootout point. We want to pass the player move into the
initialized method here. So we can just use
this keyword here, and this will pass the player
move into this method here. And another thing
that we need to make sure is that whenever we are
starting the shootout here, we need to make sure
that we are stopping the player movement here using the set player
movement method. So here I'm going to add that call to this method here
and set these two falls. Okay, so now we can save both of the files here and
let's go back to Unity. Okay, so here in unity, first, we want to create a
new anti game object, and this is going to
be the shootout group, where we're going to put every shootout point object in this empty game object so we can better
organize our scene. Let's create an empty child, and let's call this
shootout point number one. We can add the
shootout point script, and then we can also copy this and rename this
to shootout 0.2. And on the camera
game object here, now we have the shootout
entries variable, and we can add a component here, and we can direct the
first shootout point to this shootout point here and the second one to
the second shootout point. Now, let's save the scene here, and we need to test
some values in the preview distance to get the distance value for
each shootout point. So for the first shot out point, let's enable the debug
here and scrap this value. And if we go to top view here, we want to stop the camera at that first green point here. So around these position
should be good. Let's round it to 2.6 and
set that distance to 2.6. And if we scrub further, we want to stop the
second shootout point in this green in the second
green circle here. So maybe around
this position here. Let's copy this value
and paste it here. So we have set up this
shoot at point distance, and let's just reset the preview distance and disable the enabled bug here and
let's save the scene. And whenever we are testing
or we want to build the game, we need to make sure that
this enabled bug is disabled. Otherwise, it will mess with
the camera movement later. Okay, so now let's go to the
game you and test this out. Okay, so now at the
first shootout point, you see that the camera stop and is moving gets
set to falls here. And later we are going
to spawn enemies, do Tr come down, and then also check whether if all of the enemies
has been killed or not. But for now to emulate
the area cleared, we can just press
return or enter, and it'll continue move. And then when we arrive to the second point,
we can do the same. By pressing Enter, we
are simulating the area cleared and we reach at
the end of the path here. So, yeah, there is what we need to do to create
the ShutlePoint, and this concludes this lesson. And we are going to
continue on the next video.
6. 05 Basic Shooting Mechanism: In this video, before we
continue to shoot out point, we need to make
sure that we create a shooting mechanism
for the player. In order to do that, let's
create a new script, and let's call this
player script. Next, we are going to create a interface an interface script for creating a basic
method for hable object. Let's just create
a new folder and let's just this interface. And I'm going to create
a new C sharp script, and I'm going to call
this I hittable. Basically, for the standard
practice in C sharp, interface, we name the class with an I in front of
the class name here. Interface able. And we
are going to remove all of the un here and it's not
going to be a moo behavior, and it is also not
going to be a class, but it's going to
be an interface. And we want to also
delete this method here. And basically interface is a basic contract for other
script to implement a method. So for example, we can
declare a method name and all of the other clays that implement this interface needs
to implement that method. For example, let's create
a new method called it. And basically, if we
create a new script, for example, if we
go back to UT here, and let's create let's
create a new SSH script, and let's call this
dynamic dynamic objects. L et's open this script here. And we can make this object implement the hit able
by adding a comma after mono behavior and then type the interface that we
want to implement. Now it gave us an error
because our class here does not implement interface member hit. We need to do that. But before I'm going to
implement the hit method, I'm going to modify
it and for the hit, it will ask for a object
for the argument. Let's just call this
hit. And safety. Now if you are
using Psal Studio, you can just highlight the interface on
the dynamic objects class and then press show potential fix and then
implement interface. You'll see that it will
automatically implement the method that are
declared by the interface. Let's just delete this here, and now we can do
anything with it. For example, if I just
delete the built in method, and let's create a new
private rigid body RB and we can just initialized
the rigid body on start. Let's check if get component rigid body is no, Then we one to create a new one. Or perhaps it will be
better if we try to grab. Let's just put the get
component rigid body inside this variable here. Let's just duplicate this
and then paste it here. Now we can check if the RB is, then we want to create
a new rigid body to it. Let's just type RB
equal game object. I think it's at component
rigid body. There you go. This way, it will make sure that any objects that are using this class are going to
have a rigid body in it. Now whenever we got hit, we can always apply a
force to the rigid body. For example, we can just
type rigid body at force, and we can get the vector
from the ca it information, and the cas hit information
has a lot of information. For example, we can grab the normal and then we can inverse the normal and then multiply by some value 50 here or a 100. This will be a basic, and of course, we are
going to modify this. Let's go back to unity, and then let's open our
player scripts file here. In this script, we want to
create a mechanism where we can shoot to an
object in our scene. Since the player script is going to be attached
on the camera, we can create a new
private camera variable and grab the camera component. Let's just create a new
camera variable called CM. On start, we can always just
use the GT component camera. And save it. The next thing that we need to do is we want to make sure that whenever the mouse is clicking
the left button, get mouse button down, and the left button is zero. One is for the right button, and two is for the
middle button. Whenever we click the
left mouse click, then we want to
create a new y cast. Let's just type ray, and
this is object ray object, and let's just call this ray. And this will be
a cam for camera. I have a method, which is screen 0.2 ray, and this is basically will
shoot ray from a screen point. For the screen point,
we are going to put the mouse position
input mouse position. There you go. Now
we can always try to test the ray using
the physics ray cast. It has a lot of overrides,
as you can see here. We are going to use this one, I think, this one. Yeah, this one, we
can use this one. So basically, we want to
pass the ray and we want to out the RCAs hit info. So we need to create
a new cs here. Let's just create a Cas hit.
Let's just call this hit. And we can pass
the value to hit. So whatever the
resulting ras hit will be safe to
this hit variable, and we can pass this
to the other class. That needs that information. And I think the next thing
will be the max distance. Let's just put it 50 units. And if it hits something, then the code inside the if
statement will get executed. Let's just create a
new itable object. Sorry. We need to make sure if the collider is not known first. Let's create a new I statement
and here we want to check if the hit variable or the hit object has an
collider attached to it. So if it hits something,
it should have a collider. It's not null, then we want to grab the hittable
object if it's available. Let's just create a new i hit hit, let's
call this hitable. Then I'm going to
grab from the hit, get hit collider, get component, and here we can
grab the i hable. There you go. We are
going to check this again if itable is not null, then we want to run
the hit method here, and then pass the
cast information hit. We can also print its name by running the debug log command and pass the collider
game object dot name. Okay save this, and now we can
attach this to our camera. Let's go back to Unity, and if we select
the camera here, we can add the player
script component, and for testing it out, let's just create
a new cube here. I'm going to create
a new normal cube. I'm going to probably put it
here or here in the middle. And put it on top here, and let's add the
dynamic object script. Let's save this and
let's give it a try. And let's open our console here. If I click here, you see that we are hitting
this cube here. And if I, since the
object name is the same, the debug lock is collapse. So let's just call this box, this one here, and save this. And now you'll see that
if I shoot this box here, it moves because we have this
We have this where is it? Because we have this at first
comment on our hit method. Basically, this
script will check if the object that
gets hit by our Cs, does it have a hit component or any class that implement
hitable interface? And if it's not null, if it's available
or if the object has an hable interface
attached to it, then we want to execute
the hit method. And we can implement this
in any way we want to. For enemy, we can
put the health. We can decrease the
health inside the hit. Method interface
is quite powerful. And this is how we do the
basic shooting mechanism. And of course, we are going
to expand this mechanism.
7. 06 Initial Enemy Setup: Hello, in this video, we are going to continue
our project, and we are going to create the basic simple
enemy mechanism, and of course, we are going
to expand those mechanism. First, let's just
delete the box here. We don't need that anymore. And we need to define
the NAF mesh area. In order to do that, we can go to the window under the AI, click the navigation menu, and it will open this
navigation panel. And here under the bake, first for the environment, we want to set
this two a static. Let's just enable the static and then, change the children. Now once we change
this to static, if we go to navigation, we can press the big button
and then it will bake our area here. There you go. As we can see, we
have an area here, but in this position, There is an issue
here because we are going to need for the enemy
to hide from this area here, and we don't have
navigation math. We need to decrease the
radius here to maybe 0.3. We have a smaller
agent size and it will have it will create more area
for the N the N mesh area. Let's just create bag
again and see if. As you can see here,
now we have NAF mesh inside this and it's
connected here. It's better. We can create from this position and then it will
be able to move to a certain position outside
of this hallway or alleyway. Once we already
create the NF mesh, if we go back to the inspector, this blue navigation
mesh gets hidden. The next thing that we want
to do is I'm going to create a new capsule For the enemy, I'm going to create a
new capsule object, and this will be the enemy. And to be aware of the
direction of the capsule. I'm going to add another
three d cube object, and I'm going to
make this smaller, like so and then move
this to the front here. So we know which are currently the capsule
is facing right now. Yeah, let's go to the lighting here and I want
to disable the ato generate. So it doesn't generate
the lighting every time and save the scene. This is going to be the enemy. Let's just call this enemy. First, we need to add a
navigation mesh agent, and also a rigid body. Since this object
is going to move, we need to also
assign rigid body. But if we don't need the physics interactions or the dynamics calculation
to this object, we can just disable
the use gravity and then enable
the is kinematic. Okay. Now let's change the
navigation mess radius to 0.3, so it's the same with our
navigation mesh settings. For the capsule, I'm
going to change also the radius of the capsule
collider to around 0.3 also. Now we have the enemy. The next thing that we need to do is we need to create a
new script for the enemy. Let's just create
a new Shop script, and then let's call this enemy
script. Let's open this. Now there are a couple variables that we need to define
for this enemy script. First, we need a
serialized field, and this should be integer, and let's call this Mx health. This is for defining
the maximum health. Then we are going to also add a private
integer and this will be the real health or
the current health that we use for calculating
the enemy health. Then we also need a serialized
field type of transform, and this will be the
target position. And the target position is where the enemy are going
to reposition or move after he came out from the hiding or the
ambush position. The other thing that we need to have access to is the
transform of the player. Let's just create a new
private transform variable, and let's call this player, and we also need a
bleion variable, and this is for toggling whether the enemy is
already dead or not, and the last one will be a
NF mesh agent component. But if you can see here, it doesn't show in the out
complete because you need to using the unit
engine AI. If we Highlight the class here and
then show potential fix. We will have an
option to add this. I'm going just to
click this and it will also be automatically
added on the script, and I'm going to
call this Agent. Let's create a new
initializing method, and this is going to
be a public method. Let's just call this in it. Another thing that we
need to create, I forgot, we need to have a reference
to the shoot out point. Let's just create a U
variable shoot out point. Now on the init, we are going to pass the shootout point and
just call this point. Let's just assign the value
point here that we pass as a parameter to the
shootout point. Okay. Now we are going to
grab the player, also the NF mesh agent. Let's just type agent equal
get component NF mesh agent. For the player, we can just get the camera class and then get the main camera and
get the transform. There you go. We have
the player transform. Inside the update, we want
to also make sure that the enemy we also
always face the player. Let's just check that. Inside the update, we want to check if the player is not null, and if the enemy is also
not dead currently, then we want to face the player. In order to face the player, we are going to create
a new factor three, and let's just call this
direction, and for direction, it will be the player
that position, substract with our enemy
position, current position. Then I'm going to
zero out the y value. The rotation will be strictly
on the x and z plane, and we can set the
transform rotation, rotation to a quaternion look
rotation at the direction. Now inside the init method, we are also going to add the
destination for the agent. Let's just check this
if agent is not. Then we want to set
the destination to our target position. Target position position. We want to grab the target position position as the destination for the agent. Another thing that we
need to make sure that we need to disable the
rotation of the agent, the me agent component
doesn't drive the rotation. Since we already handle the rotation ourself inside
the Pate method here. Let's just add code here, Agent rotation and
set this to falle. This is basically we'll
ignore the rotation, and we are going to also
create a hit method and we need to implement
the interface hittable. Now once I've implemented this interface, you'll see
that we have an error, so I'm going to click the show potential fixes and then
implement interface, and it will automatically
implement the interface here. Right now, we can just
subtract the current health, but we need to
initialize this also. Inside the init, let's just set the current health
to the Mx health. I. And then we can subtract the current
health in the hat method, and also print a log
message to the console, am shut and if the current health is
less or equal zero, then we want to enable
the s is that bulto true, and we also want to disable the agent component
set to false. Another thing that
we need to do is, we need to make sure that if that's true then we want
to return this method, so it doesn't execute any
of the code here below. The other thing that
we need to set up is, I think that would be all. Let's just give it
a try and we need to do a couple of modifications
on the shootout point. Let's just go to
the shootout point. Now we need to create
an enemy entries. I'm going to create a
custom class here outside, and I'm going to add a system
serializable attribute to it and call this public
class enemy entry. And this is custom class, so it doesn't need to
derive from mono behavior. I'm going to create a new
public enemy script, the enemy. Then for the other value, it would be a float delay, and this is the
delay in seconds. How much time we will need
for this enemy to appear. Now we are going to add
a new serialized field, and this will be the
enemy entry array, and let's just call
this enemy list. And we are going to create a new coroutine for
spawning this enemy here. Let's just create a new
i enumerator method that returns type enumerator. Let's just call
this send enemies. And Inside the cotin here, we want to look through the
enemy list collection here. Let's just type
for the variable, it will be the enemy and inside the enemy
list collection. Here, we are going to add a yield return new
weight for seconds, and we are going to pass
the enemy delay here. This is the delay
that we declare in the enemy entry here. And after we delay the
code execution here, we want to access the enemy
that enemy initialize. So when it's initialized, it will start the
enemy to move here, as you can see here,
once we initialize, it will set the destination
for the enemy NMS agent. So the enemy will start moves. But we need to pass
the shootout point. Let's just pass
this shootout point here with the dis keyword. If we want to, we can just also debug the
enemy object name. Let's just type enemy, enemy, game o name spawn. Inside the shootout
point script. We also need to add
a private variable, which is an integer, and let's just call this enemy killed. This is for counting how
many enemies that we already killed and after we kill all then we can move
to the next area. Let's just create a
new public method and let's just call
this enemy kill. For this public method, we are going to increase
this enemy k integer. Let's just type
enemy kel plus plus. This means that we
are increasing by one or incrementing
by value of one, and we want to check if the enemy kel value is equal
to our enemy list length. Then we want to continue
the movement here, the movement of the player. Let's just use this
this value here. Copy this and then
paste it here. Okay. So now that we've created the public method
for the enemy kilt, we need to call this
from the enemy script. Whenever each of an instance of the enemy script gets killed, then we want to run
this method here. I increase the enemy
killed value here. Let's just go back
to the enemy script and inside the
current health here, we can just access the shootout point and run
the enemy killed method. And save this. Of course, this is still the basic
implementation and we are going to modify
this more later. Let's just go back to
unity and test this out. This will be the enemy here. I'm going to set this or save this as a pref
Inside the game folder, I'm going to create
a new folder. And inside this prefabs folder. I'm going to direct
this enemy game object as the prefabs. Now since we already
changed this to a prefes, we can just add a script, which is the enemy script, and then we can apply
this to the prefabs here. Now we need to
reposition the enemy. Let's just enable the
navigation and I'm going to move this enemy
to this position here. I probably here. I'm going
to duplicate this enemy. But first, let's just set the health value because
currently it's still zero. Let's just set this
23 and apply this. It will be the default value. We can duplicate the enemy here, and we can put it here. If we go to the navigation, we can see, we can
put it for over here. Then in the shootout point, we can create a
new t game object. And this will be the position. This is for this enemy here. We can move him to
this position here, and we can duplicate this anti game object
and put it behind here. The second one will appear
in this position here. We can move this slightly
to the site here. Let's just rename this. This is the target position one, and this will be the
target position two. Okay. Now if we select
the enemy here, I'm going to set
this enemy here, the target position to the
second target position. And for the first enemy here, I'm going to set this as
the first target position. Okay, let's save the scene here and under the
shootout point, we want to set up
the enemy less, let's just set this 22, and for each of these entries, let's set the first
enemy two this one here, and the second enemy will be in the second entry here and probably we need to set a delay, so let's just set
this 23 for now. The next enemy, we
can set this two. Basically, this will get
triggered after this is shown is B if we go
back to the code here, you'll see that in
the shootout point. The four e loop will get led
by the first enemy delay. And then it will
initialize that enemy. And then on the next loop, it will delay the code based on based on the
second enemy delays. For example, this this enemy here or sorry, the second one, will get shown after 5 seconds after 5
seconds approximately. So let's just give
it a try here. I'm going to check one more
time by the code here. When we start shoot out, there's another thing that
we need to do is, we need to start this
coroutine here when we run the start shoot
out method here. Let's just run the start
routine SN Enemies. Save this, and let's
get back to unity. Now after it's compiled. Let's give it a try. I'm going to enable the
console window so we can see the message here, the debug lock message. Let's press play. Sorry, I'm going to generate
the lighting one time. Now we have the light bag, save the scene again
and then press play. Now let's take a look
when we stop here, the enemy after 3 seconds show, there you go. We can shoot him. And if we go to the enemy here, the under the inspector,
the first enemy. We can check its health, and now our current health
still one so we can click one more time. There you go. It's zero. But we need to destroy or later if
we use the character, we need to play the
death animation. Let's just use destroy.
First now here. In the enemy script.
After we kill, we want to destroy
the game object. Save this let's give it a try. If I go to the prefet here, let's just fix a
couple of things here. Let's just remove the collider from the box game object here. Let's just remove the collider. Yeah. And save this again. Let's run this one more
time and give it a try. Now the enemies show up, we can delete here, and then
we can shoot three times. After we kill all the enemies, you see that the player
starts to move again, and we can define another
shootout point here. Yeah, that is the basic of
the enemy initial setup, and later we are going
to change the model with the real robots character
and also with animations.
8. 07 Enemy Animations Preparations: Okay. Now in this video, we are going to continue
develop our enemy mechanism, and now let's start
preparing model. If we go to the game folder
and inside the game folder, I have a Robot Kyle model, and inside the folder model, we have the FBX file, which consists of the three
D model of the Robot. First, we need to change the RIG setup to a
humanoid and press apply, and we can leave the other
settings to default. Now once we have the
animation type, change, we want to open
this on Explorer, so we can see the FBX model, and now we need to prepare
a couple of animations. In order to do that, I've used Mixamo for creating
custom animation. You can go to the address, which is mixamo.com.
Just register. It's free, and then after
that you can log in, and we can also upload
our own character. Press the upload
character and I'm going to copy the model path here. I'm going to select a character file and
then paste the path, then I'm going to
upload the FBX file. Once we upload the FBS files, we are going to pick one
of the animation library, and that library will be retarget to the
Robot kyle object. So we can use the animation correctly and fit very nicely
to the robot character. Press next once already
done just PressNx, and now we can select many of the animations that are
available. We can also search. I'm going to search
a walk with gun. Or we can search just gun and there should
be a lot of gun related. Let's just search for
the walk with gun. Or we can just search for
rifle. Yeah, we can use this. We need a forward motion. Probably the one that are not
firing would be the best. We can use this rifle walk. I'm not sure if slow, let's just for the
one that are probably faster. This should be better. We want the translation
information because we are going
to use that to create a very nice motion that are synchronized
with the animation. Press download and then
set the format two FBX four unity and we
can choose without skin, and then just download it. Once we press download, it will prepare the assets
and we can save the FBX file. I'm going to paste
our path here. And then go to the game folder and I'm going to create
a new animations. Then I'm going to save this. But probably I'm
going to change this to run forward rifle. Once we save this, let's search
for the one that are sts. This one, we can use
this one for sidestep, but I think this is too slow, so let's just look for
one that are faster. Do we have this on top? Let's just search
from the other pages. This one should do.
We can use this. I'm going to press
download again and we have the previous
settings reed, just press download and then
save in the same folder. We probably need a animation. This one is quite nice
actually. Or this one probably. Yeah, we can use
this, press download. I cannot include these
assets in the tutorial because the terms and
license for using Mixamo, so you can just sign
up yourself and then download the
animation that you need. I'm going to download
this one and let's just call s for animation. Or rifle that. Yeah. I think we
can use this one. Now we can press download
and into the animations. Now that we have all of
this, animations prepared. Let's go back to unity, and it will have the animations
automatically imported. But we want to set up the
animation type first. Let's just select all of
the FBX files here inside the animations folder
and choose humanoid. Then for the Avatar definition, since this is only
an animation file, there are no avatar
inside of it, so we need to copy
from the other vor, Let's just copy from
the other Avatar, and we can choose the
Robot Kyle avatar here. This one here. It's from the Robot Kyle model
inside the model folder, and it's from the
Robot Kyle FBX. Let's just select this one
and then press apply. Okay. So now if we select one of them, and if we go to the animation, there are some issue here. Let's check. The issue was with
the model settings. So we need to change the
scale factor to 0.01. We need to make sure that
the settings here are the same with our
Robot Kyle model here. As you can see, it sets
to scale factor 0.01, and all of the
settings are unchecked except blend shapes and
sort hierarchy by name. And this is the most important. This convert units is unchecked. So we need to make
sure the animations have that same settings. Once we uncheck the convert
units and the rest of the options, press apply. Now if we go to the rig, you'll see that we don't
have the error anymore. And if we select one of the animation and we go
to the animation window, you see that we have a very nice animations for
the robot here. Okay. Now we need to set up
a couple of things here. First, we want to
set the rotation, not to the body orientation, but the original and
then press apply. Here, we want to
change the two feet. We want to set these
two big into pose here, and this one is also
a big into pose. Now we have a very
nice moving motion. If I close this, and
try reselect this, you'll see that the robot will move linearly on this line here. So it moves very nice. But make sure that for the
root transform position, we don't want to check the
big interpose options. For the other the
left cover here, let's just set the same thing. Set this two original,
big inch pose, and this is two feet for
the root transomposition y. Big inch pose, and let's just leave the root
transomposition. And now if we press play, you see it moves nicely
to the x axis here. The other thing that
we need to set is for the hat reactions, and this make sure the root trafposition y to feed and leave
the other settings. For the des animation, make sure we have
the same settings. And what this setting
is basically do. We are preserve the
original rotation. So it's facing correctly on the z axis, the way it should. And for the root
transform y position, we want to bake into
the pose so it won't change the translation
in the animation, and it starts based upon the feet on the first
frame of the animation. There is one animation
that we forgot to pick up. So basically, we want to
pick up the rifle idol. So let's pick up this one. Or we can, the rifle aiming
idol should be better. And then download this and save it to the
animations folder. If we go back to it, you'll see that we have
this new animation. I think it's wait a
minute. This one. Let's just set up the
model taps first, set the scale factor to 0.01, and check all of these options, except the blend shapes and
the short hierarchy by name. For the rig, change
this to humanoid, and copy from other Avatar, and I'm going to pick the robot Kyle avatar
and press apply. Once we've already set that, we will have this aiming. For the aiming, we can set this loop options to
enable the loop time. Set this to original,
Big into pose, set this feet, Yeah. For the running, we also need to enable the
loop time I forgot before. Press apply, check this
and then press apply. Also for the left cover, we want to also set the
loop time to enable.
9. 08 Enemy Animations Scripting: Okay, now we are going to continue preparing the
animations for the enemy. Now, the next thing that
we need to do is we need to go to the prefabs
folder and then open the enemy prefabs by pressing the open prefabs
here in the inspector, and it will go to prefab modes. Now here on the enemy, we can delete the cube object. For the enemy, we can
also delete the mesh, caps mash filter component, and also the mesh render. We only have the collider, the mesh agent, rigid
body, and enemy scrip. Now I'm going to drag
the robot kyle model. Let's go to the model file and then drag this as
a child object. Now if we go to the perspective or the isommetric view from
the right view, if I select the enemy, you'll see that the robot is basically in this position here. Let's just align the robot game object to the base of the
collider and the NM agent. Select the robot Kyle,
just move this down. And there you go. We can also
adjust the capsule height. Let's just select the
capsule collideer. And if you over the top
part of the capsul coder, you'll see a very
small yellow square. It's like a handle and
you can drag this, drag this and move it, so it's aligned
with the upper part of the head of the robot. For the NF mass agent, we can change the height, and here we have the value
of our capsule glider, just select the
height value copy, and then paste this inside the height value of
our Nap mas agent. We will have the same height. Now we have this setup, we need to create an
animation controller. If we go to the
animations folder, I'm going to create
another subfolder inside the animations folder, and I'm going to call
this controller. Here, I'm going to create
an animator controller, and I'm going to call
this base enemy. If we double click this, you'll see that we have
this animator window open. We need to set up a bland tree. State here, and this will
be the default state. Inside the bland
three, we will have the options to change
the bland three type. I'm going to use a
free from directional, two d free from directional, it needs a parameter. If we go to the parameter, it automatically create a float parameter with
the name of bland. I'm going to create the
other float parameter. This will be the x speed. I'm going to rename this
by double clicking it, and then call this x speed, and this will be the z speed. And now we are going
to use the x and the Z speed as the parameters of our
plan three type here. We need to create a
couple of motions. Let's just add motion field. We need five motions. The first motion will be
the rifle aiming idle, this one here, we want to have a value for the x position
20 and the y position 20. The x and y positions
corresponds the x speed and z
speed value here. The other thing that we need
is the run forward rifle. The third one would
be the left cover. Pick the left cover again here. For the last one, this would
be the run forward rifle. Basically, we are going to
invert the last two animation. This will go to the right side and this will be run backward. In order to reverse
the run forward, we need to change the speed
to negative one on the, the run forward rifle,
animation entry. For the left cover, we want to enable the
mirror animation, so it will mirror the animation. And now we need to
change its value here. If we are idling, then we want to set the position
x and the position y20, and if we run forward, we
want to set the position y21. The z value should be one
to trigger this animation, but the x speed should be zero. For the left cover, we want to set this
two negative one, correct, and for the right one, which the left cover inverted with the mirror options here, we want to set this 212
positive one, and the Y position 20. For the run forward that are reverse in terms of animation
speed here, we want to set the position x20 and the position y
two negative one. Now we have a very nice
settings over here. If we press plea, and
then I expand this, you see that we have
the ideal animation. But if we start modifying
the x speed here, you see that it goes to
the right side and here, it goes to the left side,
as you can see here. If we set the z speed to one, it will move forward and it
will start move backward. And we can test this
out by dragging, I think we can drag this yet. You see, if I move
this sideways, we will have a slightly
shifted or tilted motion, and it blends very nicely
with the animations. As you can see
here. Now we set up the bland tree correctly. Let's just set this
back to zero, zero. Now we need to go
to the base layer and then start set up this
so it works with the script. Let's go back to the scene view here and select the robot kyle, and now we want to direct the base enemy controller to
the robot kyle controller inside the inspector and leave the apply root
motions enabled for now. Let's go to the enemy script. Now we need to add a
private animator variable, let's just type animator, and let's just call
this ym On start, we want to grab the ym
component by typing ym equal, and then we want
to get component. But this time we want to get the component in
children. Why is that? Because if we go back to it D, you see that the script is on the parent and the animator
is on the child object. Let's go back to
the script here, and then grab the
animator component. The other thing that we need
to do is we need to create a method called Run blend. And save this. Now the animation will drive the translate or
the movement of the enemies. We need to disable the update position on
the agent components. Just type agent, update
position, set this to false. The other thing that
we need to create is a private for three variable. Let's just type vector three
as the type or the class, and I'm going to call
this movement local. Okay. Now inside the
run blend method, we want to check if the m is not or the m is not disabled, so enabled, but with
the exclamation sign. Then we want to return. But if it's or if it's disabled,
then we want to return. This method won't gets executed. Now we need to check if
the agent next position. Next position is
basically gets or sets the simulation
position of the NMS agent. This way we are getting
the next position, the simulated position, and we want to substract with
our current position. We want to check the distance of this position by using
a square magnitude. We can use magnitude, but
square magnitude is more performant because there are
no square root operation. It's less than, for example, a very small value 0.01. Then we want to make sure the animations are running
to translate the enemy. Here inside the statement, we want to modify the movement local to the direction
of this vector here. Let's just type or just duplicate this
calculation vector. But we want to get based on the local transform of
the anime game object. Let's just get the transform and then use the inverse
transform direction. Since this is method, as for
a factor three direction, as you can see here on the help, so we are feeding this
direction to this method here. Make sure we put a sem
cooln at the end of line. Now outside this if statement, we want to access
the anim variable, which is the animator companion. Then we want to set
the float value, which is the, this
should be string, X speed, and this
should be named correctly as the one
inside the animator. Window here, the parameters
for the animator. Now we want to pass the
movement local dot x. Then for the next line, it should be the set
float for the z speed, and this one would be
the movement local z. Now we save this
Now to test this, we can just run the method
inside the update method. Let's just run the run
plan here and save this. Let's give it a try and
let's go back to the UT app. Go to the scene here. If we exit the prefabs, you see that both of our enemy are change or switch to
the robot game object, since this is a prefab,
it gets affected. Now if we press plea, let's test this out. On stop, when it's appear, it should play the
correct animation. There is an issue here. Sorry, there is a mis type
on the code here on my end. Basically, we need
to make sure that the square magnitude is
greater than this value, then we want to calculate
the movement local. Means that if the transform
position hasn't arrived yet, then we want to calculate the movement based
on the direction. Now let's go back
to unity and then press to test this out. If we stop here, it
shod start playing. There you go. But it moves
further, as you can see here, the enemy, because the
parent game object are staying here and the one that are moving
is the child game object. We need to make sure that
the root motion from our animator or animations are driving the
parent game object. Let's just create
a script for that. Here inside the scripts folder, let's just create a CO script, and let's just call
this animator move. Then we want to delete the start and the update method for
the matter move here. Let's save the enemy script. Here, we want to create a
on animator move method, which is a built in
method from unity, and this callback will
be invoked at each frame after the state machines and the animation have
been evaluated, but before on animator IK. Let's just use this. Basically,
we need the ym component. Let's just create a
private animator, call this anym just
recreate the start method, and then for the an ym, let's just get
component animator. Inside the animator move, we want to offset the
transform pan position based on the animation
Delta position. Let's just sorry not this here, but inside the anima
move on animator move. We want to access the
transform Parn position, and then we want to
increment this by the m Delta position. This, L s this and
let's go back to unity. Here, let's just
modify the prefep, so I updates the anima game
object here on the scene. Let's just open prefep. Under the Robot Kyle
game object here, object, we want to add
the animator move script. As you can see, once we add the animator move script here, the root motion are
handled by script. Animator acknowledge
that inside the script, we have this on animator
method or callback, and let's head back to our
scene here and save this. Now let's give it a try and
see if this fix the issue. Now if we stop, we should
see the robot appearing. Now it's moving and once arrived, it
stopped, as you can see. There you go. Now we have the robots and the robots and the
animations working. On the next video,
we are going to continue for the animation
implementations.
10. 09 Enemy Movement Fix and Hit Animation: We are going to continue
working on the enemy script and we are going to smooth
some movement in this video. Let's take a look at
what we have right now, and I'm going to press play. You'll see that when we selected the enemy and enable Gizmo, you'll see that the NMS agent is moving faster and it follows. And if I try to play this again, you will see also
that the robot object or the game object are
walking through the wall. If I dog this below
here, the game window, you will see that if we try to replay this
and I press play, you'll see that the agent will move correctly,
avoiding the wall, but the game object
are trying to get the nearest direction to
the agent gizmo here, the N mesh agent gizmo. We need to fix that. I'm going
to revert the layout here. In order to fix this, let's
open the enemy script, and now I have open here, and we are going to
create a couple of modification inside
the run bland method. The first thing that we need
to change is instead of measuring or comparing the
square magnitude between the transform position and
the agent next position, we can just grab the
remaining distance from the agent object. We type the agent variable. We can get the
remaining distance, and we can check if
the remaining distance it's greater than 0.01. Then it means that we haven't arrived to the destination yet. We can also use the agent velocity instead of
calculating the direction. I'm going to grab the
agent velocity here. And I want to make sure that
the NaF mess agent stays together with the game object
or the main transform. Here we can put Below here, we can type Agent,
the next position, and we can assign the next
position to the transform. Basically, what this
code is doing is, we are going to make sure
that the NaF mesh won't go as fast as before and it will
stick to the game object. Now we can save this and
let's give it a try. Let's go to the game
view and press play. If I highlight the enemy here, you'll see now the NF mesh will stay with the capsule
glider when moving. There you go. And there is an issue when we arrive,
so we need to fix this. But if you take a look, that you'll see that when
the butt start to move, it moves correctly.
It follows the path. It doesn't go through the
world anymore because we are compensating the NAF
mesh next position to our transform
position, every frame. So we are getting the velocity, and it drives the animation to move based on its velocity. But after that,
we are correcting the agent next position. Now we can tweak this even more. First, we can normalize
this value here. So it's taste one, and we want to add an else condition and
for the else condition, basically that we want
to set the movement local value 20
whenever we arrive. Let's just type movement
local equal to factor 30, and save this. Now let's go back to
unity and give it a try. Now it starts move and when
it's arrived, it should stop. Yeah, we still have issues. Okay. Now we can move this line here to be inside the
if statement here. We want to make sure that we are updating the next mag when we are still moving and when we
arrive to the destination, we don't want to
execute this como. Let's let's t to
play this again. Now it moves, and
when it arrives, it stops. There you go. But you see that the
stop transition, the dal animations transition very abruptly. We
need to fix that. Let's go back to fical Studio. And here we are going to add a vector tree and then
run the erb method. No left. Basically, this lb will interpolate the
vector A two vector, for the vector B, we want
to use this value here, and for the vector A, it's going to be our previous
movement local. Let's just use movement local. After the vector
B, we need to pass a float as the
interpolation value. I'm going to use
value of two here and multiply this by time Delta T. Now that we have
this. When we arrive, we also want to the vector to
this vector 30 value here. Let's just create a
new vector method and then pass the movement local as vector A and vector B. We want to set this
two vector 30. Let's just set the same value
for the interpolation value to multiply by deta
Let's save this. And let's go back to unity and play this again to see if
this fix the issue or not. Now when the enemy start moving, we can see that the animations will stop very
nicely. There you go. We have fixed the movement
and now the enemy will follow the NAPM agent
movement very close. The next thing that we need
to create is we need to create an animation whenever the butt gets hit by the bullet. Let's go to the prefs folder and let's open the enemy
currently on a debug inspector. I'm going to switch back
to normal inspector, and then press open prefet. Here, if I select the robot kyle here, go to the animator. Let's go to the base layer.
We only have this plant. We need to create
two different state, and the first one should
be the head state. And the second one will
be the dead state. I'm sorry. This one should
be the hit and that. Oh I made a mistake here. Let's just rename this
default state here. This should be plan three,
and this will be the hit. For the hit animation, let's just pick the hit
reaction and for the dead, I'm going to pick the
death front headshot. Let's create a transition
from any state to the hit state and any
state to the dead state. And let's just make transition from hit back
to the bland three here. Probably we can
reorganize this better. We need to create a couple of new parameters
inside the animator. Let's just create
a trigger and this will be shot and
another trigger. Dead, and we are going
to also create a bulion, and let's just
call this is dead. So the dead trigger will
trigger the dead animation, and the bulion will be a safe or a safe check
for the hit animation. So we can only go to
the hit animation when we are shot
and the is false. Now let's go back
to the script here. And we want to set
whenever the bot is that, then we want to
trigger the animation. Let's just try to
trigger the animation, set trigger, and we should
trigger the dead animation. We also want to set the set b, and this would be the is b and the string should
be the same with our parameter name here
in the animator window. Let's just set this true. Whenever we are still alive, we need to add an
else condition, and if it's still alive, then we want to play
the hit trigger, hit, shot trigger and save this. Now let's assign the
condition transition here from the any state to hit, we want to add a shot trigger and we want to make
sure whenever is false. The value will be
false by default. For the dead, we want to set this that we want to
use the dead trigger. We need to make sure
that has exit time is disabled and for the hit
back to the bland t, we want to make sure has exit time is true and
no conditions at all. It will automatically go back to the bland t whenever the hit reaction are
finished playing. Okay. Now if we
check the script, again, I think is good. Let's just let's just test
this by running the game, and let's to shoot the
bug when it arrives. There you go. It plays
the hit reaction. Oh, we cannot check the dead because we
destroy it right away, so we can add the
delay on the destroy. Add a coma here, and
then maybe we can set the two 4 seconds delay for
now and go back to unity, and let's test this again. One, two, three, and it's dead. Yeah. After 4 seconds, it will get destroyed. Yeah, there you go.
Here in this video, we fix the movement, now it's much
better than before, and also we added
the hat reaction and the death animation.
11. 10 Basic Hit Effects: Hi, in this video, we
are going to create the particle effects
on the surface. So when we want to
create a feature, whenever we shoot on the
ground or on the wall, we want to create
a particle effect. So in order to do that, let's just create a
new C shop script. And let's call this effect. And let's open the script. And in this script, we
want to implement the able Let's just do that, and then let's just
implement interface, so it will create
the public method. We don't need the uplate
method for this script. And now we need to
create a serize field for holding the particle prefs. The particle eff prefs. Let's just call this
particle prese or eff prefs. Let's save this. We also want to create a private
particle system. And let's just call
this particle cache or FX perhaps describe
the variable better. And on start, we want
to instantiate this, and we want to make this
object as a child of this game object who
holds this script here. And for the effect prefs, it should have a
particle system. So we need to make sure that the object that are reference to this variable here should have a particle system
component attached to it. So we want to check if the
effect prefs is not null, then we want to
instantiate this. So let's just create a new game object reference,
effects, temporary, and let's just instantiate
the effect prefects, and we want to set this
game object as the parents. So let's just type transform. Then we want to set the effect cache variable to the game object particle
system components that are attached to it. Let's just type cache here, and then grab the effect tem and type get component
particle system. Okay. So now we've created this. We want to play the particle whenever this
method gets triggered. So let's just delete this, and we want to check if
the FX cache is not null. Sorry, I set the wrong
parenthesis over there. FX cache is not null, then we want to set the position of the effect
cache to the hit point. Set also the rotation, bits its normal and play
the particle effect. Let's just do that. We can grab the particle
system cache here, and then type transform
type position, and we can set
this to the point. And we can also set
the rotation by accessing the particle
system variable. The transform
rotation, and we can grab using the quaternion
look rotation, and we want to
facing the normal of the hit point or the
Cas hit variable here. So now we can save this. And then we want to
also play the particle. So we can just access
the play method. And since the Flex cache
is a particle system, then we can access the
play method directly. So let's just save this. And let's give it a try. Okay, here, if I
select the cube here, we can add the ha effects, and it ask for an ef prefab. So If we go to this effect examples folder
and under the weapon effects, under prefabs, you see that
we have a metal impact. Let's just use this. But
instead of using this pref, I'm going to duplicate this
prefab by pressing Control D, and I'm going to
modify the duplicates. That way, we will have
the original prefe And we can always duplicate the original prefabs
when we want to make a modifications to it. So let's just direct this
to the prefes folder. And I'm going to open the prefbs and we
need to modify this. Since the particles reside
on the child object here, let's just create
a modification. We can just remove all
of the mesnder here, and the filter,
also the collider, and we can duplicate the particle system by copying the component and
then paste this here. And we want to drag the sparks, dust and decal as a child of
this and just delete this. Now if we go to
the sub emitters, you see it still have
the same reference. And as you can see, it emits
to a certain spear shape. B here under the shape, we have an hemisphere, we want to use I think, instead of using a spear, we should be using cone here. Yeah. There you go. Now we have the cone
is facing that way. And we can probably change
the direction of the spawn here by setting the let's
check the sparks here. We can rotate the child object. Let's just rotate
this set this 20, and now it should be
facing the correct way. Okay, let's just select all of the child game object and
then set the y value 20. So I face the correct way. Okay. And now we want to make sure that the
particle is not looping. Let's just disable looping
and then press play. And it's looping. So We want to set the rate over time under the
emission panel to zero and set the particle emission
using birst instead. Let's just press plus
on the birds here and set this 21 should be enough. For the child, we also
want to set this out. This is already set correctly, so let's just check
the dust also. It's using burst most of it. Okay. So if we press
ph play only once. Et's stop and then press play
again. Yeah. There you go. And we want to set the simulation space world
for all of the particle here. Let's just select all
of the particle and set the simulation space world. Okay. Now if we try
to play again, Yeah. Okay. This way we make sure that the particle aren't
going to loops, and we only want to play
the particle whenever we invoke the play method
from the script here. Okay. Another thing that we need to set up is we want
to disable the play on awake options in the parent particle,
so let's just do that. So the particle doesn't
get play whenever we instantiate to the scene. Let's go back to the scene
here and save the scene. And select the first cube here, and we want to drag
the metal impact prefabs from our prefes folder to the effect prefabs here. Now let's save this.
And if we press play, let's give it a try. There you go. If we shoot on the surface, you see that we have the
particle effect instantiated. And the nice thing with this, we only have one particle, but we are basically
moving the position of the particle based on our shot or hit rac
as hit location, and we play the particle. And since the particle
space is rolled, those particles
stays on that spot, even though we click
on another area and we move the p not
the parent sorry, we move the particle
system game object. As you can see here,
we have traces from our previous particle
invoke or particle play. So now we have this setup. We can just duplicate
the component here. Sorry, I paste it by mistake. So I'm going just to undo this. And we can copy component this this hit effects component and then select all of the other e, and we can paste this as new. Okay. That is the
basic hit effects, and on the next video, we are going to create
a specific hit effects for the enemy whenever
we shut the enemy.
12. 11 Enemy Hit Effects: Hi. In this video, we
are going to continue on our virtual Cp game project. And in this video, let's create the hit effects
for the enemy. So in order to create hit
effects on the enemy, we can simply reuse the previous script
that we have created, which is the hit effects
and assign the effect. But there is an issue here. This won't work
right away because the enemy script also
implement I hitable, and the hit effects
also implement that. So if we take a look here
on the player script here, you see that whenever the player press the mouse button,
the left mouse button, and start to shoot, it will detect only
one hitable component. So we need to change this
to make it work with multiple hit or a
multiple component that implements I hitable. So let's just do
that. And first, we are going to change the
itable class here to an array, and let's just
call this itables. And then we are going to use
the get components method, the pleural case for the
get component method. Then we want to check
if the hits is not u and hables length. It's greater than zero. It means that we have at least one hitable components inside this able,
I hittable array. Then we want to run this
code inside the statement. We are going to use a
four each statement, and basically we can create a hable in able
array or collection. Let's just cut this
here and paste this inside the four
each statement. Let's just save this. And
now let's go back to unity. And I've already prepared
an effect prefs, and it's attach in the description of this
course here, take a look. Basically, we need to
import that package. It's called FX electric
unity package, and when it just imported. And here, it used
the texture from the particle eff
example from unity. So we don't need
to reimport this. We can just import the pre, electric prefs, press import. And now we have this prefs. If we double click on the prefs, you see that we have a
very nice electric effect. There you go. Let's just use this as the
effect prefs for the enemy. Let's just open the enemy prefs, and we want to change this
in the prefabs level. So let's just add the
Hat ex component again, and then select the fx
electric drag this as the effect prefs under
the at ex component. And if we go back
to the scene here, you see we have multiple
component just remove this one. Okay. There you go. We use the hit ex component that we added on
the prefabs level, and let's save the scene. And this should be applied
also to the other enemy. Yeah. And now if we press play, and then we start
shoot the enemy. I'm going to disable
the gizmo so we can see it better. There you go. We will have a very nice
electric effect. There you go. And it still works on the wall, regardless of the changes
of our player script here. Because it will grab any amount of itable or any amount
script component that implements hittable. So if there are only one, then this steel
wheel gets executed.
13. 12 Exploding Props: Hello again. And in this video, we are going to continue our
Virtual Cp game project. And in this episode, let's create an
explosive items or a props that we can
shoot and then explode and also create
damage to the enemy. So in order to do
that, let's just create the explosion
prefex first. If we go to the
effect examples and the fire explosion under
the prefes folder, we will have this big
explosion prefep. Let's just duplicate this. And then we want to go
to the prefs folder, sorry, not the prefs folder. We want to drag the
duplicate prefet into the prefet folder. Let's just do that. And now we have a duplicate
version of it. If I double click here, you see that we have a
very nice explosion. Currently, it sets to loop, so we want to disable that. Select all of the game object and the child game object here, and all of them have this particle systems
component attached to it, that's disable the
looping options. Now if I go to the most
parent game object, and if we press play, it will only play
once. There you go. And now let's create the
explosion script that handles the applying
damage upon explosion, and let's go to the
scripts folder. And I'm going to create
a new CHOb script, and I'm going to call
this explosion damage. And let's open this equipment. Now we can create a
couple of variable. The first one should be
the float damage radius. The range of the
explosion damage, at what distance are this
explosion going to damage the enemy or other
object that has implemented the
itable interface. We want to also create a
float delay until destroyed. Yeah. So this will be the delay, how long before this explosion game object
gets destroyed, and we don't need
the object method, so let's just delete this. And now we want to
create a method, a built in method called
dro Gizmos selected. And this is basically
for handling any Gizmos when this
object is selected. We want to access the Gizmos
class and access the color. And I'm going to set
the color to red. So with gizmos dot color, this will apply any
Geismo that we implement below this line
here to this color. And for example, if we implement a couple
of codes here and then we implement another
color changes to green, for example, then
this implementation will affect all of
the Geismos that we implement after
this line here. So that is how gizmos dot colors work. So let's just delete that. And then let's go deleting
all of this used line, and let's just access
the Gizmos class again, and we can draw couple our many shaped cube frustum
gutture, icon and stuff. I'm going to use
the wire sphere. And this is a method. And for the center, I'm going to use the
transform the position of the explosion damage
game object here. And for the radius, you guessed it that we are
going to use this value here. So I just duplicate this and
paste it here. And safety. And if we go back to unity here, you see that if we apply the
explosion damage script, you see that if we increase
the damage radius, we will have a radius indicator. So when we approximate the damage radius
size on the screen, we can estimate the optimum size before this explosion
affect nearby enemies. So let's just set this 23. And here if we go to
the top view here, you'll see that press play, the explosion is
approximately size of three in terms of radius. This should be enough. For the delay until destroy, we can check the duration, which is 2 seconds currently. Let's just set the delay until destroyed to
also 2 seconds, but I'm going to add 2.2. It took longer until we
destroy this game object. Let's go back to Fiscal Studio and continue on the script here. Now we want to run
the destroy method, and then let's destroy
this game object after the delay until
destroy seconds arrived. Now the next thing we need
to do is we need to create a method and this will
be a custom method. I'm going to call this
damage nearby objects. Here, when we
execute this method, we want to grab all of the collider inside a
sphere that we are casting. So let's just create a
collide class here or object, and array of collider, and let's just call
this calls collider. And we can use the
physics class access its method member and
use the overlap sphere. This also ask for a
position and also radius. Let's just pass the
transform that position, and for the radius, let's
just use the damaged radius. Now, we will get a bunch of collider stored inside
this calls array, and we need to look
through that collider. Let's just create a
four e statement. For the variable,
I'm going to call this call Stans Form collider, and for the collection,
we can just grab the calls collection
here and save this. Basically, we want
to check if the variable or the object that
we are currently iterating, we want to check
if the object have a i hable components
attached to it or not. Let's just create a able array, or we can just copy the code from the player
script. Let's just take a look. We can grab this, and then paste it
here. There you go. Basically, we need to change the hip to the call
game object here. Let's just type call. Since this is already collider, we don't need to
access the collider. Let's just delete
that. And we want to pass a ray cast inside
this if statement here. So basically, on every look
of our four each here, we want to create
a ray cast from the explosion damage
central position to the game object that has
hit als attached to it. So let's just create a new ray cast it, let's call this hit. And then we are going to
create another if statement. And this would be a
physics that Rak has, and we are going to use the overrides the
eighth override. So we want to use this
variant of the method. Basically, we want to use the origin of the explosion
and the direction, and the rac has hit
as the out parameter. So let's just type transform that position here
as the origin. And for the direction,
we can just grab the collider game object that
we are currently iterating. Grab it transform that position, and we can substract it with
the transform position. And with factor subtraction, we will get the direction from the central position to the collider game
object position. Here for the third method, we are going to output the resulting cast operation to the hit parameter or variable
that we've declared here, and now we can save this. Here inside this hables or the
second for each loop here, we can pass the hit cast as the argument for the
hit method and safety. But for the explosion damage, we want to make
sure that it kills the enemy with
only just one hit. We need to modify our
or hittable interface. We need to also pass
a integer damage, and we can set this
default to one. Okay. Basically, with
this default value, if we don't specify
damage value, then it will pass one as the
damage value by default. We can just leave the
previous implementation without changing the code here, for example, the dynamic object. We can just yes, we still need to
implement the damage. So let's just type damage, set this two equal one. Let's check the other
script. For enemy script. We also need to
modify the hit able. Yeah. There you go.
We have an error. Let's just go to the hit method and then
add an h damage equal one. Now here on the enemy script, we can just use that damage to subtract our
current health. Let's just And safetess. The other thing that we need to check
is the hat effects. Yeah. We need to modify
this also in damage one. We won't be using
this integer here, but still because we implement
that in our interface, we need to pass
those value here. And now if we go to
the explosion damage, you see that the hat method now soon will ask for
an integer damage. But since we set the
default value to one, we don't need to pass that. But for this explosion damage, we want to pass a very high
value, for example, 100. So it will kill the enemy
instantly and save this. Later, we are going to
also use this damage value to create a weapon variant where we can have a better or a greater weapon that has
a greater damage value. For the player
script, right now, we can just lit this as it is. Okay. Now we have
this implemented. We need to run this on start. Let's just run the damage near my objects method on
start and save this. The last thing
that we need to do is we need to create a spawn on shoot a script that will spaw the explosion upon
shoot or upon head. Let's just create a U subscript, and let's just call this spawn
on a let's just open this. For spawn on it script here, this is a quite simple
script actually, so we can delete the start
and the update method, and we will need to implement
the able interface, and then let's just
implement the interface. Now we are going to
create a serials field, and this will be
the game object. We can just call
this prefabs paw. Then we can create
another Serres field, and this will be A B and we
can call this destroy on it. And let's implement
the hit method here. Basically, we want to check first if the p choose
paw is not null. To prevent null
reference exception, and inside this statement, then we want to instantiate
the choose paw, and we want to set the spawn on hit game object
transform that position. And after transform
that position, we want to pass a
default rotation, which is quaternion do identity. And now we want to check
if the on hit is true, then we want to destroy
this game object. And the spawn on it
game object will be the object that
spawn the explosion, for example, or anything else. For example, a
barrel, gas barrel, or something that are
explosive or flammable. Let's just save this, and
let's go back to Unity. The next thing that
we need to do is, I've already downloaded
this assets from Unity ***. It's free. It's called
Ci Barrels 40 sample, and I will put the link
to the description here. Let's just press import. Let's just import this. But
I'm not going to import the scene here because we
won't be needing that. Okay. Let's just import this. Once it's imported,
I'm going to direct the sci fi barrel
folder sample to inside the underscore game
project game folder here. Now, let's go to the prefect here and select the one
that we want to use. Probably I'm going
to use this one. Let's just d barrel white S to the environment
group here game object. And it should be somewhere
in our scene here, it's here, and let's
just put it here. Let's just check the size of. I think we can put
it over there. Let's just hide this guide here. We won't be needing
this anymore. Let's just deactivate
it and save the scene. Now if we select the barrel
white game object here, I'm going to add
a collider first. We need a collider.
Let's just add a box collider and it fits. Then we want to add the
spawn on hit component. For the pref to spawn, let's go to the prefex
holder and drag the big explosion that
we have prepared before, and let's enable the destroy
on hit and save this. Now let's open the console
window here and press play. So let's just wait a
while until the enemy appears. There you go. It appears and also appears, and let's shoot the
explosive here. There you go. It kills
all of the enemy. So it's quite nice that we
have this and we can use this for help the player
navigate a harder area. And if you take a look here, we have an issue that the get remaining distance can only be called
on an active agent. And this is happening because
here on the run bland, we are checking if the agent remaining distance is greater than this value here, and this gets
executed on update. So after the enemy is killed, we are disabling the agent. There you go, and
this causing issue. So let's just add a condition here on top
of the if statement. And add if if the
agent is disabled. Basically, we want to check
if the agent is not enabled, then we want to
return, and save this. And let's give it a try
one more time to see if this error is still showing or it's still
happening or not. Let's press play, and
wait for the enemy. Okay. So now let's
shoot the barrel again. Okay. There you go. We
killed both of the enemy, and we don't have
that error anymore. Yeah, that is how we
create an explosive props, and you can use any other
explosion type if you want to. Just add the explosion
damage to the explosions.
14. 13 Custom Weapon: Hi. In this video, we are going to create a
custom weapon mechanism. So we can change weapon based on the
weapon that we pick up, and if the MO runs
out on that weapon, we want to revert back
to the default weapon. Okay, first thing, first, we need to create
a new CS script, and this will derive from
the scriptable object. So I'm going to create
a new subfolder inside the scripts folder and I'm going to call this
scriptable objects. Here inside the descriptable
objects folder, let's just create
a new Shap script, and let's call this weapon. Data. And let's open a script. Since this script is going
to be a scriptable object, we need to change the
mono behavior type here to a scriptable object. And scriptable object is
basically a script that we can save as an asset
in our project folder, and it can hold and also method. And we also want to move the up method here from the player script
to this weapon data. We want to move all of this shooting script or code
into the weapon data here. Another thing that
we need to do, we need to define a menu
to create a set menu here. On top, let's just
create an attribute and it ask for two arguments. First is the file
name. Both are string. The first one is file name and the second one is the menu name. For the file name, we can set this two file name
equal weapon data or we can add a
custom weapon data. Then here inside the
parenthese menu name, we want to pass weapon data. And close this with a
parents, and safety. Basically, this will
allow us to create this scriptable object using the menu inside the
project folder. Let's just delete the update and start, we won't
be needing that. We are going to create
a custom data here. First, we need to create
a custom enumerator. Let's just create a custom a here outside of our
weapon data class. Let's just type public am, and I'm going to
call this fire type. And there will be a
single and rapid. Rapid is for automatic gun
type and single is for the normal weapon or
the default weapon. Let's create the
needed variables here. First, we are going to
need the fire type. Let's just type the fire
type as the variable type, and I'm going to call this type. And the second one
will be the rate, but this should be a float, so type float and call it rate. I'm going to set this
default to 0.15. The next field will
be an integer, and this will be the max MO. And the other one would be an integer also and this
would be the damage value. We also need to create
another bleion to define whether this weapon data is the default weapon or not. Let's just create a bleion
called default weapon. And we also need to declare a couple of private variables. First, we need to have a
reference to our camera. Let's just create a
private type of camera. Let's just call
this CM. We need to also have a reference
to our players script. Let's just call this player, and also a reference
to the current O, and also we need to create
a float variable to hold our next fire time. Okay. And since we need a reference to the player
script and also the camera, we need to initialize this scriptable object
using a method, and that method will be called from the
player script here. So I'm going to create a
public void setup weapon. And I'm going to pass the
camera as the first argument, and the second one would
be the player script. I just call this player also. Okay. So inside
this method here, we can first define the KM variable here by
using this statement, this equal K, and this will be referred
to this variable here, the local variable that
we have inside our class, and this will be referred to the argument that
we are passing here. The second one
should be the player to player equal player, and we want to also set the
next fire time to be zero, so we can shoot right away
whenever the game start. Also for the current MO, we want to fill
this to the next MO that we've set up on
the inspector here. That will be all for
the weapon setup, and the other method
that we want to create is the public void fire. For fire, we don't
need to be a public, so we can set this
just to be a private. Let's just go to the
player script here, let's just copy all of this here inside the update method. I'm sorry, I'm going
to highlight all of them from the I statement
to the closing brackets, right click, and
then press copy, and then I'm going to paste
this inside the fire method. We will have errors. Good. There are no errors
because we already defined the CM here so we can
use this right away. And I'm going to
create a public void, and I'm going just to
call this weapon update. And this is the method
that we are going to call from our player
script update, replacing all of this here. Okay. Inside the weapon
update, basically, we want to check
whether the fire type is a single shot
or a rapid shot. Let's just create if statement, and if the type equal
to fire type single. Then we want to have
a separate code else. If it's a rapid one, then we should have a different kind of
shooting mechanism. The first one, we are
going to use the if input, get mouse button down, and we are going to
grab the left click. This zero value indicates
the left click, and inside here, we want
to run the fire method. We want to run this
one here. Oh, sorry. In the fire method,
we don't need the input get button
down statement anymore. So let's just delete this
and the last bracket here, and I'm going to organize this by moving back
to the indentation. And then when we are firing, then we want to substract
the current MO by one. And here, inside the
rapid fire type, we want to use an input
that get mouse button. Zero. So this one
is for holding, and it will return
strug whenever we hold the mouse button. And this is only on the first frame when the
user press the mouse button. So this only happened once, and this happens
throughout many frames, depends on how long the user
are holding the left click. And we want to add a statement where the time the
time is greater than the next fire
time. And safe test. We want to also check if the current MO is
greater than zero. We want to also check if the current MO is
greater than zero, and we want to create
an if statement, and this one if the current O
is less or equal than zero. For this here, we can just
create the else statement. Here, let's just
type the bug log and type O runs
out, please reload. We can just copy this line
here and paste it here, so it will throw out
the same message. And now here, we want to run the fire method and
also substract the current MO. Then we want to set the
next fire time to be equal our current time when
we are firing at this moment here and
then add the rate value. This is the rate value. So we
will only be able to shoot automatically at this time or if the time time is greater than
our last next fire time. There will be an
interval between shot. Although this is
a very short one, just like an automatic weapon, and we can change this rate
value in the inspector, so we can tune those
value to fit better of our weapon characteristic,
and let's save this. Now that we have this
inside the weapon update, we want to also create a Couple condition. And
this one would be if currently we are using
the default weapon, and we are pressing the button. Sorry, not to get button,
get most button down. And this one will
be the right click. So that's why I'm using
one as the argument. Then we want to refill the O. Current O equal the max MO. And this is for the
default weapon. We want to set that we can
reload for the default weapon, but for a custom weapon, we won't be able to reload this. And then here, we also want to declare if this
is not a default weapon, and the current O is
equal or less than zero, then we want to switch back
to the default weapon. But we haven't implemented this yet in the players script, so I'm going to I'm going
to type a comment here. Okay, save this, and I
think that should be okay. Let's go to the player script. I'm going to drag
the play script, to be the site of
our weapon data, so it's easier to switch. And now we can just safely delete all of this, the update. I'm going to create
a new SuriS field, and this would be a weapon data, and this would be
the default weapon. And I'm going to create
a private variable, and let's call this weapon data, and this would be
the cur weapon. Inside update, we
want to check if our curn weapon is not new, then we want to run
the cur weapon weapon. And here, let's just
create a public method, and let's just call
this switch weapon. And for switch weapon, we need to pass a weapon
data as the argument, but we want to set a
default value of null here. So we can run this
method without passing any argument and it will automatically pass a uL
data to this method here. Then we want to set the current weapon value to
whatever we are passing here. We want to check if not null, then we want to pass the weapon, and if it's null,
then we want to pass the default weapon here. So it seems that
I've mistyped this, so it should be default weapon. And I'm going to
use this name here. So basically, this is
a ternary operator. And if this statement is true, then this will be passed
to the current weapon. But if this is false, then this one will be passed
to the current weapon. And after we switch weapon, we want to run the
current weapon, set up a weapon method, and we need to pass the camera. So this would be the CM here. And the next argument is
the player script itself, so we can just pass this
statement as an argument. And here, whenever we start, we want to run the switch
weapon without argument. So we are assigning the default weapon to
the carino weapon, and that is going to be the weapon that we are going to use inside the update method. Let's save this. And
for the weapon data, I'm going to also use the switch weapon here inside
this if statement here. So basically, if it's not the default weapon and we
are running out of MO, then we want to switch back
to the default weapon. So we can just access the player variable that
we've prepared. And let's just call
the switch weapon, and we don't need to pass the argument to set this
back to the default weapon. Save this. Okay. So let's get back to unity
here and let's give it a try. It's already finished compiling. So if we go to the
main camera here, now we have a
default weapon slot inside the camera or
the player script. I'm going to create a folder. Oh, sorry, this should
be inside the game, so I'm going to
drag this should be inside the game folder. And inside data, I'm going to
create a weapon data here. As you can see, we
have this asset menu now, create asset menu. So let's click this, and this
would be the default gun. And for the default gun, we
can set this two single, and for the damage value, we can set this 21, and the maximum
MO 210, probably, and let's check this
default weapon. And for the default weapon, then we can select the main camera, and we can assign this to
the default weapon here. Okay. So now I'm going to create another weapon and this
will be a machine gun. And for the machine gun, we
will have a maximum MO of 13. Set this type two wrap it and a greater damage
value to chew, and I'm going to disable
the default weapon here. Save this. Now for testing this out. Basically, this code here
won't be working yet because we need to implement
weapon pick up later. But let's just test
this if you see that if we change
the weapon data, will the player script
works, let's just test this. I'm going to press play. Now I can only shoot
once every click. And we need to shoot three
times to kill the butt. Sorry, there is an issue
with the butt here. I'm going to check this first. I'm not sure what causing
the error before, but now it seems to
be working fine. So let's just give
it a try again. Now using the default
weapon, default gun. I need to shoot three times one, two, three, one, two, three. Let's change this
to a machine gun. I'm going to go back to the machine gun scriptable
object here. I'm going to change the
damage value to three. So we will see that
we only need to shoot once to kill the enemy, but there is one thing that we need to modify in
the script here. In the weapon that here, we have the damage
value declared. But here, whenever we hit, we don't pass a damage value, so it will default to one, as you can see in
the coat in there. So I'm going to add damage value variable as the second argument and
safety, and this should work. And we don't need to test the default gun
because it's Sorry, V. Yeah, because the default gun
has a damage value of one. So it should behave the same as if before because we have
the default value of one. And machine gun have have
damage value of three. When the enemy came out, we can see that we can
shoot once and it will kill the enemy right
away. Oh, sorry. I forgot to replace this. We are still using the
machine gun weapon data. So let's just change this to the machine gun and
save the scene again. Okay. Let's write. You
shoot these robots. There you go. One shoot, and it kills automatically. So yeah, there you go. We have a custom weapon, and we can create lots
of different weapons depending on the characteristic
of the weapon itself.
15. 14 Weapon Pickup: So let's continue on our
custom weapon mechanism. And now we are going to create the mechanism to pick up
a weapon from a bonus. So first, let's create a script, and this would be
a new CH script, and let's call this weapon pick up and this would be a very
simple script, actually. So I'm going to
open this now here, go to the iso studio. Yeah. Now it's open. I'm going
to resave the weapon data. And here, we need to create
a new serialized field, and this will be a
type of weapon data, and let's just call this weapon. And we also need a private
variable of players script, and let's just call this player. And I start, we want to grab the player script
from our scene. So let's just type player
equal fine object of type and pass the player
script as the type, and we want to remove
the update method. And basically, we want to make
sure that weapon pick up, implement the i hable interface. Let's just type the i hittable, and I'm going to implement the interface
automatically here. And let's just delete
the exception here. Okay. So basically, whenever we shoot this weapon
pick up object, we want to run the
player switch weapon, and then we want to pass
this weapon variable here. So this will set up a new
weapon on the player side, and we can use that
new custom weapon right away when we
shoot this game object. And then we want to
destroy this game object. Okay. So let's go back to Unity, and let's just
create a new cube, and I'm going to scale
this down here, 20.6. Then let's add the
weapon, pick up script. If we go to the data folder, we want to set this
to the machine gun. And let's save this. If we go to the main camera, or if we select the
main camera object, we want to set the
default weapon back to the default gun here. Let's just save this and
let's change the inspector to the debug window here so we can see our curent weapon over here. Let's rename this cube
here that we've just created as the
machine gun pick up. And let's save this as a prefab. If we go to the prefs,
I'm going to drag this machine gun pick
up as a prefs object, and let's just delete
this one from the scene, and I'm going to duplicate
this barrel object. But instead of
spawning an explosion, I'm going to spawn
the machine gun here, and I'm going to make sure
it's destroyed on hit. Let's disable this one here. We can just test this weapon
pick up prefs. Save this. Now if we press play, I'm going to try to shoot
and I can only shoot once before I shoot
the other weapon. And now it's empty. I
cannot shoot anymore. If I right click, I should
be able to to reload. I'm going to shoot this barrel, and you see it switched
to our weapon, pick up and let's click this. Now, if I click this, if
we go to the main camera, you see that our current
weapon is machine gun. Let's just try to shoot
this so many times, and I'm holding the mouse
button now, as you can see. Now when it's empty, it automatically switch
back to the default gun. Of course, we are going
to implement the UI, so it will give the
player an indication whether we are currently using the default gun or
the custom gun, and also it will show us warning or a message if we
need to reload our weapon.
16. 15 Weapon FX: Hi. In this video, we are going to create
a weapon effect. So let's get started. First, let's go to the scripts folder under the
scriptable objects folder, open the weapon data script. Now we want to create a
new zero S field variable, and this would be a
type of game object. Let's just call this muzzle ex. We want to also create
a new float variable, and this would be the ex scale. Probably, we want to set
this to 0.1 by default. Also we want to create a
private particle system. Field here, and let's just
call this cache effects. Here, inside the setup weapon, we want to add a if statement and check if the muzzle
effect is not null. Then if it's not null, we want to instantiate
this and then save the particle system
component as a cache effect. First, we need to create a new temporary
object game object, and we want to instantiate
the muzzle effect. And here, we need to create a new method inside
the player script, and let's just create
a new public void. And let's just called
the set muscle effects. Then we want to pass the
transform of the effects. And we need to also create a new transform and let's
just call this child effect. First, let's check if the
child eff is not null, then if it's not null,
we want to destroy the child game object. Then we want to set the object
that we are passing from the weapon to set its
to this game object. Then we want to
set the child eff. To this current effects that
we are currently passing. Save this. Let's go back
to the weapon data here. And then we want to
run the player script, which is the player here, and then we want to run the
set muzzle effect method. And let's pass the
tamp game object, but we need to pass the
transform component. Let's just type the transform. Then here below,
we want to cache the particle system that are attached to
this game object. Let's just type tat component, and then let's just call
the particle system. And now we need to go to
the fire method here. Whenever we fire,
we want to play the cache effect particle. In order to do that, I'm going to create a new factor three, and then I'm going to call
this muzzle position. I'm going to use the
camera object and then access the method
that it's called screen two world
point and screen two world point as for a
factor three arguments. I'm going to create a
new one and I'm going to grab the mouse
position x on the x axis. For the Y xs, let's just grab
the input most position Y. For the z xs, we want to set a very small value that are
very close to the camera. For example, 0.2 should do, and then we want to
end this line here. Another thing that we want to make sure is we want to create an if statement and then check if the cache
effects is not. Then we want to run
this code here. Let's just put the
muzzle position inside the if statement. Then let's just set
the cache effects, the transform do position. The muzzle pause that
we've just created. Then let's set the rotation
of the cache effect. Th quaternion rotation, and
let's get the ray direction. We want to use the
direction of the ray here. The last thing that
we want to do we want to play the particles. Let's just call
the cache effects play method, and save this. Now this should work. Let's get back to unity,
and let's test this out. Inside the effect example, let's go to the weapon effects, and then under prefs we
have this muscle flesh. Let's just duplicate this, and then drag this
duplicate muscle flash into our prefs folder. And now I'm going to open the muzzle flash effect
and modify this. If we go here, you see, it's actually already very
nice looking muscle flash, but we need to change
the settings a bit. Here, we want to
disable the looping, so it doesn't loops
anymore, only play once. Let's set the y rotation 20. Let's go out from the prefabs
and I'm going to rename this object prefabs
to muzzle flash only. Here inside on the data folder, we want to set the muzzle
effects to that game object. Let's just open the prefects and then drag the muzzle effects here to the scriptable object of our weapon and
for the machine gun, we can also use that for now. But later we can create eight
different muzzle effects. Now let's test this out. If I press play, we should
be able to see the. We have a issue here. As you can see here, if
I select the camera, the clipping planes are set to 0.3 on the near properties, but here, when we position
the muzzle flash here, you see, we have set this to a value that are smaller
than the clipping planes. So in order to fix this, we can just set the near
value to a very small value, type zero, and it will snap
to 0.01 and save this. Now if we press play, again, we can but we have a
very big muscle effect. So we need to apply the scale
that we've declared here. If you see here under
the serialized field, we have a fX scale fields declared, but we
never used this. So in order to use this, we can just go here
and then insert a new line and access the
scale of our time game object, and then set this value to
equal vector three dot one, and I want to multiply
this with our f X scale. And for the default
value is 0.1. So we are going to scale the particle by a tenth
of its default size. Let's go back to unity here. And if we check the default
gun and the machine gun, we have an effect scale of 0.1. So if we press play now, the muzzle effect should be
smaller. Yeah. There you go. If we want to decrease this, we can just set this two
even smaller values. And for the machine
gun, we can set this to a slightly bigger one
than the default one. So let's just type 0.07 here. Okay. Now if we shoot, you see we have a very
small muzzle flash. But if I shoot this, and then I pick up
the machine gun, you see we have a
bigger muzzle flash. Okay. There is another thing
that we need to fix here. If I press play, you see
the impact particles. The decals is facing
the wrong way. Basically, it has
a cell rotation. We should align this
particle to the wall here. Let's open the metal
impact particle here, the p, open here
if we expand this, go to the decal me object, and you see under
the renderer here, we have the render
alignment sets to view. Let's just change this to local, and let's go back. To the scene here and
let's test this out. Now if I shoot here, the decals align perfectly. There you go. Yeah, that is basically how to
create a weapon effects.
17. 16 Delay Action Extensions: In this video, we
are going to create a set of tools in an
extension script. In order to do that, let's
go to the scripts folder, and inside the scripts folder, let's create new folder
called extension. Inside this folder, let's
create new Shop script, and let's call this extension.
And let's open this. So for you who wonders
about the extension script. Maybe some of you often
see that whenever we create control or
input detection, we are using the input
class like this, and then we are running a method like get button
or get button down. And this is basically an extension to do
input related things. And we can also create
our own extension for another set of tools that are purposed
to do other things. So let's just delete this, and we are going to start
and the upput method. And we're also going to delete the mono behavior
inheritance there. And we are going to set this
class to be a static class. And now we can create a static method that are going
to be used on other class, and this script
doesn't have to be attached to an
object in the scene. So now I'm going to create
a new public static method, and I'm going to call
this delayed action. And we will be requiring a
couple of arguments inside. The first thing, we are
going to use this key word, and then the type will
be mono behavior, and I'm going to call this B, and the second one
will be an action, so we can pass a method or delegate into the
second parameter. Let's just type system action. And let's just call this
action and save this. And we also need to create a new static enumerator to
do the core routine here. So let's just call this
delayed core routine. And then we want to
pass an action again. Let's just type system that action to declare
the action class, and then let's just
call this action. And we also need a
float as a delay. And here we also need to pass a float as the third argument. And now inside the enumerator, we can just use the yield, return new weight for a second, and then pass the
delay as the value. And after that, we can
just run the action. We can check if the action
isnull or not using the question mark and
then type dot invoke. So this is basically check
if the action isnull or not, and if it's not null,
then we want to invoke the method
inside of this action. So safety, and now inside
the delayed action, we can just type the MV, which is our mono behavior, because start coroutine can only be executed from
a mono behavior class, so we need to pass
this, start co routine, and then we can pass
the coroutine name, which is the delayed coroutine
that we have below here. And then we can pass the action, which is the action,
and for the delay, we can pass the delay. Safety. And this keyword will be as an extension of
the mono behavior. So right now, if we go to the
player skip, for example, we can use that extension
by using the dis key word, and this will refer to this mono behavior right now that we have the player script, and then we can run
the delay action. As you can see here, we
now have this method, and it says it's an
extension over there. And now we can pass a method. And then after we pass the
method, we can pass the delay. So for example, if I want to run a certain method or
a certain delegate, and then I pass five for the
delay this delegate will gets executed 5 seconds after
the start are executed. So now I'm going just to
create a new delegate here. And then I'm going
to create a code. Let's just type debug that log. And then let's just
finish the code here. We can just type
delayed action runs after 5 seconds and save this. And this resides on
the players script. So now if we go back to Unity, and then we check on
the console panel, and then if we press play, you'll see that we will
have that message. In 5 seconds. There you go. Here, you see that that code inside the delegate gets
executed after 5 seconds. Now we have created this
delayed action extension, and this will be quite
useful for running a delayed method or a
delayed code if we needed, and this is how we
create that extension. Basically we can extend
any type of class, moo behavior, factor three, or any other object.
18. 17 Game Manager States: In this video, we are going to start creating our game manager, and we're also going to start to implement
the game states. So each state, we can
run a separate codes, like when we are
starting the level, we can set the ready for
probably around 3 seconds and we can show a
text animations and then start the gameplay. And also at the end, we want to switch state to level so we can start
calculate the score. So in order to do that, let's go to the scripts folder, and let's create a
new C sharp script, and let's call
this game Manager. You'll notice that once we
created the game manager, it has a different icons, and this is the default
icon that Unity has provided for any script that are using the
name Game Manager. Now we can open this
in fisial studio, and for the game manager, first, we want to declare a public
static game manager, and let's call this instance. This will be a property, so I'm going to set a public getter. But for the setter, I'm going
to set a private setter. And this way, this property can only be set from
this script here, but it can be or it can be accessed
from any other script. And the static reference, make sure that this variable only have one instance
across in the project. So it's shared.
And now we want to create a new void weight method, and let's initialize
the instance variable or the properties and
pass this script itself. Now outside of this class, let's just create a new public en and this will
be the game state. And for the state,
I'm going to set a default state and
for the start state. And the gameplay state, and the last one should
be the level and state. Now that we have
declared the game state, we can use this in our
game manager class. So let's just create
a new sealized field, and for a type, this will be a game state, the um that we've declared here, and for the object name or the variable name, let's
just call this state. And we will also
need a reference to the player movement script. Let's just create a
new serialized field. And I'm going to type
player move for a type, and let's just call
this player move. And now we want to delete
the update method, since we won't be
using it right now, and let's just create
a new public void, and this will be a
switch state method. And this method we'll handle the switching state
of our gameplay. So for the argument, let's just type game state, and let's call this new state for the new state
that we are passing. And we want to check if
our current state is equal the the new state that we are passing when we are
running this method, we want to return this method. So don't execute any
code below here. And if it's different, then we want to set the state to the new state
that we are passing. And here we want to
use the switch case, and we want to check the
current state value. So here, let's just
open a bracket, and then if it's case
game state start, then we want to
run a certain code here and then add break. And then we can just duplicate this blocks of code and
then paste it two times. And let's just change
the state to gameplay, this one, and the last
one should be level. So now we have a
switch statement that handles three state
of our gameplay. And right now we haven't set up any UI or text for
the start state. So let's just run a debug log, and let's just type game Start. So we can see it in the console. And then here, we can use the delayed action that we've implemented
on the last video, and then we can just pass a
new delegate. As the action. And for the delegate, we want to run the
switch state method. And then for the state, we want to change
this to gameplay. And here as the second argument, we can just pass the duration. So we want to execute this
code here after 3 seconds. And now we can just run a the back log here
inside our gameplay, and let's just type
state gameplay. And we can add time here. Time time. You see the time stems when this
code gets executed. So now on start, we want to also make sure that the player move
script is disabled. Oh, I've typed this wrong. It should be player move. So we want to make sure
that the player move is currently disabled
on this state. So let's just set the
enabled value to falls. And we can just copy
this line here, and up on starting the gameplay, we want to enable the script. Now we have this implemented. We are going to
expand this later. But right now on
the start method, let's just run the switch state and set this to gameplay start. We won't be using
the default value, but we want to make sure
that when we are running start the first time that
this level initiated, the state and the new
state will be different. It will default to
the default state, and then when we execute the switch state start
in the start method, I will switch Judi state here. So now let's save this. And since we created a static instance reference
to this game manager, we can access this script from anywhere from any other script. And since this is
also a public method, we can access this method
from any other script. So later when we are
finished the game, we can trigger this level
and state from other script. Now let's get back to Unity. Once it's compiled,
I'm going to create a new anti game object and
reset the transform value, and I'm going to call
this game manager, and then add the script, and I'm going to assign
the player move. Direct the main camera to
this player move and it will register the player move script that's attached to
the main camera here. I'm going to put the game
manager on top here so we can access it easily
and save the scene. Now if we open the console here, I'm
going to clear this out, and if we press play, you'll see that the
game will start, and after 3 seconds, it runs. Here, when we start logging
the gameplay state here, it prints out the time
statem which is 3 seconds. So after 3 seconds, we can start the gameplay. In those 3 seconds later, we can create a UI animation like a ready text
or something else. Later when we finish the game, we can switch to
the level and state and start trigger the
score calculations. So that is for creating
the game state, and later, we are going to expand this
game manager by adding a score calculation and life
or health for the player.
19. 18 Game Manager Score and Lives: In this video, we are going
to continue our game manager, and in this part, we are going to create
the lives and the score. So let's go to the sal studio, and I've opened the
game manager here. So now we need to declare a
couple of private variable. So I'm going to create
a new private integer, and the first one
would be the enemy hit and then shot fire. And we also need a private float for our current health,
for the player health. And here, both, we need to
create a new serialized field, and this would be
a type of integer, and let's call
this player health and sets its default
value to ten safety. Below start, let's create
a new method called in it. Here, we want to initialize
the current health to the player health that we've declared on the Ses field here. The next thing that we need
to do is we need to create a new public void method. Let's call this shot hit, and we want to pass a bulion, and let's call this bulion hit. And if hit is true, then we want to add
the enemy hit value. And we want to also add
the shots fired value, whether we miss or
we hit the enemy. And we are going to use this both variable to calculate
the accuracy of our shots. The next method that
we need to create is the public void player hit, and this is for the player to receive
damage from the enemy. We want to pass a float damage. And upon executing this method, we want to subse the current
health by the damage value. And make sure all
of the methods are using the public scope modifier, so it can be accessible from other script. So
let's save this. And now let's go to
the weapon data, and inside the weapon data, we have the fire method. And here we want to check
if we hit the enemy or not. So inside the four H, we can check if Our
current hit object is an instance of
an enemy script. So we can check by using the keyword and then type
the type of the script. And here we can access
the game manager instance and run the shot hit method and pass through
as the argument. And make sure we return
the method here. So it will skip the
other hitables. Because if we found an enemy
script in the hitable, then it means it
must be the enemy. Here we can add an L
statement because if we hit other instances of hitable that isn't
the enemy script, then we want to pass false as the argument of the
shot hit method here. And we want to make sure that we return the
method when we hit something. So under this statement, the physics has brackets. We want to return the method. And here, if we
don't hit anything, we also want to run the shot
hit with false argument. So let's just copy this line
here and then paste it here. So basically, what
we are doing here, we are checking whether the hitable is an instance of
an enemy script or not. And if it's true, then we are passing the true
argument as the shot hit. And we return the
code because we don't want to check for
further hitable component. And if we don't found
an enemy script at all, then we are running
this code here. And here, if we miss It
doesn't hit anything, then we still want
to count the shot hit by passing the
false argument. So let's save this, and let's head back to Unity. And here, we can select
the game manager. Under the Inspector tab, we want to make sure that
we right click on it, and then select the bug. So we can see the private
variable in our inspector. So now let's press play
and test this out. And now you see if
I shoot on the air, we add the shots fire. And if I shoot on the wall, we also add the shots fire, but the enemy hit stays zero. And now if we hit an enemy, you see that we add the enemy hit and also the shots fire. And let's just try to
kill the enemy here. And you see that we have
six enemy hit and eight shots fire because we we have tried shooting on the scene because we have tried to shoot before
we shoot the enemy, and we are shooting in the scene without hitting the enemy. So we have two additional shots fired compared to the enemy hit, and we can use this value to calculate the accuracy later. There's one thing
that we need to do in the game manager script. Let's go to the Ficial studio. Under the game
manager. On the start, we want to run the init method. Let's just type in it. And we want to make sure
that the current health are sets to the player
health value on start. Now if we try again, check on our current
health, it should be ten. There you go. But we haven't
implemented this yet, so we are going to implement
this in the next video.
20. 19 Weapon Pickup Visual: In this video, we are
going to continue to create a visualization
for our weapon pick up. So in order to do that, first, we need to go to the asset store and download any weapon model. I've picked this one,
the Sipi gun light, and I'll put the link
in the description. So I've already downloaded
and import this asset, and now it's under
the Sipi gun light. So I'm going to drag this folder and put it
inside our game folder. And just to make
things even tidier, I'm going to create
a new subfolder inside the game folder
and call this models. And let's just put all of the models inside this subfolder that
we have just created. For the Robot Kyl, I'm
going to move that also and also for
the Sipi gun light. Now we have put
all of the models inside our model subfolder. Let's go to the game
view here, and first, we need to modify the prefs object for our weapon pick up. Here we have the
machine gun pick up prefs under the prefs folder. Let's just open the prefs. And if you go to the scene here, you see we are in a prefs mode. And now we can drag the prefe of our sci fi gun light that
we have just imported. So I'm going to go
to the prefs folder, and inside the gun light folder, the gun have five
variations of color, so we can pick one of them. I think I'm going
to pick this one or maybe this lighter one and set this as a child of
our machine gun pick up game object prefps. And now it's on the scene, but the position
is not zero out, so it's somewhere over here. In order to center the gun according to its
parent position, let's just zero
out the transform, so we can just click the **** wheel settings here
and then set reset position. And let's go to the parent
game object and just delete the mesh filter component and also the mesh render. And I'm going to increase
the size of our weapon pick up scale by chew on every axis. And then let's just position the child object around the
center of its parent object. So I think yeah, this
should be enough. And if you having a hard time to reposition this because
of the pro grid, just hold the negative
sign on your keyboard, and you can change by pressing the negative and the plus sign near the back space keyboard. You can change the size
of the increment or the snaps value of the grid. So now I've repositioned
my gun here. I'm going to go back
to our scene here. And now we need to
modify the script. Of our weapon pick up and
also the spawn on hit script. Let's just go to
the script folder, and I'm going to select the weapon pick up script
and open it in fis Studio. Now we need to create a new serializable
field type of float, so let's just create that, and I'm going to call
this rotate speed. Let's just set this default 290, and this is in degree. And since we are
going to multiply this value by time Delta time, it's going to be per seconds. So now let's just create
a FOID update method. And inside the update, I'm going to access the transform component and then access the rotate method. And I'm going to use the
second overload of the method, which is a for an
axis and an angle. For the axis, I'm going to
type Vector three dot up, so it's the y axis, and for the angle, we're just going to feed the rotate speed, but we are going to multiply
this by Delta time. So the speed of the rotation
will be consistent, which is 90 degrees per second. So let's just save this. And the other thing
that we need to modify is the spawn on hit script, let me just show you
now if we press play, and if I shoot on
this weapon pick up, you see that our gun is somehow
submerged to the ground. This is because the gun is being instantiated on
the zero position of our parent game
object in this one here. So it gets instantiated
at this point. In order to fix this, let's just open the spawn on hit script, and I'm going to add a
new serialized field. And for the type,
it will be a float, and I'm going to
call this y offset. So this is the offset
on the y position. And I'm going to set
this two deft 0.5. And when instantiating
the prefab spawn, we want to offset the y position here by adding with
the factor three, and this is basically
going to offset the transform position
by one on the y axis, and then we want to multiply
this value by the y offset. So Later we can adjust the height depending on
our y offsets value. Let's just save this, and
let's go back to unity. Now if we see the script, it's updated and
have a new field, which is a y offset,
and we can change this value according
to our needs, depending on the size of the object or on the
position of the object. I've test this out and 0.5 seems to be a good
value in this case. I'm going to leave
this as it is, and let's save our scene. Now if we press play, If I shoot the barrel, you see that our weapon is
now on the correct position, and we can pick this
up by shooting it, and now we have the
machine gun activated. So yeah, that is how we change the weapon pick up
model visualization, and with this, we can create as many weapon pick
up as possible.
21. 20 Enemy Attack System: In this video, we are going to continue implement
the enemy script, and this time we
are going to create an ability for the enemy
to shoot at the player. In order to do that, let's
open the enemy script, and here we need to implement
a new struck to hold the value of the
enemy shoot interval. Let's go here below and outside of the
enemy script class. We are going to create
a new public struck, and let's just call
this interval. Range. And let's make
this serializable by adding an attribute
system do serializable. Now let's create a
new serialized field, so it's available
on the inspector, and this will be
a type of float, and let's just call this
minimum and maximum. Then we want to create a public constructor
for the struck. Let's type public, and then the struck name
interval range. And then we open with a sets of parenthese and we need to pass the minimum and
the maximum value here. And this is basically
not this variable here. It's a argument for
the constructor, and let's create an
opening bracket. And here we want to
set this minimum value by using the keyword this
dot mean equal to minimum, and then this max equal
to maximum and safe. Now we have the constructor. The last thing that
we want to create is a public getter type of float, and let's just call
this get value. Here we can use a getter. Since this is going
to be only one line, we can use a direct or
a Lamda expression. Then just type return
random random range, and we can pass the
minimum and the maximum. Oh, sorry, we don't need
the written keyword here, just type random dot range. And the range will be the
minimum and the maximum. This will return this
value here whenever we call this properties.
Let's save this. Now inside our enemy
scripts folder, we are going to create a new serialized field of that struct that
we have just created. Let's just type interval range, and let's just call
this interval, and we want to set a new default value using
the constructor with the default value of probably
1.5 seconds, 2.7 seconds. This would set the default value of our interval variables, but we can change this
in the inspector. The next variable
that we want to create is a float variable, and this is going to
be the shoot accuracy. And let's just set this
to 0.5 by default. So the accuracy will be
50% at this rate here. And now let's create a new header to organize
the inspector better, and let's just call this
shooting properties. And now we have declared
the shooting properties. Let's start to create the co routine to handle the shooting. Here below the public
void it method. I'm going to create
a new core routine, and as with coroutine, we need to return the
type of IE numerator. Let's just call this shoot, and we don't need any
arguments or parameters in it. Now inside the shoot coroutine, we need to delay
our core routine until the player arrive
to its position. We can use the yield return
and using the new Q word, we will have this wait until. Basically, this wait
until method suspend the coroutine execution until the supply delegate
evaluates Q true. And basically it asks for a delegates that return
a type of boolean. In order to use this, we can just use a
Landa expression, and it's quite easy actually
using Lamda expression, we need to supply a
sets of parenthese, and this would be the
signature or the argument. Since it asks for a delegates that return a type of bull
without any argument, we can just insert a sets of parenthese and then an
equal sign with a aro sign. After the rosign, we
need to put a set of brackets to define
the condition. Now here we can
create the condition for this weight
until to evaluate. Basically, we want to check
whether the enemy already arrived to its position or not. We can just grab here using
the agent remaining distance, and let's just copy
this line here, and then paste it here. But instead of checking whether the remaining
descent is greater, we can just check
if it's smaller. And we can increase the
size here to increase the threshold and then add a semicolon at the end as usual. And now you see we
still have an error because we need to use
the return keyword. So we need to return the condition or the comparison
of this statement here, whether it's true or false. And then if it's true, then we are going to execute
the code further below here. And if it's false,
then it will wait until this statement is true. Now, after the enemy arrive, we want to check if
the enemy is not dead. We can just check
is that is false. This exclamation mark, heck
if this bulion is false. If it's false, then the code inside the bracket
will gets executed. And if the enemy is dead
or if that is true, then the code will get skipped
or will not be executed, and the coroutine will stop. And now here inside
the wil blocks, we want to generate a certain
random value, random range, and we want to check 0-1, it's smaller than
our shoot accuracy, then we want to
damage the player. And we already declare how
to damage the player inside our game manager by accessing
the player hit method. So let's just use that
type game manager and grab the instance or the
static reference and then run the player hit method, and we can subtract this by one. Also, we can run a
debug log here and then add a message that
player has been hit. And after we shoot here, we want to delay the coroutine using the interval range value. Let's just type return, new weight for seconds, and it asks for a float value. And this will delay
our co routine using the float value that
we pass here in seconds. And we can grab our interval
variable or object here. By typing the interval name, and we can use the
get value property, and the property will return a random value between our
minimum and maximum value. So now let's just save this. And the next thing
that we need to do, we need to run this
coroutine on initialization. So if we check here inside
of the init method, we can run the co routine after we set the destination
of our agent. And make sure the coroutine run after this line
here. Why is that? Because here because here we are waiting for this
value to become true. And if we have set
a destination, then this will be
true right away, and this will get
executed right away. So now let's just
start the Curtin by passing the Curtin name, shoot and save this. And now if we head
back to Unity, we can check on the console. And also we can select
the game manager and change the Inspector chew
the bug by right clicking, and let's check our
current health value. If we press play, and we can
wait until the game start, and after the enemy arrived. We can check, there you
go. We have been hit. We have been hit right
away, and as you can see, our current health is now
decreasing as we get hit. But there is an issue here because the shoot
cotin runs right away. I found the issue here
inside the shoot routine. We need to delay
this code a bit. In order to fix this, we
can just add a new yield, return new weight for second, and then pass a
very small value. In this case, I'm going
to pass 0.2 second. Why do we need to delay this? We need to make sure
that the engine set destination has been run and it's already
been initialized. Otherwise, if the co routine happens
on the same frame with this code execution here, and this will return
true straightaway. We need to delay that. And
once we have a destination, the agent remaining
distance will change to a quite large value depending on the position of the enemy to the
target position. By delaying this, if
we head back to unity, you'll see that if
I press play now, Once the game started, we can see the bug log here, and it will tells us
if the enemy has been spun and the first one already been spawn and the
second one has been spawn, and it will start
shooting when it stop standing or arrive
to the destination. Now it already shoot the player two times three
times and you see that our current health is currently six and decreasing further. So we have the enemy
attack working, and later we need to
create a visual to give feedback to the player that
he or she has been hit.
22. 21 Enemy Attack Visual: In this video, we are
going to continue to work on the enemy
attack mechanism, and this time we are going to work on the visual aspect of it. So in order to do that, let's go to the models folder
and to the Sipi gun light. And under the prefe we have
a couple of variant here. I'm going to duplicate
the red one, and I'm going to direct
this to the pref folder. And now I'm going to
rename this to enemy gain. After I've renamed this, I'm
going to open this prefect and start to work on
the shooting visuals. And with the shooting visuals, we are going to use
a particle system. Now let's just
create a child ob, an empty child object, and then for the child object, let's just call this shot X. Then let's add a
particle system. The first thing that we want
to do is we want to expand the emission tab and then make sure that the rate over
time value sets 20, and we want to use
the burst emission instead of rate over time. Let's just press plus here
and then set the count 21. This will only emits
one particle at a time. Let's set the duration one here, so we can see every one seconds. Let's move the position of the emitter around this position here to the barrel in front
of the barrel position. And we want to make sure
that the z position is facing forward. Next thing that we need to
do is we need to expand the shape tab here
and set the angle 20, and also the radius zero. So we will have a
forward particles. Now, following the z axis here. Now let's go to the
render tab here, expand this and change
the render mode to mesh, and we can use the
default mesh from unity, the capsule one.
Let's just use this. For the render alignment, let's change this to local. The next thing that we need
to do is we need to go to the main tab here and then enable the three
d start rotation and set the x value
to 90 degrees. Now the bullet aligns
perfectly with the gun and also the
particle direction. Right now it's too big,
so we need to change the start size to
a very small value and 0.07 so be a
good starting size. And now we have a very nice
particle bullet effects. I think I'm going to make
this even smaller 20.05 k. And now we need to enable also the trail
of this particle here. Let's just scroll down, and then under the trails tab enable this and expand this. Now we have a trail, but the size is about the same
of our capsule diameter, so we cannot see the capsule anymore because it's
overlap with each other. So let's just change the size by modifying the width
over trail here. I'm going to set this two curve by clicking the drop down on the right side, and with the curve here, we can modify the n trail
size to a very small one. Now you see that we
have a very n and we can also decrease the
start size a bit. As you can see, we have a
very nice trail shape here. And now we need to create a
material for this particle. Let's just go to the
material folders, and I'm going to
create a new material, and this will be
the enemy bullet. And the second one
would be the trail. Let's just create a new
material again and then call this enemy bullet trail. For the enemy bullet,
I'm going to change the material to
particles standard let, and I'm going to
enable emission. For the LBD, I'm going to
change this to a color of red. For the emission, I'm
going to set this to an orange color and set
the intensities to 0.5. With the particle shot
effects, game object selected, let's just direct
the enemy bullet to the material over here. And I should change the bullet
material, as you can see. For the trail, we are
going to set up this as a t material and set this color to the
bullet orange color. We can just use the
color picker here and then pick the orange
color over here. Let's set this enemy bullet
trail as the trail material. If we go below in the particle
system component here, we can direct the
enemy bullet trail to the trail material,
and now we have this. Okay. Now we need to set the start speed
to a higher value, probably ten, so it's faster, and we want to also
disable the looping and also deplay on a
wake checkbox here. So it should play only once, and we are going to trigger
this particle de script. So let's go back
to the scene here and let's open the
enemy prefects. Now let's expand the bone root here and further
to the shoulder, the upper arm forearm
and the wrist. Once we've seen the
right wrist join here, let's just drat the enemy gun as the child of the
right wrist join. And let's just reset the
transform of the enemy gun here. And let's rotate this, it's aligned with
the hand bones here. Let's just rotate this.
By holding control, we can snap the rotation
every five degree. I'm going to rotate also
on the z axis here. Now we can move this, but
since the progrid is enabled, let's just disable this first, so we can move this
more accurate. And if we want to check if
the gun is aligned correctly, while the post is aiming, let's just select the
Robot Kyle game object. Go through the
animation window here. And if you don't see
the animation window, let's just go
through the window. Under animation, just open
the animation panel here. I'm going to this, this, and then lock the animation
panel window here. Whenever we select
other game object, the robot will stay
in the aiming post. Now let's just adjust the
rotation of the weapon here. And then let's just rotate the
z axis also a bit and move this slightly upward and rotate
this on the x axis here. Okay. So yeah, this
should be enough, and now let's go back
to the scene here. Now we are going to work
on the enemy script. So let's go to the project tab here and then open
the enemy script. And now we have the
enemy script open. We need to create a
new suized field. And this will be a type
of particle system. And let's just call
this shot effects. This will be the reference
to the particle system of the enemy gun. Save this. And here below inside
the shoot coroutine, we want to adjust the
shot effects direction according to the
hit status or not. So here we want to set
the shot effects that transform the transform
the rotation, and we are going to pass the quaternion look rotation based on the enemy
transform forward, and then we are going to add a random value using
the inside unit sphere. This will add deviation. Here inside the I statement, we want to override
the rotation again. And this time, we need
to make sure that the particle direction
hits the camera. Let's just use the quaternion
look rotation again. This time we are going to
grab the player reference, and since we've
already done that, so we can just type player, grab its position and
subtract this with the shot effects,
transform that position. And this way, the
forward of our particle will line perfectly to
the camera position, and it will give
an illusion that the bullet is
hitting the player. Save this. And outside
the if statement, we want to access the shot ex particle
system again and then execute the play method. And this will trigger the
particle once and save this. Let's go back to unity here, and let's go to the prefe holder and
open the enemy prefs. And here, under
the enemy script, we should have a new
shot effX field, and let's just open
the enemy gun, expand the enemy gun here, and then drag the
shot game object as the particle system
of this shot effects. So let's go back
to our scene here, and then let's test this out. Press play, and see if the enemy now have a visual
representation of the attack. There you go. We have a fisial
representation of our attack, but we need to modify the
particle. Let's just do that. Now let's go to the project tab. Under prefs. Let's
open the enemy prefs. Here, select the
Robot Kyle and we want to play the aiming idol. And let's just lock the
animation window again. And here under the shot effects, we want to select
this shot effects, and we want to shorten the
lifetime of our trail. Here, let's scroll down under
the trail tab, open it. For the lifetime, let's
just set this 20.1, or even shorter 0.08. The other thing that we
need to change here under the main panel or
the main tab here, for the simulation space, we need to set this two world. And let's just apply
the enemy gun game o, press apply all, so the settings
get safe to the prefeb. Now if we press play, the particle is much
shorter the trail. And if we are currently
moving around, the particle will stay
on the world space. Doesn't get rotate with
the parent game object. Now let's just head
back to the scene here and test this
again. Press play. And you can wait until
the enemy arrive. And now you can see it
shoots and it hits us. Currently. If we expanded debug, you see that our health
is very low now. So, there you go with the
enemy attack visualization.
23. 22 Player Damage FX: In this video, we
are going to create an effect when the player
are being shot by the enemy. So in order to do that, first, we need to
import a new asset, and this asset is a Keno glitch created
by K Jio Takahashi. It's an image effect, and I'll put the link
in the description. So in order to don't note this, we can go to commit here and then select the Keno
glitch unity package. Once we on the Kino Glitch
Unity package page, just download the file here, and it will download
the package. I've already downloaded. And now let's import
that package. Back in Unity, let's go to the assets menu import package, click custom package,
and I'm going to import the Kino glitch Unity package,
and then press import. Now, after importing
the kino glitch, let's go to the
main camera here, and then add the digital
glitch component. Now we are going to work
on the player script. Let's go to the scripts folder and then open the player script. Here I have the
player script open, and first, we want to create
a camera shake effect. So in order to do that, I'm going to create a
new cotin here below. And I'm going to call
this do camera shake. I'm going to add a
couple of arguments. The first one would
be a type of float, and this will be a timer. The second one would
be also a float, and this would be the amplitude, and the third one also a float, but this would be the frequency. Inside the c routine, we want to create a new vector t to store the initial
position of our camera. So let's just type in it pause, and then let's grab the
transform that position, and we want to also create
a new vector t variable, and this will be
the new position. But for starting,
we are going to also use the transform
that position. And below here,
we want to create a new float called duration, and we want to pass the timer as the value of our
duration variable. Below here, I'm going to
delay the code here by a very small value,
a 0.2 second. And then I'm going to
create the loop here. So we are going to use the
wild statement and then pass the duration value
if greater than zero. So while the duration
is greater than zero, we want to run the
shake method here. And here, first, I'm
going to subtract the duration by the time
the Delta time value. And then I'm going to
execute the yield return No, to look this while statement. And with substriting
the duration with a time that Delta time, we are making the
duration as a timer. So now, the first thing
that we want to do, we want to check the
distance between the new position factor
with the current position. If The distance is very small, or the camera position is very
near to the new position, then we want to calculate
a new random position. So I'm going to set the new position to the
init position again. But this time I'm going to modify the x position by adding a random range from negative
one to positive one, and I'm going to multiply
this by the amplitude. And for the y position, we are going to do the same. I'm going to paste this
code here and then change the component to the y value and then use the same random range multiplied
by the amplitude value. So basically, we want
to check if the camera is a or almost arrive
to the new position, then we want to set a new random position on
the x and the y axis. And then we want to set the position of our camera
to the new position. But we are going to use a factor three dot b to interpolate
our previous position, which is our current position
to the new position. And for the interpolation value, we are going to use
the frequency argument that we are passing
on the coroutine, and then we're going to multiply
this with time dot delta T. So the frequency will
be relative to time, not to frame per second. And after this wild statement, we want to set the
transform that position back to the
innate position. And now we need a public method to execute this core routine. So let's just create a new
public void shake camera. And we need the same
argument as our co routine. So let's just copy
all of the argument here and then paste it inside
the shake camera method. Inside the shake camera method, we want to start
the co routine and then pass the co routine name, then we pass the argument, the timer, amplitude,
and then the frequency. Okay. So now let's go
to the game manager, and whenever the player hit, we need to run this
shake camera method. So now we need a reference to the player script from
our game manager. So let's just create a new serialize field and
then type player script. And then let's just call
this player script. And here, we can just run the player script method called shake camera that
we have just created, and then pass the time, the amplitude, and
the frequency value. For the timer, I'm going to
set this two half second, and for the amplitude, I'm going to set this 20.2, units, and for the frequency, I'm going to set this 25. You may change this value to adjust the shake to your liking, but this is going to
be my value here. And now let's go back to unity. First, in the game manager, we need to assign the
player script reference. So let's just drag
the main camera to this player script slot
here on the game manager, and then sake the scene,
and then press play. So after the game runs, I'm going to wait until
the enemy shows and then wait for us to
get shot by the enemy. There you go. We
have a shake effect. And now let's open
the enemy's script, and let's fix the
shot F X direction when the enemy miss shooting us. So here inside the while is not that statement.
We have this line here. And what we want to do
is we want to multiply this random inside unit sphere
with a very small value. So we have a very
small deviation. Otherwise, we will have a
deviation maximum of one unit, which is quite a lot if you see. So let's just
multiply this by 0.1. Okay. And the next thing
that we want to do is, we want to implement the
digital glitch effect that we've just import in
the beginning of the video. So in order to do that,
go to the player script, and then on top of
the player script, we are going to use
the Keno name space. And then here, let's just create a new private digital glitch, and then let's just
call this glitch ex. On start, we want to
initialize this glitch eff. Let's just use the get component and pass the type
digital glitch. And now we can use this inside our camera shake coal routine. Here inside the while duration, we can just access the glitch effect objects and then
modify the intensity. But before we modify
the intensity here, we need to create a curve for modifying the intensity
of our glitch effects. Let's just create a new s field, and this will be a type
of animation curve. And let's just call
this glitch curve. And basically, with this curve, we can set a value
in a curve manner. Then we can grab those
value from our curve evaluated by the time or
the duration of the shape. So here inside the coroutine, let's just grab the
glitch curve and then run the evaluate method
and then pass the time here. For the time, I'm going
to use the duration, but I'm going to
normalize the duration, so I'm going to divide
this by the timer value. The timer is constant
in this case, and the duration that we are modifying is the
duration variable. Let's save this, and
let's head back to unity. Now we're back in the unity. We need to set the glitch curve
to a something like this, so it will start zero,
and then over time, the glitch will raise up to one and then decrease again 20. And we can test this out, run the scene, and let's
see the glitch effect. There you go. We have the
glitch effect working, and if it's too strong, we can always adjust
the curve to push down the middle position to some value that
less than one, probably 0.7 here, and let's just adjust
the basi tangent here. So it line, and let's
test this again. Now you will see that whenever we are being shot by the enemy, the digital glitch
intensity raise to 0.7. There you go. Now it's working, but we need to make sure that the digital glitch intensity B zero whenever we finish
shaking the camera. So let's go back to
the visual studio, the player script here. We want to set the glitch
effects intensity here, zero. So that will be all for creating the damage
effect for our player.
24. 23 Hostage Setup: In this video, we
are going to create a hostage or civilian
aspect of the game. So we want to create a new script to handle
the hostage movement, and we want to also
make sure that if we shoot the
hostage or civilian, our lives get substrate. And the first thing
that we need to do is we need to open the
animations folder, and we need to import
the animations file. And I've already prepared
the FBX for this, and I also include the needed
files in the video lessons. So let's just direct
the hostage hands in the air FBX to the
animations folder. And then let's go to
the Models folder, Robot Kyle texture, and I'm going to import the robot color hostage.
So let's just direct this. And now let's start
implementing the hostage. Now let's go to
the prefs folder, and then for the enemy,
select the enemy, and then press Control
D to duplicate, and let's just rename
this hostage or civilian. And now let's open this
Pref and let's modify this. So we won't be needing the gun. We can just remove
the enemy gun. And then we want to create a new materials
for the hostage. So let's go to the models folder under the
Robot kyle folder. There is a materials folder. Let's just duplicate
the robot color, and let's just call this
robot color hostage. And let's change the base
color or the albedo, to the robot hostage
one. It's the green one. Let's just select this one, and it will create a new material. And then let's drag this color hostage
to the robot model. Now we will have a robot
with different color. So it's clear that
this is the hostage. The next thing we need to do, we need to prepare
the hostage script. Let's go to the script folder, and then let's create
a new C sharp script, and let's just call
this hostage script. Before we modify the script, let's go to the
animations folder, and in the controller folder. I'm going to duplicate the
base enemy controller, and let's just call this
hostage Controller. And now we want to modify this. So let's just double click
on the hostage Controller. And now we need to
create a new layer, and let's just call
this upper body layer. We want to also
create a avatar mask. Let's just right click
on the project, create, and then go to the avatar mask, and let's just call
this upper body mask. Now with the upper
body mask selected, expand the humanoid here, and we want to
disable the ground, the leg, the leg and the body. We want to leave the hand at
the IK and the head enabled. Now with the upper body mass, we can go to the cockweel
of the layer settings here. We want to set the
width to one and then choose the layer mass as the mass of our upper
body layer here. Let's just press the circle here and then choose
upper body mask. And now the next thing that
we need to do is we need to set the hostage hands
in the air FBX files. Rick type Q humanoid. So let's just set the animation type che
humanoid, and then press a pie. And now, if we already
set this humanoid, and then we unselect this
and then reselect it, go to the animation tab. You'll see that we
have this here. And the animations is
only 50 frames long. So we need to change
the n value to 50, and let's just call the clips or rename
the clips to hostage. And we want to also make sure that the loop
time is enabled. So now we press apply. After we prepared the
hostage hands in the air, we want to create
a new et state, and then for the state, let's just call
this hostage pose. And let's expand the hostage
hands in the air and direct the hostage clip to this motion slot on the
hostage post state. Okay. Once we've set this up, we want to go back to our scene here under
the prefs mode, select the root kyle, and then change
the controller to the hostage controller one. So now we've set up the robot, let's start do the script. Now let's create
the hostage script. So we already created the file. Let's open this
in visual studio, and we want to make sure
that the hostage script derived or inherited
from the enemy script. Let's just rename the mono
behavior here to enemy script. And now if we save this, Then we delete the void
start and the update method. If we go back to unity here, you'll see that if I go to the prefax folder and the
hostage pref, open it, I can delete the enemy script, and then I can add
the hostage script. And the hostage script will
have the same properties as the enemy because this hostage script inherits
from the enemy. Later we are going to
hide some of the options that we are not going to
use for the hostage script. But now we need to modify a couple of things
on the enemy script. Let's just open the enemy script here and modify those things. Top here, we want to make
sure that the NMS agent, the scope access modifier
are set to protected. So let's just type protected. This way we can use this
agent on the child script. Otherwise, if it's private, then the agent won't
be available on the child script
or a child class. And here, in the enemy script, We want to exclude the start cotin shot from the
hostage script. We don't want to execute this cotin in the
hostage script. In order to do that,
we need to create a new protected method, and this should be a virtual
method and then void, let's just call this
behavior set up. With this behavior set up, we want to cut this line
here and then paste it here. Now we can call
this method inside this if the agent is
not now statement. Let's just call this
behavior set up. The other thing that
we need to do is here, under the hit, we want to change this for the
hostage script. In order to do that, we
need to extract this also out into a virtual method. Let's just type another
protected virtual void, and let's just call
this that behavior. And then here, we want to cut this and then paste it here. And then we want to run this method inside this
if statement here. So let's just type
that behavior. And now the next thing
that we want to do is in the hostage script, we can overwrite those two
methods that we've created. So in order to do that,
let's just type overwrite. And then we can choose the method that we
want to overwrite. We have created the behavior
setup and the dead behavior. So let's just create the
behavior setup first. And I want to remove the
base behavior setup calls. And let's just overwrite this by typing agent set destination. And this would be
the target position. And as you can see here, we don't have the
target position because the target position is
set to a private field. So in order to fix this, let's go back to the enemy script. And for the target position
transform this one, I'm going to add a
protected keyword. This way, the target post will still be serialized
on the inspector, but the child class
has access to it. So now we can just type target position
position and safeties. This way, when the init method are called inside
the hostage crit, The behavior setup that gets executed will be
different than this one. And it's going to be
this behavior setup. So we don't have to start
co routine shooting method. This way, the enemy won't
shoot at the player. Now the next thing that we
need to do is we need to also override the other
method that we've created, which is the dead behavior, and here we want to remove the base calls Instead of
adding the enemy keel, like this one here, we want to substract the player health. So let's just type game manager, the instance, and then
let's just call player it. And right now we can
substract this by one, but probably we need to
substract with a bigger value. So it's more punishing when we kill a hostage or civilian. Okay, let's just save this. And now let's head
back to unity. And for the hostage here, I'm going to set the Mx held 21. So we only need to take one
shot to kill the hostage. And now here, let's create a new shoot out area on the
second shoot out point. We want to add the hostage
here to test this out. So let's just drag the hostage to be the child of the
shoot out 0.2 here, and let's position this. I think we can position the
hostage in this area here. And rotate by 90 degree. And we need to create an empty object for the
target position. Let's just do that, create
a new empty game object. Let's just call this
target position one. Let's put this around
this position here. Okay. Now if we
select the hostage, let's put the target position to the target pos slot here. Let's just drag this
and then set this as the target position.
Now, save this. Let's test this out. Now, let's kill this enemy here. There you go. Let's see if
the hostage is coming out. Okay, there is an error here. The error was apparently, I forgot to put the
hostage game object here to the enemy
slot over here. So right now we are going to direct the hostage
here to the enemy slot. And since the hostage script is the child class
of the enemy script, that is why we are able to direct the hostage
game object to the enemy slot and the hostage script gets
registered by this enemy slot. And we need to make another
slot for the enemy, and we need to
duplicate the enemy. Let's just duplicate
this enemy here and then make this the child of the
second shootout point, and let's put this enemy here and rotate this 90
degrees on the y axis. Let's create a new
target position by duplicating this one and
probably put it here. And by selecting the enemy here, we want to set the
second target position as the target position. Let's just rename this,
select the enemy and drag this target position to the target position slot
of the enemy script here. And now if we select
the shoot at point, let's just drag the
enemy game object to the second enemy
slot over here. And save this and
let's test this out. Right now, there will be
an issue where we need to kill both the hostage
and the enemies to pass the shoot out point area. And we are going to fix that.
I'm going to kill this. And now here you see that
when the enemy came. After we kill the first enemy, it won't progress
because we need to kill the hostage and
the shootout point register the hostage
as the enemy. Now after we shoot, then we can continue.
We need to fix this. So let's go back to the scripts folder and open
the shootout point script. And here, Okay. Inside the enemy kilt method, instead comparing the enemy kilt value to the length
of the enemy list, we need to create
a new integer to account for the total enemy
in that shoot out area. So let's just call
this total enemy and create a new variable. And now when we are
sending the enemy, let's add the total
enemy variable depending of the enemy
class over here. We can just use a
ternary operator, and here we need to check if the enemy object and the enemy entry from
the enemy object, which is an enemy script, d is not a hostage script. Now we have this
exclamation mark, and this will check if this
statement is negative, then we want to add
the total enemy. Total enemy add by one else, we want you to add the
total enemy by zero. This way, if the enemy
is a hostage script, then the total enemy
won't get add by one. And let's go below here, and now let's just compare the enemy killed with the
total enemy variable. And now after modifying this, let's just add a debug log to see if this that is
executed or not. So let's just print the
game object name at A, string, clear, and save this. Let's go back to unity
and test this out. So press play, and I'm going to open
the console area here. So let's just shoot the enemy. And shoot out 0.1 is clear, and let's just kill
the second enemy. There you go. The shoot
out 0.2 is clear. So that is how we implement the hostage using inheritance, and we can behavior by
overriding the method.
25. 24 Area Timer Setup: Hi. In this lesson, we are going to
create an area timer for each shootout point. On each area, we are
going to start a timer, and if the timer is finished, then we are going to
proceed to the next area. And this will create
a possibilities that the player cannot
kill all of the enemies, so we can create a scoring
system later based on that. Now in order to do that, first, we want to
open Pis Studio, and then we want to modify
the shoot out entry class. Let's just go below and the
player move script here. We have a custom class for
the shootout entry object. And here we want to create
a new field type of float, and let's just call
this area timer. And we want to also set the default value
for the area timer. And I'm going to set
this two 15 seconds. And inside the
shootout point script, we want to add a new method for overriding the area clear. Let's just create a new public
method called area Clear. Inside this method, we
want to check first, we cannot use the
area clear name because it's already been
used by this property here. So let's just call
this set area clear. And we want to check if the
area clear bulion is true, then we want to return this and don't run the
method further. And if the area
is not clear yet, then we want to clear the area and we want to resume
the player movement. Let's just access
the player move objects or variable here, and let's just set
player movement to true. And now, whenever
the area is clear, we want to stop the
enemy from shooting. So in order to do that, we need to add a
new public method inside the enemy script. I'm going to open the
enemy script here. Inside of this class here, I'm going to create
a new public method, and I'm going to call
this stop shooting. F stop shooting,
we can just stop all of the cotins inside
this mono behavior. Since we only have one coroutine
inside the script here, we can just run this
method from mono behavior, and it will stop all of the
proteins inside this class. Okay, let's go back to
shoot out point here, and we want to look
through the enemy here. And for the able, let's
just call this enemy. And for our enemy collection, it's called enemy list. Let's just type enemy list. For each of the enemy, we want to access the
enemy sub object. And this enemy sub object
is a type of enemy script, so we can access the stop
shooting method right away. Hi, this is Rome
from the future. And in this script,
the shootout point, there is a bug that has
been pointed by Matt Hills. Thank you so much for
spotting this issue, and we need to check whether the enemy
is destroyed or not. Because if we finish an area, when the timer goes to zero, but we haven't killed all of the enemy and we kill
some of the enemy, that enemy will probably
already gets destroyed, and this will throw an error. So in order to do that, we need to check if the enemy and this is basically
the enemy entry, and each of the enemy
entry will have the enemy script object in it, so we need to check that object. And we want to check
if the enemy script that belongs to this
enemy entry is nal, then we want to continue to the next iterations
of this for each loop, so we can just type continue, and this will skip
this code here. So for that enemy entry, if the enemy script object is already renewal or it
has been destroyed, then we want to skip this code, and it will go to the
next enemy in the list. So with this small code, we are going to prevent
that bug from happening. Now we have this
method declared. We want to modify the
start shoot out method. And the first thing
that we want to add is, we want to pass a float, and let's just call this timer. And inside this method, we want to use the delayed action extension
that we've created. And with this delayed action, we want to run the
set area cleared with the delay of our timer here
that we pass on the method. Now if we save this, Let's go back to the
player move, script here, and you'll see that we
have an error because now the start shoot out a
for a float argument. Let's just pass the
float argument by accessing the shoot out
entries index of I, and we want to pass
the area timer. Now, basically, when
this gets executed here, the shoot out point will initialize the needed
value or variables, and then we'll delay
the set area cleared method with the time as
the delayed interval. Now, let's head back to unity, and let's test this out. So I I Select the
main camera here. You'll see that now we have
the area timer of 15 seconds. Let's make this a
very small value. For example, 5 seconds. Now let's test this
out, press play, and let's see when we arrive
to the shoot out area. You see after certain seconds,
it will start moving. There you go. It starts move. It means that the
timer is already finished and it starts
move to the next position. We are going to add a UI later. But now the next
thing that we need to do is we need to create a new timer object to count
the timer for the UI later. I'm going to create a
new C sharp script, and let's just call this object. And I'm going to open this
script inside Visual Studio, and I'm going to remove
the mono behavior. So this will be a custom class. Inside this time object class, we want to create a new
private co routine object, and let's just call this,
and we want to create a new public method
called starter. And since we are going to
use a co routine here, we need to pass a mono
behavior to this method. Let's just type a mono behavior
as the first argument, and I'm going to call this B. And for the second argument, I'm going to pass the duration. And basically, we
want to skip if the timer object is not null here or if we already start a, then this time object
values should not be null. So let's just check
if the time is, then we want to return
this method here. I'm going to add
a debug log here, so we can see if
something happens. Let's just type debug log, and let's just tie
timer already runs. We need to create
the timer coroutine. Let's just type I enumerator
for the written type, and let's just call
this timer runs. We want to pass the
duration here to the cotin Inside the coroutine, we want to create a wild loop with a condition if the
duration is greater than zero. Then we want to start
counting the timer. Here we can subtract the
duration with a value of one, and then we can add
a yield return. But instead of using Nu, which is going to
return every frame. So the while loops is going
to be executed every frame. I'm going to delay the
loop for every second. I'm going to use new weight 4 seconds and set
the value to one. This will work
exactly like a timer. Every 1 second, we are going to substract the
duration by one. If the duration is zero, then we want to break out of this wire loop and execute
any code below here. When the timer runs out, we want to set the timer
cotin back to null. And now we have
this timer object. I'm going to expose
a public integer, and I'm going to call
this display timer, so we can see the timer
on the inspector, and I'm going to pass the duration value to
this display timer. So let's just set the
display timer value to the duration that is currently running in
the current loop. So it's a float, and
this is an integer, so we need to cast
this to an integer. I'm going to add a casting in front of the duration value. So it doesn't throw the error
anymore. Let's save this. The next thing that
we want to do is we want to add this timer
object to the game manager. You may ask that why we create another script
for the timer object. This is to comply with the
single responsibility pattern. So I'm going to try
to make every script to serve only a single
responsibility purpose. So it's easier for
us to debug later. When there is an error, we can isolate an error
in a single script. Instead of putting
the timer inside the game manager also with all of the co routines
and the method. And this is very
easy to use here. Since this is not
a monel behavior, we can just create
a new private field and with a type of time object. And let's just call this
time object here. I'm sorry. And I'm going to
initialize this. And save this. And now we can use all of the method
inside the timer object. So here inside the game manager, we want to create a new public
method to start the timer. So here below, I'm going to
create a new public void, and I'm going to call
this start timer, and we are going to ask for the duration of the
timer as an argument. And now to start the timer, we can just access
our time object, and then we can run the
start timer method, and then pass the mono behavior. And since the game manager
is a mono behavior, we can just pass
this class here, and then pass the duration. Another thing that we
need to create is, we need to create a
method to stop the timer. So let's go to the
timer object here, and I'm going to create a new public void
or a public method, and I'm going to call
this stop timer. And for the stop timer, we are going to ask for a mono
behavior as the argument. And here we want to check
if the timer is currently null or if there are no
time currently running, then we want to
return this method. Or we can just add a
debug dot log here, and let's just say that there
are no currently running. And I want to
return this method. But if the timer is not null, then we want to stop the
cotin using the mono behavior that we pass here and then
run the stop cotin method. And for the crotin, we can just pass this
ter cotin object here. Let's just set the
cotin argument for the stop protein method. And then after
stopping the cotin, we want to set the
timer back to null. We have created the
stop timer method. Let's go back to
the game manager, and then we want to create a
new public void stop timer. And here, we can just
access the timer object, run the stop Ter
method and then pass the mono behavior
with this keyword. Okay, now we've created the start timer and
the stop timer, we want to trigger this
from the shoot out point. Whenever we run the
start shoot out method for the shoot
out point here, we want to also start the
timer inside our game manager. Let's just access the
game manager instance and then run the start timer. And this as for the duration, so we can just pass the
time as the duration. And then here, we want to stop the timer if all of the enemy is
already been killed. So here, let's just access the game manager instance and then run the stop timer
method and save this. For the set area cleared, we don't need to stop the time because by the time
the area is cleared, this means that the time is up, so the timer will stop
by itself and save this. Now let's head back to unity. And I'm going to modify
the timer again and make this probably to 10 seconds
and save the scene. And if we select
the game manager, we cannot see the timer
object because we haven't set the class as a
serializable class. So let's go back to
the fisal studio and inside the ter object. I'm going to add a
serializable attribute. So Type system serzable. Save this. Another thing
that we need to do is, I forgot, since the timer object is private, we cannot see it. But if we want to see it, we can add a sized
field attribute or we can just enable the develop
view of our inspector. Then here we can see
our timer object and we can see the display
timeer field. Now let's save the scene
here and let's play this. Now, if we arrive
to the shoot area, you see that the display
timer real start. Okay. There is something
that I forgot you did, and that's why the display
timer is not working. So let's go back to
Fiscal studio here. And inside of our Tier object, we forgot to run the co routine inside the start timer method. So now we need to run the
coroutine if the coroutine is. So let's just t equal
to start co routine, and we want to run
the runs co routine, and then pass the
duration as the argument. Before this
enumerator time runs, never gets executed because
we forget to run this. So now I'm going to
save the script here, and let's go back to Unity. Now, after the
script is compiled, let's just run and
see this value here. Take notice of the
display timer value. Once we arrive to the area, it start counting nine, eight, seven, and when this
value becomes zero, then it will continue
to move one, zero. There you go. And
on the next area, it will start to count the area. It will start to count
the timer again. This is how we create a timer
with a separate object, so it will handle
all the time code. In the next video, we
are going to start creating the UI
for our game here.
26. 25 Game Play Statistic: In this video, before we
continue to create the UI, we want to create the
game stats first. So we want to record how many shots we
have been fired and how many enemy that
we've been killed and how many enemy are there
in totals in the scene. So let's just start to that. Let's go to the
visual studio here, and inside the game manager, we already create
two integer variable for the enemy hit
and the shots fired. This is basically for
calculating the accuracy later, but we want to
also add the enemy killed counter and
the total enemy. Counter, and also
hostage killed counter. So now we need to create a
method to add this value here. So let's just create a new
public method below here. The first one that we
want to create is we want to create a
register enemy method. And this is for registering
an enemy, and to register, we only need to add the
total enemy by one, and this will be called by
the enemy script on start. The next method that
we want to do is also a public method and
called hostage kill. For this one, we want to add
the hostage killed value. The next thing that we
want to create is we want to create an
enemy killed method. Et's just create a
new public method and call this enemy killed. Let's just add the enemy
killed variable by one. Now we've created this
three new method. Let's go to the enemy script here, and upon initialization, on the behavior setup, we want to add the gay manager
instance register enemy. Whenever the enemy
is initialized, we want to register
an enemy here. Below here, whenever
the enemy gets killed. And I think Yeah, here, we want to
register the kill. So let's just run
the game manager instance dot enemy killed. Oh, sorry, instead
of adding this here, we can just add this inside the death behavior.
So let's just do that. And this will add the
enemy killed counter. And for the hostage script, we want to add the game
manager instance do hostage killed whenever
the death behavior on the hostage script
gets executed. Let's save this. Now
let's head back to unity. With the inspector set to debug, we will be able to see those private variables
that we have created. Now let's test this out. I think I need to
increase the timer, so I'm going to go to
the main camera here. First thing, I'm going to set the area timer to 30 seconds. So we have time to kill all
of the enemy and save this. Now let's press play
and test this out. Let's select the game manager and we will see the stats here. So now let's kill
this enemy here. And we've killed two enemy. Now let's try to kill the hostage first to see and let's kill this
enemy. I need to reload. There you go. Now
you see that I've fired shots 15 times and
I've hit the enemy 13 times, and also we have killed three enemies out of three
total enemy in the scene, and we also killed the hostage. Later we are going
to use this value to calculate the scores at
the end of the gameplay.
27. 26 UI Health Bar: Hi, in this video, we are going to start
working on the UI. And in this chapter, let's create the Health
UI for the player. So in order to create
the Health UI, I've already prepared a package. So let's just import
that package. I'm going to go to
the assets menu, import package, and
choose custom package. And then here I have
the UI Pa package. Once it's opened, the import package window,
let's just import all. And I'm going to drag the UPAC inside our underscore
game folder. Now, if you open the UiPaC, we will have a prefabs folder and inside this prefabs folder. I've already prepared
a player HUD object, and we can drag this
to the main camera. I'm going to drag this to be the child of the main camera. And now let's adjust
the parameter. As you can see here,
we have a help bar. We also have the MO counter and also the
reload notification. The way the prefs set up is already facing the correct
way relative to the camera. If you can see here, if
we go to the game view, you see we have the UI following the camera because
it's parented to the camera, and it is slightly
rotated on the y axis. So now we are good to go. I'm going to also import a
custom fund and for this step, you can just pick
your own pond and replace the fund in the
text after that step. Inside the underscore
game folder, I'm going to create
a new subfolder, and I'm going to
call this funds, and I've already
prepared a fun here. I'm going just to drag this
fund that I'm going to use. And now once we have
imported the fund here, I'm going to right click on the phone and then
choose to create menu, and under the text Mash menu, I'm going to click
the fond asset. So it will generate the fond
asset for the text Mash pro. So now that we have
generated the fond asset, we can just select the
child object that has the text mesh pro
components attached to it. And here we can pick the
phone that we've generated. So now we have a
better looking phone. And for the real text, I'm going to also change phone
text here. There you go. And now we can start working on the script for
the UI Manager. So let's go to the
scripts folder, and I'm going to create
a new C sharp script, and I'm going to call
this UI Manager. And let's open this UI Manager. So now that we have opened
the UI Manager script, I'm going to delete the
start and the update method. I'm going to also delete the mono behavior
inheritance here. And I'm going to add a system
serializable attribute. And I'm going to use this
UI manager class just like the time or object class that
we've previously created. So let's go to the gay manager, and I'm going to move
the game manager to be beside of our
UI manager script, so it's easier to switch. And here we can
declare the UI Manager as a field or object inside
the game manager script. So here I'm going to
add a serialized field. For the type, I'm going to use
the UI manager class here, and I'm going to call
this UI manager. Now that we've created this
back to our UI manager class, we want to import the Tex
Mash pro library first. I'm going to type using
tex TM pro class, and then I'm going to also be
using the Unity engine UI. Here, inside the
UI manager class, we want to create a
new serialized field, and this would be
a type of slider, and this is for our health bar. I'm going to call
this health bar. Then we want to create a public method for
updating the health bar. But before we
create that method, we want to create a new
public void init method, and this is for initialization. And for initializing,
we want to pass the maximum health that
we have for this player. Let's just type float, and let's just call this float Mx health as the argument
for the init method. And then here for
the health Bar, we want to assign its x value to the Mx health that we've just
passed through this method, and we want to also set the health Bar value
to the Mx health Argument here. The next
thing that we want to create is we want to create
a new public method for updating the health. Let's just call this
up date health. And then we can
just pass a float, and let's just call
this float value. And now whenever we
call this method, we want to update the
value of our health bar. Let's just type
health bar, value, equal the value that we've
passed here and save this. Now if we go to
the gain manager, we want to initialize
the UI manager. Let's just equal
the new UI manager. Instance and save
des, and here below. Inside the player health method, we want to update
the current health to the health bar value here. Let's just type here below the current health
substract by the damage. Type UI manager, update health, and then pass the
current health value. And this will update the health
bar value and save this. And now let's head
back to Unity. Now if we select the
game manager here, you see that we have a new
field called UI Manager, and we can expand
this, and we can see the field inside the
UI manager class. Now we want to drag the
health bar to the slot here. So just select the
parent health bar under the player HUD here, and I've already prepared the slider component here
and set its value ten, and you'll see that if we
slide this value here, our health bar gets decreased. So let's just select the game
manager again and then drag the Health Bar game
object and save this. One thing that I forgot is we need to run this
initialized method. So let's go to the game manager. Inside its innate method, we want to make sure that we are initializing the UI Manager. Let's just call the init method of the UI manager
object and then pass the current health that
we are initializing before. And save this. Now,
let's go back to Unity, and now let's test this out. I'm going to press
play and see if sorry, we need to hide the read text first, so I'm going
to hide this. And now let's see that
if the health bar gets updated whenever we
get shot by the enemy. There you go. This is how
we create the health bar. Feel free to explore
the prefect that I've created and how I've
lay out the UI here. Also for this reload, it has a animator component. Basically, it's just
animating its Alpha value. Here, as you can
see, it animates the Alpha value of the four
te color of the text here. In the next video, we are
going to continue working on the weapon
information of our HUD.
28. 27 UI Weapon HUD: In this video, we are going to continue work on our UI system. And this time we are
we want to change the weapon icon if we
pick up a new weapon, and we want to also
update the MO counter here and also enable the reload notification
when the MO runs out. Now in order to do that, let's go to is Sudio, and we need to access to the weapon that
first, and also the players. Inside the player script, we want to create a new
public static event. So let's just create
a new public static, and for the event,
I'm going to set this to a system not action. And I'm going to pass a
object for our event here. For the signature, I'm going
to add the weapon data, and I'm going to call this
event on weapon change. And I'm going to fill this
event with an empty delegate. So we don't have to
check for any subscriber whenever we want to invoke
this event. Save this. And I purposely
make this static. So it's easier to
access this from any other script without having a reference to
the player script. Okay. The next thing
that we need to do is, we need to go to inside
the switch weapon method. And here, after we set
up the new weapon, we want to broadcast this event. Let's just invoke the
weapon change event, and let's just pass the
carbon weapon data. Because current weapon
data, it's a weapon data, so we can pass this to our
own weapon change event, and this event will
broadcast this data, and the one that
are subscribing to this event will receive
a new weapon data. Now the next thing
that we want to do is we want to go to
the weapon data, and inside the
weapon data class, we want to create a
new public event, and this one won't
be a static event. It's just going to
be a public event, so we can just type
system dot action. And we want to pass the
MO as the signature or the object that we are
broadcasting via this event. So let's just add integer
as its signature. And then we can just call
this on weapon fire, and we can just set up an anti delegate just like
we did in the player script, safety and or maybe we can
just call this on weapon fire. And we want to broadcast the
on weapon fired event here inside the weapon
update here and whenever we are pressing
the mouse button here. So now let's just invoke the on weapon fired event here and then pass
the car on MO. And for the machine gun here, we want to also invoke the event here. Now
let's just save this. And we want to also
add the weapon icon. Let's just go top here in
our field declaration. And I'm going to add a
new serialized field, and this will be
a type of sprite. And let's just call
this weapon icon. I'm going to expose this serialized field
using a property. Below here, I'm going to create a new public property
type of sprite. And then I'm going to
call this GT icon. And then for the GT accessor, I'm going to return
the weapon icon. Here. Okay. Now
we can save this, and let's go to UI Manager. So now inside the UI Manager, we want to declare a
couple of variable, and I'm going to add
header attribute, and I'm going to pass a string
for this header attribute. And I'm going to call this
weapon HUD, and below here, I'm going to create a new
image for the weapon icon. Then for the other
fields that we need to create is
a text Mash pro. Let's just search for the
text Mash pro object, UGI, make sure the UGI one, and let's just call this MO tax. And the other thing that
we need to create is a game object for
the reload warning. Let's just call this
reload warning. And here inside the in method, we want to subscribe to an
event on the players script, the static event
that we've declared. So let's just access
the players script, and then let's just access
the on weapon change event. We want to subscribe this method that we have here
inside the UI Manager. So we need to create
the method first. I'm going just to
subscribe to a method. And I'm going to make iso Studio to generate this
automatically. There you go. We have this at weapon and it already passed
the weapon data here. So I'm going to remove the Trow new not implemented exception. And here we want to also create a new private variable
to hold our weapon data. Let's just type
private weapon data, and let's just call
this current weapon. Here inside the
update weapon method. We want to check if the
current weapon is not null. Then if it's not null, we want to remove the
on weapon fire event two a method called update MO, and we haven't created that yet, so let's just create a new void, called update MO, and let's just pass an integer for the MO. And here we want to
update the MO text. Using the set text method from
the text mesh pro and pass the O that we passed
as an argument here in the method and convert
this two a string, and I'm going to
format this string two a two digit value here using the string argument
of this two string method. Now here, if the current
weapon is not null, we want to unsubscribe the weapon fired event
from this method here. Let's just type negative
equal up date MO, and this will unsubscribe
because we are changing the weapon data to
a new weapon that we passed by this event here. So now we want to update
the Caron weapon to the new weapon object that we've passed in this argument here. And then we want to subscribe to this new weapon data event
to our update MO method. And below here, we
want to also update the weapon icon sprite by accessing the sprite
properties and then pass the Caron weapon, get C sprite properties,
and save this. Now that we have an
event subscription, we need to also remove this whenever this UI manager
gets deactivated. So I'm going to create
a new public method and just call this remove event. And then I'm going to copy
this line here, paste it here. But instead of using
the positive sign, I'm going to change this
to negative sign to unsubscribe this method
to this event here. Let's save this and
inside the gay manager, we want to create a new
void on disable method. This is a built in
method from unity. Basically, it will gets executed whenever this
script gets disabled, whether we stop the editor or if the gay manager
gets destroyed, this will also invoke. Inside the undisable method, we want to access
the UI manager, and then we want to access
the remove event method. Another thing that
we want to add here, inside the UI manager. Whenever we update the O, we want to also enable
the reload warning, whenever the O is zero. Here, before we setting
the O text here, let's just access the
reload warning game object and then run the active method. Then for the argument,
the bully and value, we can just pass O less
or equal than zero. So this will check if the MO
is equal or less than zero, then this will be true and the reload warning will gets activated. And
let's save this. And let's go back to Unity. I'm going to wait a while for
the script to get compiled, and now it's finished. Let's select the game manager. And here we have a new
fields for the weapon HUD, for the weapon icon,
we want to direct this weapon icon object
from our player HUD prefs. The MO text just direct
this tax object, and for the reload warning, we want to dg the reload game
object. Let's save this. Now let's test this out. Okay, there was an issue, and apparently, it's related
to the race condition. So in order to fix this, we need to go to the fiscal
studio, and basically, the switch weapon are executed from the start of
our player script. And on the UI Manager, The init is also triggered from the start
of the game manager. If the player script start happens to be earlier
than the game manager s, then the UI manager won't get ahold of the new weapon data that the player
script are passing. In order to fix this, we
need to delay this a bit. I'm going to use this extension that we've created,
the delayed action, and then I'm going to pass a delegate that
runs this method. And then I'm going to
pass a very small value, sample 0.1 second,
and save this. Another thing that
we need to fix is also on our weapon data. Whenever we update or
we reload the MO here, as you can see, we want to
also fire this event here. Let's just copy that line
and then paste it here. So this will update the
MO on our UI manager. Another thing that
we need to do is, let's go back to Unity and
inside the data folder, we need to add a sprite
for the weapon icon. So I'm going to select
the default gun here. And with the UIPac that
we've imported before, we will have this gun icon, and I'm going to use this
for the default gun. And for the machine gun, let's just use the
other UI icon weapon here. And let's save this. Once we've set up the icon here, we also need to add a bit of modification inside
the weapon data here, and let's copy this
weapon fire line here. And we want to also run the weapon fired event whenever we set up this new weapon here. So let's just run it here. And after we fire the event
on the set up weapon method, we want to go to
the player script. And then we want to change
the order of our event here. So we want to make sure that
the event it's fired or it's broadcasted before we
set up the carbo weapon data. So let's just paste it here and delete this empty line here. Save this. And now
let's go back to Unity. Okay. So now let's
test this again, and if this works, then the real text gets
hidden right away, and the Oh gets
updated right away. So let's just press
play. And there you go. As you can see here, the O
gets updated right away, and we can shoot and then
when we reload it's update, and we can also
pick this weapon, and it change the icon. Also update the O, but we
cannot reload with this weapon. So if this weapon is empty, then it will return back
to the default gun here. And let's just shoot out, and if the MO runs out, you'll see that the
reload tax is enabled. And once we reload, it will gets disabled. So yeah, we finish with
our weapon HUD system, and on the next video, we are going to work
on other UI elements.
29. 28 UI Timer: In this video lessons, we are going to continue
to work on the UI element. And this time we are going
to work on the time. So in order to
work on the timer, let's go to the
gay manager here, and I'm going to create
a new UI Canvas object as the child of our gay manager. Now with this canvas selected, I'm going to change
its setting for the UI scale mode under
the Canvas scalar. Let's change this two
scale with screen size, and I'm going to set the
reference resolutions to be a full HD 11920 by 1080. I'm going to set the
screen match mode to 0.5, so it will be halfway
between the width and the height and save
the scene again. And now inside this canvass, if we go to the D
view here and then we press F while
selecting the canvass, you'll see that we have
this very big canvass here. And every UI that
we've prepared inside this canvas here will get overlaid in front of our
camera in the game view here. So now I'm going to create
a new child object, and it will be a type
of text mesh pro. And if we zoom this here, this will be the timer counter. I'm going to call this timer, and I'm going to put
this on top here, but I'm going to
change its size first. I'm going to set its height
to 75 and the width to 150. For the text, I'm going
to set this 200 and also set the out of
size to be enabled. I'm going to align
this to the middle. And also change the phone assets to the rex LA that we've
created in the previous video. Now we have this timer setup. Let's just line
this by accessing the anchor presets and by holding the outt button
on the keyboard, we can just choose this
top middle one here, and it will snap our timer to
the top of our canvas here. Now let's open the
UI Manager script. Let's go to the FSO Studio
and access the UI manager. And here, for the UI manager, we want to add a new field
below our health bar. Let's just type a new Serialize
field, and for the type, it will be a text mesh P UI, and I'm going to
call this Tier tex. Now we want to modify the Tier object script
that we've created before, so let's just open
the Tier script. Here, we want to create a
new public static event, static event is quite powerful. It makes us easy to
communicate between script without having
dependency on each other are usually
called decoupling. Let's just type public static, and we want to set this
as a system action, and then we want to
pass an integer. Let's just add integer
as its signature, and let's just call
this on time change. And set up an anti delegate. With the static event, declare, we can go to our time runs here, and then here before we
subtract the duration, we can pass this duration via
our on time change event. Let's just invoke this or
broadcast the on time change, and we want to pass an integer. But since the
duration is a float, we need to cast
this to an integer. I'm going to add a cast to integer type by using
asset parentheses and then the integer
keyword and then type the duration variable name here. This will convert the
duration into integer, and the on time change
event will broadcast this. Now inside the UI manager, we want to subscribe to
this on time change event. Inside the init method, let's just add a
new time object, and let's access the on
time or change event, and we want to create a new
method called update Ter. But we haven't created this, so let's just tell Visual
Studio to generate automatically. And
now we have this. I'm going to change the
arguments name to current Ter. Inside this update timer, let's just update
our timer text here. Let's just type
time tax, set text, and then we want to convert the current time into a string
using the string method, and then format this
two a two digit number. Using the string argument. Save this. Now that we have a couple of subscription
in the UI manager, we need to make sure
that this also gets removed whenever the
object gets de activated. Inside the remove event, we want to paste this
line here and then change this to subscription type. And here, we want to also
make sure that we are unsubscribing the weapon fired
inside the remove event, so just copy that line and
paste it here, and save this. Now let's go back to Unity, and let's test this out. First, we need to set
up the timer text here, and we already create
the TR game object with the text mesh component attached to this
time game object. Let's just drag this to
this Ter text slot here, and it will register the
text Mash Pro UI component. Now let's save this and
let's test this out. Now, when it arrives, it updates the timer, and it start counting the
timer here, as you can see. So let's just kill the enemy, and let's go to the next area, and it will count down the
area again when it arrives. So there you go, as you can see, it's quite easy to implement the I using the static event.
30. 29 UI Hostage Killed: In this video, we are going to continue to work on the UI, and this time we
are going to add a text whenever we
killed and hostage. But before we delve into
the hostage kill text, I'm going to show you that I've created another
shoot out point, the third shoot out point, and I've duplicated the third
one from the second one. In the third Shula point, it consists also
hostage and enemy, and I put it in
this position here. For the hostage, I put it here, also the target position
on this position. And for the enemy, I put it here, and the target position
is run this position. So you can just do that
yourself to follow this video. Another thing that
we want to fix here, let's just run this
game to test this out. But you'll see that at the end, the camera is a bit tilted. There you go. It's a bit tilted. We want to fix this.
But before we fix this, make sure that the third
shootout point are input to the player move script as the third shootout point. I've added an entries here, and then I drag the third shootout point object
to the slot here. And then I set the
distance at 17 here. Now let's fix this.
We need to go to the scene view here and select
the camera path movement. And then select the
last point here. And if we select the
individual point here, and if you select rotate, you'll see that we have
this rotation gizmo, and we can rotate just this point to
adjust the up vector. But I found the best
value for this is 358, so I'm going to just input
here the angle under the selected point options on
the path creator component. Now if you select
the camera again, and then I going to scrub
this to update this, you'll see that we have a
correct camera rotation, not slightly tilted. Let's just enable the
back option and then I'm going to set the preview
distance back to zero. Sorry, I'm going to enable
this and then disable this. Now that we've set up the
third shootout point, let's just create the
hostage keel text. Under the game manager
Canvas, expand this. And I'm going to create
a new UI text mesh pro, and I'm going to go
to view here and then press F to focus on
this text mesh object. And on zooming out, I'm going to increase
the width text, maybe around 350,
for the height, I'm going to make this 100, and I'm going to
type hostage kel. And now inside gamevi, we
can see the text here, and let's just adjust
so it looks better. I'm going to set the alignment
to center and middle. Also for the verte colors, I'm going to pick a red color and then pick the font asset
that we've created before. And then I'm going to
set this out of size, but I want to make this bigger. So I'm going to
increase the width to around 450 and then
the height around 150. So we have a bigger tax here. And let's just
increase the maximum. Okay. I think this is the maximum tax size according to the width
and our height setting. So if we want to
make this bigger, we need to increase
the size here. So let's just increase it again. Okay. I think it should be okay. And let's just call
this hostage kel. And now we want to
modify the script. The first thing that we
need to do is we need to open the UI manager. And here inside the UI Manager, we want to create a
new serials field with type of transform. So I'm going just to type this. And then for the type transform. And for this field,
let's just call this hostage killed text. And now inside this UI Manager, we need to create
a new public void to show this text here. I'm going to create a new public void and then call the method show hostage kill, and we need to grab a position. So I'm going to pass a
factor three as an argument. And we want to also pass a
Bulion for showing this. And we want to enable the tax
based on the bleion here. Let's just access
the game object of the hostage
keel tax variable, and then run this a active method and then
pass the show bulion. And we want to check
if the show is false, then we want to
return this function, so we don't need to
execute more code below. Now we need to create
a helper function to keep our UI inside
of our screen. Because if the hostage is at
the very near of our screen, then we might show the
hostage keel text here, but some part would
be outside of the screen here because the
position of the hostage keel. We need to count that to make sure that the UI doesn't
going off screen. In order to do that, let's
open our extension class here, and then let's create a
new public static method. I'm going to create a new
public static method, but this method will
return a factor three Let's just call this get
position inside screen. Here, we want to pass a vector through for the resolution,
the base resolution. Let's just call this base res, and then we want
to pass a vector two also for the
anchored position. Then let's just
create the method. Inside this method, we want
to create a new float, and let's just call
this with boundaries. For the width bound,
we want to use the base resolution x. Substract with the
re transform width. Instead of anchored pause, we need to pass a rec transform. Let's just change this to a t, and then I'm going to
substract the base re x component here
with the rectangle t width and let's just
create the height bound. This will be the base S dot y, substract with
direct dot height. We are grabbing the width and the height of our UI component. Also, we want to add a
float for the offset, and then we also
want to subtract this resulting value
with the offset. We have some margin to the
edge of the screen here. And then for the height, we also want to add the offset. So now that we have the
width and the height bound, we can use this for clamping the anchored position of
the rec transform here. I'm going to explain this a
bit for this formula here. Let's head back to unity, and let's just
create a new image. And now, as you can see, this image is 100 pixel width
with 100 pixel of height. If we want to increase this
size to cover the screen, then we need to insert the
base resolution with Sizo. In this case, is 1920. And with this, we need to get the half size of
the screen here. And then we need
to take account of our width in half
of our UI elements. So for example, if I
put it this in the 960, which is the 1920 divided by, then this is our position. But this will result
in the UI gets crop or some part of the UI
is outside of our screen. So in order to fix this, we need to substract this with half the size of our
width, which is 250. This is basically
what we are doing. And for the left
side of the screen, then we need just
to multiply this by negative one. There you go. With this formula, we can
calculate the UI element, so it will stays
inside our screen. I'm going to delete this image here and head back to
the fiscal studio. Now I'm going to
create a new vector u, and I'm going to call
this adjusted position, and I'm going to set the
adjusted position value to be the rectangle, the retrans from object that we pass and its anchored position. I'm going to grab the
anchor position as the base value of our
adjusted position. Then I'm going to adjust
the x component of the adjusted position variable
using the math F clam, and for the value, I'm going to pass
its x value itself. And for the minimum, I'm going to use
the width bound, multiply this by negative
zero, sorry, negative 0.5. Because we want to
divide this by two, and for the minimum position, we want to multiply
this by negative one. So we can just do
this in one take by multiplying with a value
of half or negative half. And then for the maximum value, we can just use the
width bound again and multiply this
by positive 0.5. So this way we will get the
position inside our screen. And for the y position, we want to do just the same
by modifying the y component, and then use the math
f class here and the clam method and then pass the y position of our
adjusted position, and then use the height bounds, multiply this by negative 0.5, and then also for the maximum y, we want to multiply
this with positive 0.5. Now that we have the
adjusted position, we want to return this
adjusted position, type return adjusted
position. Now we have this. We can use this to position
the hostage killed tax. Let's go back to the UI manager, but I'm going to
save this first. And inside this UI manager, let's just set the
hostage k tax position, and this is the world position. To the position that
we are passing here. And then let's just create a new vector two
adjust position. And here we want to run the extension class and the get position
inside script method. For the base res, I'm going
to create a new vector two. And since we already
declare this, let's just type the value
which is 1920 by 1080, and this will be in flowed, so I'm going to add a F sign. For the second argument, I'm going to pass the hostage
kel tax and for the offset, probably I'm going
to add 25 pixel. Now save this. Once we've
got this value here, we need to apply this value to the anchored position of
our hostage kel tax here. I'm going to access
the anchored position, and we are going to adjust this position
here and save this. Now we've done with
the UI manager Park. We need to modify
the game manager to add the function to
trigger this method here. Here I'm going to create
a new private method, and I'm going to call
this show hostage kel. And we want to pass a
vector three position, and we want to also pass a bulion for showing
the text here. And now inside this
show hostage keel, we need to create
a new actor three, and we can call this Vector
three screen position. And for the screen position, we are going to grab
the camera from the player move object. And since this is a
player, we can grab this, and then we can use
the gap component and then type camera. And here, we can use
the World screen point, and then we pass the
vector three that we pass in the method here. And we will receive this vector T position from our hostage
position in the world. And then after we convert
the world screen position, we want to run the UI manager, sorry, the UI Manager, and then run the
show hostage Keel and we want to pass
the screen position. And also the show
bulion argument that we've passed
in the method here. Let's just type
show and save this. And now we need to
modify the hostage keel. First thing, we need to pass a vector three world position. This is called this world
position, so it's clearer. Then here below, we want to run the show hostage keel
that we've just created here. And also we want to pass the world position that
we've passed here. Whenever the hostage get killed, we want to set the
B show t first. And then we want to run
this delayed action. As usual, we need to create
a new delegate here, and we can pass this line
here inside this delegate. But instead of showing the text, we can just set this to
falls to hide the text, and we can add a
delay for 3 seconds. And then close this. Now we've modified the game manager
and the UI manager. There is one thing that
we need to fix here. Inside the hostage script, we need to fix the hostage heel, and this is quite easy. We can just pass the transform the position of the hostage. But since the pivot of our
hostage is at its feet, so we want to add a factor three up and probably
multiply this by 1.2. So the position will be
probably at the chest of the hostage or at the torso
of the hostage and save this. Now let's head back to Unity. The other thing that
we want to do is here, we want to add an animation, a blinking animation, and
we can just add animator. And then use the
animator controller that we use for the reload text. So let's just grab the read
animator controller here, and it will use the
same animation here. It will make the text
blinking like this. So now we have this setup. The other thing
that we need to do is inside the UI manager, we need to make
sure that this text gets disabled on start. Here inside the init method,
I'm going to add this. But instead of setting
this value show, I'm going to set this
two false and save this. And now we can really test this. Okay, so I'm going to go
focus on our camera here, and then inside
the game manager, we also need to
set the text here, here under the
hostage keel text, we need to drag the hostage keeled object from our Canvas, and now we can save
the scene here. Now let's play and
test this out, and let's try to
shoot the hostage. Let's try to so the hostage. There you go. We have
the text here showing, and let's kill the enemy here. Now if the hostages appear here, let's shoot it and yeah, try to keep the text
inside the screen. We can test this actually
by changing the size of our hostage size
to a very big value, for example, set this to one
than and 500 on the height, and we can increase
the max size here. And we can test this
out just to test if the extension that we've
created is working or not. So let's just give
it a try again. Although we want to
revert that after this. Re. There you go. And probably we can see it
with this hostage over here. Let's just shoot. There you go. Try to keep our text
inside the screen here. Now let's just revert this back. One thing that we
need to adjust here, I think we need to
adjust the height. I don't think we need to
multiply this by 1.2, so let's just factor that up. And let's check
the hostage pivot here because I'm not sure
if it's on the feet or not, so let's just focus on
and this is the pivot. This is the center,
this is pivot. Yeah, we can multiply
this with the vector up, but we need to make
this even smaller. I'm going to set this to 0.4, Multiply this by 0.4. So we will see the text around the chest and let's
give it a try one more time. Yeah. It's quite nice, the position correctly on the torso or the
chest of the hostage. Yeah. Okay. Yeah, that is how we create a UI that can appear based
on the world position, and we can also keep
those UI inside our screen with the extension
method that we've created.
31. 30 UI Intro and End Screen: In this video, we are going to continue to work on our UI. And this time we are
going to work on the Intro UI and also
the end screen UI. So now let's go to
the review here, and then let's expand the game
manager. Also the Canvas. And I'm going to focus
on our Canvas here. And the next thing
that I want to do is, I want to go to the UIP folder. Inside the prefabs folder, we have the nscreen prefab. So I'm going to direct
this nscreen prefab to be the child of our Canvas. Now we have this n screen here. We want to modify
the text first. So let's just expand this, expand the layout, and
inside the layout, we have a lot of
child object that holds an area for
the title here. And under the title
area game object, we have a text mesh pro. So let's just select
the fun asset xia that we have created before,
for the new text. And for the score area, let's just expand all of this. I'm going to collapse
this and then hold out and then
expand all of them. And for the subtext and value, I'm going to select
all of them together. And also the title and rank, and I'm going to change the
fund asset also to Raglea. So let's just do that. Now we have all of this. And the next thing
that we want to do is, we want to create an
animation for this. So in order to do that, Select the end
screen game object. And under Window,
I'm going to go to the animation menu and
then create animation. And we will have an
animation window here. And if it's appear floating, you can just click on
the panel name and then direct this and then
dog to this area here. Now with the end
screen selected, we can create an animation. I'm going to press create here to create the animation
for the end screen. And under the game
folder animation, I'm going to create
a new animation for the end screen fade in. And now we can move the end
screen game object here. I'm going to use
this move tool here and then I'm going to
move this on the y axis. And I'm going to enable
the record button, and in 1 second of duration, I'm going to slide
this back inside. I'm going to round the number, the top view 2200 and the
button view 2200 also. Okay, so we have this here. Let's press play and see
it in the game view. There you go. I'm going to delete this keyframe here because we
won't be needing it. Okay. Now we can go to the project view here and
inside the animations folder, we want to make sure
that the animation for our end screen fade in here, the loop times is disabled. So I'm going to disable this, so it's not going to be looping, and I'm going to disable
the end screen here. And let's save the scene. And for starting the game, I'm going to create a new
text Mash pro game object and set this to 500 or 600
and for the height, I'm going to set this to 150. And I'm going to type ready
and change the phone to Axle. For this ready text, I'm going to enable the ato size and set the
maximum to around 400. So we have a very big tax here. I'm going to set the alignment
to center and also middle. For the color, I'm going
to pick a orange color, perhaps this one. More orange. Yeah. And I'm going to
eimate this text also. Before we animate the text, let's just rename the
text two ready text. And we want to check on our script here on
the game manager, we have the state, and we
have the game start state, and it's delaying the S state
to gameplay in 3 seconds. So we want to create animations
with 3 seconds duration. So let's go back to unity here. And by selecting the ready text, let's go to the animation, and then let's create
ready text animation. Now we have this animation clip. We can create an animation. Here, I'm going to record. I'm going to add a property
of the wreck transform, and I'm going to grab
the anchored position. So I'm going to grab
the position here. And for the starting frame, I'm going to move this
to the left side of the screen and probably set the position to negative 1,500. So we have something like this. And I'm going to keep
this for 2 seconds. And let's just
copy this keyframe here by selecting the key frame, press Control C, and
then press Control V. So it's going to copy
this keyframe here. Let's just delete this
double key frame. S. Let's zoom in and let's just delete
the second one here. And at 3 seconds here, I'm going to move this perhaps to one and 500 in the position. Now we have this animation. So let's save this
and we can disable the DVU back to our set. Now we've prepared the UI, we can start
modifying the code to create a score
calculation method. So now let's go to
the scripts folder. And first thing,
first, I'm going to open the UI manager script. Here I have the UI
Manager script open. I'm going to create a
couple of variables. But before creating
the variables, I'm going to create
a new header, and I'm going to call
this score properties. For these score properties, we need to create a couple
of text mesh pro variable. Let's just create the first one. For the first entry, I'm going to add the
enemy kill text. I've typed this wrong here, so it should be a
serialized field. Let's just copy
this line here and I'm going to paste this
a couple of times. Now the second one would
be the hostage kel. And the third one
would be the shots. And the fourth one
would be the if, and the fifth one
should be the accuracy. And we want to also create a new serialized field with
a type of game object, and this would be the end
screen panel, and save this. Here below, we want to create a new public method to show
those end screen values. Let's just create
a new public void, and let's just call
this show screen. With this method, we are going to pass a couple of variables. The first one would
be the enemy kel. The second one would
be an integer also, but this is the total enemy. The third one would be also
an integer, hostage kill. The fourth one
would be the shots, or we can just call
this total shots. And the fifth one shod
be the total hit. Now inside this method, first, we want to enable
the N screen panel, so we can just run the
set active method and then pass a through
as the argument. For the enemy kilt text, we want to set its tax to the
enemy kel value divided by the total enemy and we want to multiply this by 100
to get the percentage. Multiply this by
100, and after that, we want to convert
this two A string using the two string method with a two digits argument here, and then we want to
add a percentage sign. But since both of this
value is integer, this will produce an integer. So we need to cast one of
this value to a float. So we will have a
fraction value. Then we can use those
value to be multiplied by 100 to get the
percentage of our enemy kl. So I'm going to cast the total enemy here
to a float value. And for the next tex, I'm going to modify
the hostage kel text. And we can just pass the hostage keel and
convert this two a string. Then for the total shots, you can just grab the
shot variable here, and then set its tax
to the total shots, convert this two,
for the hit tax, we can just modify the h
variable, set its tax, and then pass the
total parameter here, and then convert
this two a string. The last one, we want to
modify the accuracy text here. By using the same formula
with the enemy keel here, we want to divide the total hit divided
with the total shots, but we need to cast the
divider into float, and then we want to
multiply this by 100 and then convert
this two a string. And then add this with
a percentage sign. I need to convert
this to a string. Okay. Let's just check
for parent to C here. Okay. Let's add a parent
to C here and see. We are one parent, so I've added, and now it's
correct and save this. Okay, the next thing
that we want to modify is we want to go to the gay manager and
inside the game manager, we want to create a
private void here. I'm going to create a
new private void below. And I'm going to call
this show screen. And for the end screen, we want to run the UI manager, and it's show en Screen method. And we want to pass all
of this variable here. So let's just pass
all of the neme kill. I think, no, Enemy kill. Yeah. Total enemy, and
also the hostage killed. And for the total shots, it should be shots, I think, shots fired. And for the hit. I think yeah, it's enemy hit, and let's save the
game manager script. After modifying
the game manager, let's go to the player
move script here, and we want to create a new
method for clearing an area. So here we need to create
a new public void, and let's just call
this area clear. And above here, in the
variable area here, we need to create a
new private integer, and let's just call
this area clear. Inside this area cleared method, we want to increment the area
cleared integer variable, and we want to check
if the area cleared is equal to the shoot
out entries length. Then it means we've cleared all of the
area in this level. So we want to broadcast
an event here. We need to create that event. Here above, I'm going to create a new public static event with a type of
system that action. And I'm going to call this on level finish and insert
a empty delegate. And as I've explained it before, public static event is a very powerful feature
that can make it easy for scripts to
communicate with each other or one script to trigger another method
in other script. But one thing that
needs to be remembered, that static is a shared object. So when using static event, we should make sure that
the scripts are only attached to one single
object in our scene. If there are multiple script, then static won't work because it will only grab the first one, but it will ignore the other. So Make sure that use the
static event correctly. So now we have this
event created. I'm going to go back to
the area cleared method, and I'm going to
invoke this event. And then I'm going to
return this method. And if we have
still area left in this level means that
the statement is false, then we want to continue
the movement by running the set player
movement to true. Okay, now we've done with
the player move here. Let's go back to
the game manager, and we want to subscribe
this show screen method to the level finish method
that we've just created. So inside the game manager
under the I method, We want to subscribe the player move class here and the
level finish event, and we want to subscribe the show and screen method
to this event here. And on this able, we want to also remove
this subscription. So I'm going to copy this
line here and then change the subscriptions to
unsubscribing method. Save this. And now the last
thing that we need to modify is the shootout
point script. So let's just open the
shootout point script here. And now here below, whenever the enemy killed and we've killed
all of the enemy, we don't want to resume
the movement here, but instead of
resuming the movement, we want to run the
area cleared method. And with area cleared method, it will automatically resume the movement if there are still a shootout point
that we need to finish. After we modified the
player move line here, we need to also
make sure that we modified the one in here here. So in the set area clear, instead of resuming
the movement, we want to also clear the area, and this will be
called when the time is over on a certain
shootout point. So with these changes, even though we haven't
killed all of the enemy, we should able to progress to the next shootout point
whenever the times runs out. So let's just change this
to the area cleared method. And this way, we can
finish the level, even though we haven't
killed all of the enemy, but at a risk of a lower
score. So let's save this. Now let's head back to unity
and set up the end screen. And once the script compiled, we can select the game manager, and under the UI manager of
our game manager script here, we have a lot of new
fields declared. So let's just drag
the end screen as the end screen panel here. And if we expand all of the
child game object here, we can drag the value of the hostile kilt as
the enemy keeled here. And for the hostage kilt, drag its value to
the hostage kt. For the shots, drag the value
of the shots fired here. And for the shots hit value, we can direct this
to the hit slot, and for the accuracy is
direct to the accuracy slot. And let's save this. And
now let's give it a try. So now we have this
ready tax. Oh, sorry. We forgot one thing
that by default, the animation that we've
created is sets to looping. So we want to select
the ready tax animation and disable the loop
time total here. And then let's test this again. Let's try to kill
one of the hostage. And let's wait for the
enemy to show up and let's kill this one. There you go. We have killed 75% of the enemy. Okay, there is an issue.
I need to check this out. There is a race condition
where when we are showing the end screen and then start calculating the percentage
of the total enemy, not all of the enemy kill
has been registered. So to fix this in
the game manager, we need to delay
this method here. To make sure that all of the integer variable,
which is the enemy killed, total enemy hostage killed, shows fire, and enemy hit, has been counted correctly and make sure there are
no race condition. So let's just use the this
delayed action extension and then create an
empty delegate here. And let's just cut this code here and then
paste inside this delegate. We are going to delay this code here by a very small value. For example, 0.2 second, and then let's clean
this code a bit. Save this. And now
let's head Unity. And I'm going to change the
Inspector debug so we can see all of our integer fields here on the game manager
and then press play. So let's just pick
up the weapon here and let's start shoot the enemy. Let's wait for this enemy here. So far, we are 100%
of all of the enemy. And let's just finish
this up. There you go. We have the correct of
percentage of enemy kill.
32. 31 Bug Fixes part 1: In this video, we
are going to fix a couple of things
and polish the codes. There are issues so far
in our project here. So now let's inspect
what are the issues. So I'm going to play this
project, and let's take a look. Okay. The first one would be whenever we killed
an enemy here, you see that we have this error. And basically, this error, it's telling that get remaining distance can only
be called on an active agent. So somehow, the enemy scrip,
it's remaining distance, but with the enemy
already been killed, this is causing an issue. So we need to fix this. And let's just continue this. And the other thing
that we have this, the hostage or the enemy
is visible on start. So we want to make sure that we hype all of the enemy
and hostage on start. So in the third
shoot out area here, when we arrive to this position, We shouldn't be able to
see the hostage first. And when we stop,
then the hostage shook appear and then
start entering the screen. We are going to also fix that. And the third one, it's regarding the total
enemy counting. On our shootout point here, we are basically
counting the total enemy here to compare whenever we
killed all of the enemy, then we want to clear the area. But there is an issue here. Now let's test this out and take a look at the
total enemy variable here. As you can see here, on start, we have total enemy of one. But actually, we have
two enemies here. And the second one
gets registered a bit later after the first
enemy start moving. So we want to make sure
that the total enemy get counted right away when the shootout point
initialized or started. So we need to fix that also. So here, I've created a list. So basically, the enemies and the hostage should
be hidden on start. And when the enemy killed, there is this error
regarding getting remaining distance of the
NAF Mash agent component, and we should make sure that the enemy counting
happens earlier. So now let's just fix all
of these things here. Let's go to isle Studio. Here in the shoot out
point script here, we want to hide all of the enemy and the
hostage on start. So in order to do that, let's
just create a start method, and then let's look through
the enemy list here. So let's just call
the variable enemy and the collection,
the enemy list. And on all my inside
the enemy list, we want to hide the game object. Let's just access the enemy, and let's access the
enemy game object here. But since the enemy
is an enemy scrip, then we need to access
the game object and then access the set active method
and then set this two falls. After we are setting the
enemy two false here, on start, let's just
copy this line here, and we need to make
sure that we want to activate the enemy inside the send enemies
co routine here. So whenever the enemies
gets initialized, we want to also activate it. So let's just paste
the code here and then set this value true. And the other thing
that we want to modify is inside
the enemy script, we want to make sure that all of this initialization
happens on OA. Because with the changes
done in the shootout point, we are enabling the enemy
game object and initializing the init method of the enemy
script in the same frame. This can cause race condition, and it might skip the behavior
setup method from running. If at the time, the agent
has not been grabbed yet, causing the agent
values still null. So this way we are make sure
that this happens first, even though the
object is disabled. For the counting
of the enemy here, we can just copy this line
here or cut this line, and we can paste this line
here inside the start method. So let's just paste it here. We are going to count
the total enemy when the scenes start.
Let's save this. The next thing that we want
to fix is this one here. In order to fix this, we need
to go to the enemy script, and this is happens because the shoot protein
are still running, and this code here
gets evaluated, even though the enemy
has already been killed. So now we need to make sure that we run the stop shooting method, which are going to stop the co routine inside
this enemy script class. So let's just run
this stop shooting method inside the dead behavior. So I'm going to add it here, stop shooting, and save this. And now let's go back
to Unity and test this and see if all of
the bugs are fixed. So I'm going to save
the scene also. And I'm going to
set the delay back to three or four and say this. Now let's take a look at the total enemy value
here if we press play. Now when we press play,
it counts right away. We have two total enemy. Now when the enemy appear, we can kill both of them or we can just
shoot this barrel, kill using the explosion. And now you'll see that we don't have the hostage anymore. But when we enter
the Shota point, the hostage arrive and we can
just kill the enemy here. And now, if you
take a look here, we don't have the get remaining
distance error anymore. So we fix all of the issue regarding the enemy here.
Let's just stop this. There is one thing that
we need to fix here, a very easy fix
regarding the UI. Let's just expand
the main camera here and select the
payer HUD here. If we go to the
game view and then we select the main camera, I'm going to change
this back to normal, and let's just enable the back option and
let's just scrub this. As we move here, you'll
see that our UI gets clipped by the wall in this
area, this particular area. Let's just modify this here. And basically, we need
to make sure that the HUD is much closer to the
camera. Let's just move it. I'm going to change
the z position to 0.2. And I'm going to scrub the x
position so we can see it. There you go and
scrub the y position. But now it's very big. So we need to make sure that the scale is much
smaller than before. Let's just try a tenth of it. And now we need to scrub
the I again to grab it. Let's take a look
inside the editor here. There you go, so I'm going
to move on the x axis. Let's change this to
local and move it here. We have this pros enabled. I'm going to disable the
prog and this manually. And we can use the scrub
here to see the UI. Now we have this UI here
and it's quite small, so I'm going to set this 20, and then I'm going to make sure it is much closer on the z axis. Make it slightly further
and scrub position. Let's make this 23 or four, negative 0.0 45, and let's
make this negative 0.08. I think this is a
good position here. Now let's just select the
main camera and let's just scrub the preview distance
under the debug option, so we can see if our UI
are clipping an object. Because it's very close
to the camera right now. It's visible all the
way to the end here. Yeah. The should
fix the UI issue. And let's just save this. So yeah, we fix all of the small bugs inside
this project here. And after this, we are going
to work on the audio system.
33. 32 Custom Mouse Cursor: In this video, we
are going to create a crosshair to replace
our mouse cursor. Let's just go to the
scene view here. And under the game manager, we want to go to the
Canvas game object, and we want to create
a new image object. So I'm going to right click here and then go to the UI menu, and then I'm going to
create a new image. For this image, I'm going
to call this crosshair. And I'm going to set its
width and its height 250. And for the source image, I'm going to pick the
UI cross hair that I've prepared in the UI package.
So let's just pick this. And if you zoom in here, you see that we have
the cross hair, and I'm going to change
its color to red. So let's just go here and
I'm going to pick red. And now we have created
this crosshair. We need to make sure
that the crosshair is on the most bottom order
in the Canvas child. This will get rendered last. And now let's open the
game manager script. Here, inside the
UI manager script, we need to create
a new variable, and I'm going to declare one with a serialized
field attribute. For the type, I'm going to
set this two transform, and I'm going to call
this a crosshair. And here inside the init method. We want to check
for the cross hair. If the crosshair is not null, then we want to hide our mouse. So we can just type cursor, the ysible, and then set
this value to falls. And save this. And
now here below, we want to create a new method
to move the cross hair. So let's just create
a new public method, and I'm going to call
this move cross hair. And we want to pass a
vector tree as an argument, and let's just call
this mouse position. So here, basically,
we need to check if the cross hair is not Null, then we want to
move the crosshair. Let's just type cross hair, and then we want to
access its position, and then we want to apply
the mouse position that we pass through this method
here as an argument. Now let's save this, and
let's go to the game manager. And here inside
the game manager, We need to create a
new update method. So let's just create a new void, and let's just call this update. And basically, we want to run the UI manager method
that we've just created, which is the mood cross hair, and then for the position, we can just simply pass
the input mouse position. And let's save this. Okay.
Now let's head back to unity. Once the code compile, we should have the cross hair parameters in our inspector, and now we can drag the
crosshair game object and put it in the cross hair slot of our game manager script here. Now let's save the scene here, and if we go to the game view
and let's test this out. You'll see that upon playing, the mouse is hidden and we
can move the cross hair as we move the mouse and we can shoot exactly at where
the cross hair is. So that is how we create a custom cross hair or a
custom mouse cursor in unity.
34. 33 Looks Development: In this video, we are going to develop a looks
for this game here by modifying the post
processing filters and also modify the
materials in our scene here. So now let's start. Here, I've import
a new texture and material for the robot enemy
and also the Robot hostage, and I'll put the package. And now let's replace the material to our enemy
and also the hostage. So first, I'm going to
go to the prefet folder and open the enemy prefe here, and I'm going to go to the updated folder under the Robot Kyle folder
here inside the models. And we have two new materials, which is the robot updated
and robot hostage updated. I'm going to drag the robot
updated to this enemy here. And as you can see, we have
a more shiny material, and this will look
better once we apply post processing
in our sine. And now let's go back to the
prefs folder and I'm going to open the hostage
prefs and this time, let's just drag the
updated hostage material here. And there you go. Okay, so now we have modified both the enemy and the hostage. Let's go back to our scene here, and let's create
a custom sky box to replace this
original sky box. So I'm going to go to the materials folder and I'm going to create a new material, and let's just call
this custom sky box. For this material,
I'm going to change the shader into the sky box one, and under the skybox, I'm going to pick a procedural. I'm going to change a
couple of settings here. If I go to the game view here, I'm going to change the
ground color to be as close as possible with our
grid object here color. Let's just use this color picker and then just pick
the color here. In order to apply
this custom skybox, we need to open the rendering
and lighting settings menu. And I've already ducked my
lighting settings here, so I can just drag this custom skybox to
this skybox material, and it will change our
skybox, as you can see here. So now we have apply the
skybox to our scene here. We can modify the
sky box further by going to the
inspector and then use the color picker or
the color swatch here to modify the value bit. So it's a bit seamless
with our ground. Yeah, I think this
should be okay. And now for the sky tin, we can change to a certain
color if we want to. I'm going to pick a slightly purple blue and purple
ish color over here, and then change the
color to a darker one. For the atmosphere thickness, I'm going to decrease
this value until we have a darker sky.
Something like this. We can change the
exposure if we want to. I'm going just to set this to the default
value, which is 1.3. And now we have the custom Skybox applied and
set up properly. Let's create the post
processing profile. To apply the post process, we need to go to our main camera game object
in the hierarchy, and then let's add a post
processing layer component. Here in the post
processing layer settings, we need to change layer to a specific layer
that we want to use. Here, we already have a
post processing layer, and if you don't have it, you can always go to the layer options here and then press at layer and then
create a new layer. But since I've already have this post processing layer,
I'm going to use this one. Go back to our main
camera and I'm going to change this to the
post processing layer. For the anti sing, I'm going to change
this to temporal ants. Now we've set up the
post processing layer. We need to create a post processing object to be applied to
our camera here. We need to create a
new anti game object. And I'm going to call
this post process. Let's just zero
out the transform, and we can add a post
process volume component. And we need to also
change the layer to post processing and make sure
that is global is checked, so the post process folum will
apply the scene globally. Now we can create a
new profile here, but I prefer to create
on our assets folder. I'm going to go to
the game folder here and I'm going to
create a new subfolder, let's just call this
process profile. Inside this post
process profile, I'm going to create a new
post processing profile here. I'm going to call this
game game play PP. Now we can assign this to our
post process object here. Let's just direct this
gameplay post process inside this profile slot here, and now we can add effect. The effect that I'm
going to add are the ambient occlusion
and also the blue. The next one would be
the color grading. I'm going to add a
screen space reflection, and also a fine. Now we can modify the post processing
volume, each of rights. First, we want to enable
the ambient occlusion. Let's just enable the mode
here and then the intensity. Also, we can enable the thickness
modifier if we want to. The next thing that we need
to change is the intensity. As you can see here, the
intensity default 20, so we need to
increase this value and you'll see that we have a ambient occlusion
applied to our scene. I'm going to make this
value a very small one. Perhaps around 0.5 or one. Probably less than
one would be better. Let's just set this 20.75. And we can see the
difference whenever we disable and enable the
ambient occlion here. The next one that we want
to set is the bloom here. I'm going just to enable the intensity and the
threshold of the bloom. Now we can increase
the intensity here. But we don't see any
bloom effect because our material doesn't have a
high range MIF value here. So let's just test this out by setting the
intensity first 24, and we can decrease
the threshold. So let's just lower as
we lower the threshold, you'll see that the bloom will affect more and more
color in our scene. I'm going to set
the threshold to 0.8. This should be enough. The next one that I want to
set is the color grading, and I'm going to set the mode here to high
definition range. And for the tone mapping, I'm going to set
the mode to ACS, and we will have a very darker
color of our scene here, but we can compensate this
with the post exposure option. Let's just enable
this and increase the post exposure to around 0.5. And we will have a much
more contrast picture. And if we want to,
we can also enable the track balls here by enable the lift, the
gamma, and the gain. I'm going to enable the
lift and the game here. And I'm going to
change the lift, which is the dark area
to a bluish color. And for the gain,
I'm going to change this to the opposite
direction of blue, which is the orange color to balance out the color
of our scene here. So this is what we have so far. And if we want to have much more glowing lines
here in our grid object, we can go to the scene
here and select one of the cube object and modify
the materials on it. So I'm going to go to the emission options here
and then press on the, the HDR color, and I'm going to increase the intensity to 1.5. And as you can see here, as I increase the intensity, now the lines are more glowing. Okay. And as you can see here, we have the player HUD UI also applied by the
glowing filter, but we don't have this ready and hostage kilt
applied by the bloom filter. B for the canvas, the main canvass here is set
to screen space overlay, and for the player HUD, the UI render mode is
set to world space. So that's why this is affected with the post
processing filter. So in order to make sure that this canvas is also affected, we need to change the render
mode to screen space camera, and then we need to pick our camera as the render
camera of this canvas here. So let's just direct
the main camera to the camera slot here. And now we cannot
see the UI anymore because it's very far away in the distance
from our camera, so it's actually behind
of our game object here. So I'm going to make this a very small valley, which is 0.1. And you see that our
UI is messed up. So in order to refresh this, we can just disable
and enable this. And as you can see, we
have an updated UI, and it's glowing now. And because we are changing the UI render mode to
screen space camera, the custom crosshair
won't work anymore, so we need to separate this
object into a new canvass. So I'm going to create
a new UI canvass here. Let's just call this
cross hair Canvas. Let's put the cross
hair game object under the child of this
cross hair canvas. Let's set this canvass
into a scale with screen size under
the Canvas scale and set this to a
full HD resolution, and set the match 20.5. For the crosshair, we can
set the scale back to one, and let's just zero
out this value here. So we can see it
here in the middle. And the other thing that
we need to modify is the hostage keeled tax because the way we show the
hostage keeled tax, similar to the crosshair. So in order to fix this, we need to put the hostage
kilt also in this canvas here. So let's just drag this. And
put it above the cross hair. So the cross hair
gets rendered less. So this should fix the
issue with the UI. Another thing that
we need to modify. We need to make sure that
the hostage keel transform is back to the
default value here. So let's just select
the hostage keel, and as you can see, we have all sort of numbers in the position
rotation and the scale. So I'm going to set the
position back to zero. And for the rotation,
I'm going to also set all of the
axis back to zero. And for the scale, we want
to set this back to one. Because before the
hostage keel text was on this screen space
camera canvas, and it has different values
for the direct transform. When we switch back to the
screen space overlay here, the direct transform
value gets transferred from this here to the
crosshair canvass. So we have different
transformation. And that's why we have all of the wrong values in the transform of our
hostage keel text. So now let's save the scene, and let's test this out. And as you can see here, we
have a working cross hair. And now let's take a look if the hostage kel are positioned in the correct
position on the screen. Okay. And now we have
another issue here. If we shoot the enemy, you see that we don't have
the electric effects. With the hostage, we still
have those electric effect, but with the enemy, we don't
have those effect anymore. And this is a quick
fix actually. We need to go to
the prefe folder and open the enemy prefs. And here, if we
select the enemy, we need to move the hit effects to be on top of the
enemy script here. So let's just move this up and make sure that
the hit method inside our hit effects
also gets executed whenever the enemy receive
a shot or a damage. And now let's go back to our scene and test
this one more time. And I'm going to try
to shoot the enemy. And there you go.
We have the effect. And let's just shoot
this bomb here. And pick this weapon up
and kill this enemy. Yeah. Okay. So everything works good so far, and with applying post
processing filters, you can see that we have a
much better looking game. So I encourage you to experiment with the
post processing filter and also the material settings
to achieve better looking.
35. 34 Enemy Upper Body Fix: This video, we are going to fix the enemy upper body motion. And here, if I press plea, and you'll see that
currently our enemy when moving has a strange
motion on the hand, the hand holding the guns. As you can see here. It's weird. And we also want to fix
the dead animation, sometimes didn't get
triggered correctly. So let's just do that.
I'm going to stop this. And now let's go to
the animator window, and if the enemy
animator is not open, we can just go to the
animations folder under the controller and
then double click the base enemy controller. And now here in the
base controller, first, I want to change the transition for
the dead state here. So I'm going to
change the condition to use the inside of
the dead trigger. We are going to use
the dead bulion, and set to true. And under the settings, we want to disable can
transition to itself. So let's just disable this. So the transition only happens once when
the enemy is dead. And then we want to create a new layer for the upper body. So I'm going to call
this upper body. And for the settings, we want to increase
the weight to one, and for the mass, we want to use the upper body mass that we've
created for the hostage. So let's just use
this. And here, we need to create
three new state. So I'm going to call this idol. I'm going to duplicate this, and the second one will
be the hit reaction, and the third one
will be the den. Since we are overriding the upper body part
of the animation, We need to make sure that we have each animation
for each condition. So if I go to the
base layer here, you see we have three states, and we want to replace all
of the upper body motion inside the blend tree with
this idle state here. And for the hit state, it's going to be
synchronized with the hit state of the
upper body area, and the dead state will
also be synchronized with the dead state of the
upper body layer here. So now let's just create a
new transition from any state to the hit state and also from any state to
the dead state. And we want to
create a transition from the hit back to idle, just like our base layer here. So now inside the idle state, we want to use the rifle
aiming idle animation, and for the hit state, we want to use the hit reaction. For the death state,
we want to use the death from headshot. And now for the transition
settings, for the hit, we want to set a condition whenever the enemy is shot or the shot parameters
gets triggered. And we want to set if the dead
bulion is currently false. Meaning that the
enemy is still alive, and as you can see here
in the base layer, we have the same
transition settings. So we want to make sure that the transition settings is the same as the base layer one. So this is already correct. And we want to make
sure that from the hit to idle is
using the exit time. So this is also already correct. And now for the death, we want to make sure that I that Ban triggers
this animation. Let's just choose is that Ban
and then set this to true. And then we want to also disable the transition to
self options here. So now we have set up
the animator here. I'm going to save
the project one more time to save all of the
changes in our assets. And now let's just
double check. H we put? We already put all
of the animation, the correct animation
clip. Let's test this out. And let's take a look when
the enemy are moving, it should have the correct
upper body animation. Okay. There you go. It's much better, and
we can kill this. And as you can see, that
animation is more reliable now, because when I was
testing before, sometimes it gets triggered, sometimes it doesn't
get triggered, or maybe there is frame skip whenever that trigger
gets triggered. So now we have the
correct animations, and it is much better
when moving sideways, the hand position that
holding the guns. So, yeah, that is how we fix
the enemy upper body motion.
36. 35 Audio Library Setup: In this section, we are going to work on the audio system. And in this episode, we are going to define
our audio library script. So in order to do
that, let's go to the underscore game folder
and go to the scripts folder. And here, I'm going to create a new scriptable object for holding the data of
our audio library. So let's just create a new CHO script inside the
scriptable object folder, and let's just call
this audio library. Another script that we need to create is the audio data holder. I'm going to create
a new CHR script, and I'm going to call
this audio data. And let's open both
of the script here. Now I have the Audio ta and
the audio library open. I'm going to work on the
Audio library first. Here in the Audio library, we need to change the base class into a
scriptable object, base class. And we want to also
create an asset menu. So let's just add the create
asset menu attribute, and then for the argument, we want to set the file
name to Audio Lib. And for the second argument, I'm going to set the menu
name to Audio library. And I'm going to delete the
start and the applet method. Now we want to define
the audio data that we can populate inside
the audio library. Here inside the audio data, let's just delete the base
class here because this is going to be a custom
class for holding a data, and I'm going to delete
all of the method inside. The first thing that
I want to create, I want to create a
serialized field, and this will be
a type of string, and I'm going to call
this field audio name. And for the next method, we want to create a
array of audio clip. Let's just create
audio clip array, and let's just call
this audio clipse. And the other thing
that we want to set up is we want to create
a new float variable, and this is going to
be the minimum pitch. And the second one would
be the maximum pitch. We can set a default value here. Let's just set this 20.9 float. And for the maximum pitch, let's set this 21.1. And we are going to
use this variable to randomize the pitch of our audio whenever we
are playing the audios. Thus it will make the
sound more dynamic. And since all of the
field is private, we need to make a public getter. I'm going to define the
public getter below here, as this type public, and I'm going to create
the audio name getter. And this will return
the audio name field. I'm going to also create
the audio clip gather, and this will be the single
audio clip, not an array. Let's just call this
Get audio clip, and we can return
the random clip from the audio clips array. Let's just type audio clips. For the index, we can use a random range and for the
minimum should be zero, and the maximum would be the
audio clipse dot length. This will grab the
audio depending on the number of member
inside this audio clip array, and it will pick randomly one of the audio
clips in this array here. So we can define more
than one type of sound for a gunshot,
for example, and it will play randomly
different audio, and also it will play with
a different pitch here. The last one that we
need to set up is the float getter for
the maximum pitch. And here, let's just
call this get pitch. This is the net
thing with Getter. We can create a
simple expression for returning some data. With the Audi clipse, we
can return random clips, and with the get pitch, we can return a random value between this minimum
and maximum pitch. Let's just use a
random range method, and then pass the minimum pitch. As the minimum value and pass the maximum pitch as
the maximum value. Okay, now we are done
with the audio data. We can go to the audio library, and we can create an array of this audio data that
we've just created. So let's just define
a serials field here, and I'm going to create
a new audio data array. And I'm going to call
this Audio list. Inside this scriptable object, we want to create
a public method for returning the audio
data by its name. I'm going to create
a new public method, and this will return
an audio data. And I'm going to call
this get Audio by name. And here we want to pass
the name as the argument. Inside of this method, let's just create
a new audio data. Let's just call
this value and set this to null by default. And we want to look through
the audio list here. Let's just call the
variable member audio. And for the collection, we
can just pass the audio list. And we want to check
if the audio name, it's equal to the name that we are passing
here as the argument, then we want to fill the value with this audio here with the
correct audio here. And then after that, we want to return the value. So with this method, we can just pass a string of the audio name, and this method we return the correct audio
data value based on its name because we
declare the name here. So it will compare
the string with this field here. Okay,
let's save this. And now let's head back
to Unity and create the audio library object
inside our data folder. I'm going to go to
the data folder here, and I'm going to create
a new audio library and just call this audio lab. If we select this, you'll see that we cannot
see the variable yet because we need to make sure that the audio
data is serializable. I'm going to add a
attribute system that serializable
here and save this. Now if we go back to Unity, we should be able to see
the data. There you go. We have the audio list, and
we can expand the size. So for example, I
can set the size 21, and here I can
fill out the name. So for example, this
would be the gunshot. And we can expand
the audioclipse, and then we can fill
the audioclipse. And I don't know why
the minimum pitch and the maximum pitch is set 20. So let's just set this manually. I'm going to set
this 20.9 and 1.11. And I've imported a couple of sound inside the audio here, so we can pick one of the audio. For example, this
sci fi gunshot, and we can pick this one here. And this will be the first
audio data that we have, and for the second example, we can expand the size here, and it will duplicate
the first one, and we can change this value. For example, let's say let's
just call this electric ZP. Sorry. Should be electric ZAP. Then for the audioclipse, we can change this to the
electric crackle here. So now we have set up
the audio library. In the next video,
we are going to continue to implement the audio.
37. 36 Audio Player Setup: In this video, we are going to continue work on
the audio system. And in the last video, we already create the
audio library. So now we need to create the functionality
to play this audio. So let's go to the Asset folder and inside the scripts folder, I'm going to create
a new subfolder. And I'm going to call
this folder Audio. And I'm going to create a new Cc and I'm going to
call this audio player. And let's open this
audio player script. First, I'm going to delete
the applic method here. And then I'm going to create
a new serialized field, and this will be the
type of audio library, and let's just call
this Audio ib. And we want to make sure that this script here is available
globally in our project. So I'm going to create
a static reference, a public static, and the
type would be audio player. And I'm going to
call this instance, and this will be a public
getter with a private set. So only the script that
can set this value, but all of the other
script can get the value inside of
this static reference. So let's create a wake method. And we can fill this instance
value with the script here. Let's just pass this
keyword as the value. And we put this inside the ak, so we want to make sure
that this instance is available before any
other script start. Now inside the start, we want to initialize the audio sources that we need to play an audio
in this project. But first, we want to
create a integrer, variable, and this
is for defining how many sources that we
want to have in our sc. And I'm going to set
this to six by default. And we want to also create a audio mixer group for differentiate the audio sources for sound effects and
for music mixer setting. We need to use the Unity e s, Unity engine Audio, and let's create a
new serialized field. And this would be the
audio mixer group. And let's just call this
sound effects group. And the second one would be
the Background music group. And now the next thing
that we want to create, we want to create a private Q. And why I'm using Q because
it's much lighter than lest. Let's see the short
descriptions about Q. Q represents a first in first
out collection of an object. It is used when you need a first in first out access of items. So let's say we have a queue
of objects called M Q, and we fill it with an object. When you add an item in a list, it is called Q and Q adds an element to the
end of the queue, like this example here. When you remove an item, it is called the Q. The Que removes
the oldest element from the start of the queue, and to put the object
with the queue, we can just use and queue. And this will put
the object back inside of our queue
as the last element. The other way to
get reference to the oldest element
is by using pk. Pak returns the oldest element, that is at the start of the, but doesn't remove
it from the queue. And let's get back
to our project. For the type, it will
be an audio source, and let's just call
these audio sources. And let's initialize this. And now we want to populate
these audio sources with the game object that has audio source component
attached to it. Let's just create
a new in method. But before we continue
in the init method, let's create another
private audio source, and this one would be
a single audio source, and this will be the BGM source. So now inside this init method, we want to create a new
game object and then set that game object as either the BGM source
or the audio sources, and then make sure
those game objects are set to be the child of this
audio player game object. So first, let's just
create a new game object, and let's call this BGM object. And I'm going to create
a new game object and we can pass its name here. So let's just call
this BGM source. And for the BGM source
audio source field here or Audio source object, we want to add the Audio source component
to this BGM game object. Let's just type at component. From the game ob object
here at component, and then we can pass the type, which is the audio source, and this will create
a new audio source attached to this game object. And the next thing that we
want to set is we want to set the BGM source spatial
blend value 20, so this will be a two D sound. And we want to also set the
output mixer group value, just the output
audio mixer group, to be the BGM group. And the last thing that
we want to set is, we want to set this game object to be the child of our audio player game object. So let's just access the BGM
object, transform component, and then set the parent
to this object transform. And now we want to create the sound effect sources
inside a loop here. So we want to loop, and we want to loop this amount
of time here. I'm going to copy
this integer name and paste this as the length, and we want to run
this code inside six time based on this
audio source number, and we can change this
in the inspector, so we can have more audio
sources in our scene. So let's just create
a new game object. And this will be the
sound effect object. And I'm going to create a new game object
and pass a name, which is the sound
effect source. But I'm going to add
the index i plus one, so it will start from
one in set from zero, and I'm going to convert this two string with a
format of two digit here. The next thing that we want to create is we want to create a new audio source We can
just call this temporary, and then we want to add
audio source to this effect, not the sound effect group, but the sound effect object. We can use the at
component method and then pass the type, just like we did with the background music
game object here. Okay. So now we have
the audio source. We can set up this audio
source and then put this audio source inside the
pool of our audio sources. So let's just set
the T spatial bland one this time because we want
to create a three D sound. And then the next thing
that we want to set is the output audio mixer group, and this should be the
sound effect group, we want to also
set the parent of this sound effect object
to this game object here. Let's just access
the transform set parent method and then pass the transform of this
audio player game object. And the last thing that we
want to do is we want to put this audio sources inside
of our audio source Q here. So we can just access
the audio source Q, and then we can run
the n Q method and we can pass the T game object. So this will fill
the audio sources queue with the audio
source that we've just created inside
the sloop here. And if the integer value is six, then we will have
six audio sources inside our audio
source queue here. And now before we continue
on working the audio, we need to create an
extension method to play the audio data object here
via our audio sources. So let's just open
the extension script. I have it here. And I'm going to create a new public static. And return type should be filed. We can call this
play audio data. And we want to extend
the audio source. So we need to use
this keyword here and define the class or the object that
we want to extend, which is the audio source, and let's just call
this A source, and let's start the method here. Another thing that we want
to pass is the audio data. So let's just pass
the audio data here, and let's just call
this audio data. And now because we are
extending this audio source, we can use this to
set up the pitch and the audio clips and
play that audio clips. So let's just type A source and access it Pitch properties, and we can pass the audio data get pitch property
or access or getup. And this will return
a random value of the audio pitch that we've set up in this audio data here. And for the clip, We want to also assign this with the audio data Get audio clip. And this will return
the random members of audio clip inside the
audio clips array of our audio data here. Now after we set up two
different properties here, we want to play the audio clip. Let's just access the A source and then run the play method. Now that we set up this extension method
inside the extension, we can go back to the
audio player and then start creating a method
to play an audio. Here we want to create
a new public void. And let's just call this
play sound effects, and we want to pass
a string audio name because we are going to refer the audio using its name
instead of its clips. And if we open the
audio library, let's just open the
audio library here. You'll see that we
have this function, get audio by its name here, and it will return
the audio rata. We are going to use only
the name to play an audio. In order to do that, first, we need to create a
temporary audio source here. And we can get this audio
source from our queue of audio sources here
or the pull of audio sources inside
the Q audio source. So let's just type
audio sources, and let's just use
the D Q method, and this will return the earliest audio
source in our queue. And here we want to also pass
the transform of the color. So let's just call
this audio location. And we want to set
the position of our audio source to
this position here. So let's just grab the position
from the audio location. And then we want to
play the audio data. Because we extend this, we have display
audio data method now for our audio sources. So let's just use this and we need to convert the
string into audio data. Since we already have reference
to our audio lib here, we can use the method by typing the audio lib that
get audio by name, then we can pass the audio name string that we passed inside
this method here. Let's just use that audio
name. And close this. And this will play the
audio data by selecting its random audio clips and
also its random pitch. So we will have a different
audio every time, and this will make the
sound more dynamic. And then the last thing
that we want to do is we want to put the
audio source back to its pool by using the NQ and pass the
Tam audio source here. So we will shuffle the earliest audio source
before playing it, and after playing it,
we will put it to be the most latest audio
source inside our pool. And this will repeat over and over every time we
play sound effect. And let's save this. So
now we have this setup. We need to create a new custom we can create a string
actually for this, but I'm going to create
a new custom object to define the audio
that we want to play. And you'll see why I've created this custom object
in the next video. First, I'm going to create an attribute a
serializable attribute. And this would be audio getter. And inside this audio getter, we will have a public
string audio name. Okay, now we have this. We can open the weapon data. And inside this weapon data, we can create a new
serialized field, and the type so be
the audio getter. And let's just call this
gun shot sound effects. And now we want to play
this sound effect. Inside this fire method, I think it's Yeah, it's in the weapon
update method. We want to play the sound. So whenever we are firing, or we can just run the sound effects inside
the fire method because we have this fire method shared by these two different
weapon type. So let's just go to
the fire method here. And now we can access
the audio player that instance since we already
create a static reference, and we can run the play
sound effX method. And this a for a audio name. So let's just pass the gun
shot sound Audio name. And then we want to pass the
transform of our player. And since we already
have a reference to the player inside
this scriptable object, we can just pass this transform from this player component here. So let's just type
player do transform. So now we have a
working sound effect for the gunshot sound effect. We are going to expand
this in the later video, but let's just try to set
up this inside unity. So let's go back to Unity. And now for the audio player, I'm going to attach
this audio player to the game manager. So I'm going to add the
audio player component here. And we want to grab the audio library that we've created inside our data folder. So let's just select this option here and then
pick the audio library. And we can just leave the sound effects group and background music
group empty for now. So let's save the scene here. And now to set up the audio. We can go to the data
folder and check the audio lip and
remember the name here, the gunshot and electric zap. I'm going to copy
this name here. And we can go to
the default gun, and for the gun
shot sound effect, we can just paste the gun name inside
the audio name field. And for the machine gun, we are going to use the sound effect. In this example, but we
can change this later. Okay. So now we
already set this up. Let's test this out. I think I need to make sure that the audio is not too loud. So I'm going to go
to project settings, and I'm going to decrease
the volume to 0.1 here. And let's test this out.
There is an issue here. I forgot to run the init method inside the audio player. So
let's just do that. Go to the audio player, and we want to run
this init method. Let's type in it. And now let's go back to unity. Now if I press play, and if we pause this, you'll see that we have a lot of
game object below here. And this is for the audio source that we want to use to
play sound effects, and this is for
playing the music. Now if I press play, let's test. There you go. Yeah have sounds, and it's different every time. Yeah. It has different pitch. So now we have a
working sound effects, and the sound effects will be also dynamic, which
is a good thing. And we want to
simplify the way we pick the audio name here
using a custom drawer. So in the next video, we
are going to do that.
38. 37 Audio Getter Custom Editor: In this video, we
are going to create a custom property drawer
for our audio gator class. Right now, if we go to
the data folder and if we select one of the weapon
data scriptable object, either that de false
gun or machine gun, you'll see that
whenever we want to define the sound effects
that we want to use, we need to type the audio name. And this is prone to error or mistake whenever we are
typing the sound name. In order to make this easier, we need to create a custom property drawer for this class. In order to do that,
we need to create a new script for
overriding the editor. I'm going to create a
new subfolder inside the scripts folder and I'm
going to call this editor. And all of the CSR file that lies inside
this editor folder, will not be included
in the bill. So for every editor
functionality, we should be putting the scripts inside
the editor folder, and this editor folder can be created anywhere inside
our project folder. Here I'm going to create
a new CHR script, and let's just call this
Audio gather drawer. And let's open this. Now if we go to
the audio library, this is why we create a custom class for the
audio name string. Otherwise, we can always use a string directly
inside the weapon data, for example, for defining the audio sound effects
ID or the name. But now, since we want to overwrite the display
or the inspector, this is why we create
a custom class for it. So now let's go to the
Audio getter drawer and we need to delete all
of the built in method. And we need to use the
Unity editor library. So let's just type
using Unity Editor, and we want to replace the inheritance to
a property drawer. And here we want to add an attribute called
custom property drawer. And inside the argument, we need to add a type
of keyword here, and then we need to target the class that we
want to modify. In this class, we want to
modify the audio gather. With property drawer, it's
actually quite simple. First, we need to
override the method. So let's just type
ongoing method. And as you can see here, the ongoing method
have three arguments. The first one is a
rectangle called position. The second one is
serialized property, called property,
and the third one is Gu content, called label, and label is basically
the label of the name that we've
defined in other script. Whenever we are showing a field, label is the name of the field. Okay, basically, this
based on UI with the argument position
property and label is drawing the normal inspector, and if we want to
overwrite this, we need to delete this. So let's just give
it a try here. And for drawing a custom
inspector for property drawer, we need to access
the editor GI class. We cannot use the
editor U layout or the editor GUI utility. So here, for example, if I use label fiel,
As you can see here, it asked for a
position, rectangle, so we can just pass
this position and it will know where
to put this text. And for the second argument, we can use a UI content label or we can use a custom
string label here. So I'm going to create
a custom string, and let's just say that
we've modified do gether. And let's close
this and save this. Now, if we go back to Unity and after the
script finish compiled, if we go to the data folder
and select the default gun, you'll see that
there is a text here that saying we've modified the audio getter, and
we cannot change this. This is not we one actually, but just to show you how we modify the custom
property drawer. And now let's just remove this. And now we want to
create the prefix label. So I'm going to type position. And then I'm going to fill
this with the editor I, and we can use the prefix label. And this prefix labels return rectangular
class or object here. That's why we can fit
this to the position. And this will make sure
that this label gets drawn before any croised
property that we want to modify. Okay, so let's just
pass the position. And for the label, we can also pass the label. For the audio getter,
we want to create a drop down menu where we can list all of the audio names that we've populate inside
the audio library here. In order to do that, we
need to create a couple of modification on
our audio library and also on our audio getter. Inside the audio getter, I'm going to type
public integer ID, and this will hold the index of the list of string that we are going to create inside
our audio library. To make it easier to access the list of string inside
the audio library, we need to create a new
public list of string. Let's just create a
new public static, and the type would
be list of string, and let's just call
this Audio names list, and let's initialize this. I make this static in
order to make it easier to access this variable
from any other class because we need to
access those list of string from this custom
property drawer script. We want to change
this value every time we made changes on our
scriptable object. We can use the method
called valid date. Let's just type void valid date. This is actually a
built in method. Somehow it's not
marked as built in, but we can test this out. Let's just type the bug log. Let's just say that Audio library modified.
Let's save this. Now if we go back to unity, and let's select
the Audio library, and then go to the console here. If we try to modify one of the value inside the
audio inspector here. For example, let's change the maximum page
for the gunshot. You see that the valid
date gets executed here and the back log gets
printed in the console. So we can use that to modify static list of string that we've just created in
the audio library. So I'm going to
delete this here. I'm going to clear the audio name list and
run the clear method. And I'm going to look
through the audio list here. And let's just call this
audio for each of the member, and for the collection. We want to pass the
audio list collection. And here, we want to
fill the audio names list with the audio names from each of the audio
inside the audio list here. Let's just type audio audioam. This way, whenever we change the audio library
in the inspector, it will update the
audio names here. Save this. Okay. So now we've modified the Audio
gather and the audio library. We can continue work on
the Audio gather here. So to create a pop up
or a drop down menu, we want to use the
editor GY dot pop up, and for the pop up, it ask for a position, so we can just
pass the position. And the integer for
the selected index. And we can use this
ID that we created in the audio gator
because basically the value that are going to be safe for the pop
up is the integer. And using that integer, we can grab the string from this static
list of string here. But in order to access the integer that we've
created in the audio gator, we need to access
the property here, which is the series property, and then we need to use the fine property
relative method, and it asks for a string name of the relative property path. In this case is the variable
name, that's just type ID. And then for the last argument, we want to use a string array. So we can just type Audio, Audio library class and grab
the audio nameless here. But since this is a list, we need to convert
this to an array. So we can just use
the array method to convert this two and array. Here we have an error because fine property relative return
a serialized property. So we need to grab
the integer value. Here. And all of this code here needs to assign
into an integer value. In this case, we want to
assign back to the ID value. So I'm going to copy
this integer value here and then paste this in front of the RGI and then assign this. So whenever we
change the pop up, we want to grab the
new integer value and those integer value are going to be safe to
the ID integer value. So we are overriding it. And whenever we change
the pop up here, we want to modify the string
value of the audio name. So let's just grab the
audio name variable, Let's check if the
variable called Yeah, it's audio name, let's
grab the variable, and since this is
a seriS property, we need to grab the
string value and then assign this value using
the audio library, the static string of list, which is the audio name list. And for index, we can just pass the integer value of
our ID fields here. Let's just paste this here. And close this with Sam Colm. Now let's save
this. Now let's get back to unity and see if
this code is working. I'm going to clear
the console here and let's select
the default gun. And as you can see here, now we have a nice drop down for the gunshot
sound effect. So we can change this
to the electric z. And whenever we change this,
we don't have any error. So that is a good sign. And let's just give it a try. Let's go to the audio lip here, and let's add a new sound. And let's just call this
gun reload and change the audio clip to the
sci fi gun reload. Now, we've add this
new audio name. Let's see if the audio
name list gets updated. Select the default gun and
open the drop down here. And there you go, as you can
see, we have a new sound. Now let's give it a try
of our modification here. Let's change the gun shot sound effects to the electric
zap here and press play, see if this modification
works or not. Okay. There you
go. It's working. And if we change to the gunshot, you can see we've
changed the audio. And this makes selecting
sounds much easier.
39. 38 Optimizing Audio Workflow: In this video, we are going to continue to work on our audio. And there's a couple of thing
that I want to show you to make setting up the
audio much easier. And first, we want to change the reference to the
audio sound effect name. Instead of using a string,
we want to use the ID. So here inside the audio get
drawer, we don't need this. So let's just comment
this out for now. And then let's go to
the Audio getter. It's under the Audio library. And here we want to create
a new public getter string. So I'm going to change
this to audio name with capital A and set
this as a gator. And let's just return the Audio library
audio name list with an index of our ID. Okay, now save this. So now the audio getter
will save our index ID based on the pop up that we select when choosing
the sound effects, and whenever we want to
refer to the sound effects, we are actually returning the string from the
audio name list here. Now let's go back to Unity. And you will see that
we have an error here because we changed
the variable name, using a capital A here. And now if we double
click on this error here, you'll see that it cannot find the audio name variable,
because we changed this. So instead of using
the string name, I'm going to delete all
of the audio string here. And let's go to
the audio player. And we want to use
the audio getter. So this will make typing
the code much faster. And instead of using a string, we want to grab the string
from the audio getter here, so let's just type
that audio name here. And we can just rename these two audio SFX and change
these two audio S effects. So it's clearer. And save this. And now if we go back to Unity, we should have cleared
out the error. There is still an
error. Let's check this. Okay, save this. I forgot to save
the weapon data. So let's go back to Unity. And now I finish compile, it clears all of the error. And if we press play
and then test this out, You see, we still
have a working sound. And the reason that I change the Gether
drawer using the ID. Later, when we are populating
our audio library and then setting the name for each of the audio
data list here, we don't need to reassign the sound effects
on every entry of the audio gather or every
script that are using the audio gather as the audio
sound effects reference. And this way savor, and it will be less prone to error than the way we
are doing it before. And the other thing
that we want to do, we want to add an option to set the sound
effects as a two D sound. Currently, if we go
to the audio player, you'll see that all
of the sound effects, spatial blend are set 21. And for example, if we have a very low volume sound effects that we want to play
on a distance object, we cannot probably hear this. For example, a weapon
pick up sound effects. Probably that sound doesn't needs to be a three
D sound because it's not a sound that are related to the
immersion of the game, such as a gunshot or explosion. It's more a audio
feedback for the game. We can set the sound 2a2d sound. So here, under the
play SF X method, we want to add a default value for the audio location
and set this channel. So with this set, we can just
ignore this parameter if we don't want to pass a
transform of the audio location. And here we want to create an if statement if the
audio location is not null, then we want to set
this audio source to be A three D audio. So let's just cut
this line here where we're setting the position and paste it inside
the I statement. And then we want to set the
T spatial bland back to one. And we want to create
an L statement. And this is when we are running this method without
audio location argument. So here we want to set the spatial blend of our
audio source back 20. So this will be a two D
sound. And let's save this. Another thing that
we want to create, we want to create a method to
play the music here above, we want to set the audio source
for our background music, loop option to be true, so let's just type BGM source
loop and set this to true. For playing music, let's just
create a new public method, written type of void, and let's just call this play music. We want to pass the
audio gather also, and let's just call this music. But we don't need
any other parameter. Here, we can access the
BGM source right away, and we can use the
play audio and let's use the audio library, get audio by name method, and we grab the string from
the music Audio Audio Name. And here we want to make sure that the pitch is
set back to one. We want to ignore
the random value of the pitch here
because we want to play the music at its
original pitch and safeties. Let's go back to Unity
and see for any errors. There are no errors,
so it's a good sign, and in the next video, we are going to start
populating the audio
40. 39 Applying Sound FX: In this video, we are
going to start assign the sound to every object in the scene that
needs the sound. So now let's populate our
audio reliability first. But before I'm going to show you settings for our audio here. So if you have imported the audio files in
the previous lesson, you'll see that we have
a couple audio files inside our project here. So for all of the audio effects, I'm going to select
all of them together. And I'm going to make sure
that the load type is set either to decompress load
or compress in memory, and we want to set
the quality 290. And for the compression format, for this should be okay. So let's just press apply. And you see that right now, the original size
is 1.7 megabytes, but the imported size
is 400 kilobytes, so we already save around
75% of the original size. And for the audio
music, the BGM file. We want to set
this to streaming, and for the quality, we can just leave it 100 and press apply. Since this is an Petry file, there are almost no compression, but with streaming, the Petry file wouldn't
be fit into a memory. It will be streamed from a disc. And basically with a
streaming load type, we need to make sure that are only one audio source that
are streaming from the disc. Otherwise, it could cause leg, especially on mobile devices. So Streaming usually
are used for music because usually we only have one music
in our scene. Now let's go to the data folder and set the audio library, and I've already created
a couple of entry here, but I'm going to
populate this with the rest of the audio that
we have in our project. So I'm going to add maybe 212, and then I'm going to
set this one by one, and then I'm going to
speed up this video. And for BGM, let's just set the minimum pitch and the
maximum pitch to one, so we would have
pitch variation. And now I've already set
all of this up here. I'm going to save the project so the scriptable
object gets saved. And now we need to create
a couple of scripts. So let's go to the scripts
folder under the Audio. I'm going to create
a new CHR script called play Audio action, and the other one should
be the play music action. Now let's open the play
Audio action script. Here in the play
audio action script, we want to delete all
of the built in method, and let's define
the variable first. Let's create a serialized field, and for the type, this
would be an audio getter. And let's just call this
audio sound effects. And the second option
will be a bulion, and I'm going to call
this two D sound. And this is for
enabling or disabling, whether we want
to set this audio as a two D sound or
a three D sound. And the last one
would be a delay. So we want to add, sorry, not delay, should be flowed, and let's just call this delay. So we want to add a feature where we can delay
playing the audio. So now let's create a new
void on enable method. Why are we using enable method? Because enable gets
triggered when the object gets activated and can
run multiple times. Since some of the objects in the scene are de
activated on start. If we use start method, we might never hear
the audio playing because the start method
has already been executed, and when activating
the object later, the action won't gets executed. Here, inside the
unenable method, we want to use the
delayed action that we've created
in our extension, and we want to pass an
action using delegate here, and then after
passing the delegate, we want to pass the delay here. Let's just pass the delay
and then close this. Now we can type our code
inside this delegate here. So inside this delegate action, we want to access
the audio player, and then the static reference, and we want to play
the audio sound effx, using play SFX method. And then we want to
pass the audio geter. So let's just pass
the audio Sound effx. For the location, we want to use this bleion to determine whether we are going to
pass a transform or not. So let's just type to the sound, and then question mark. And if it's true, then we want to pass Null. But if it's false, then we want to
pass this transform of the object the
script are attached to. Now, basically, if we
enable the d sound, then the sound will be a D
sound when it's playing. But if it's disable, then it will be a three D sound. For the play music action, we can just duplicate
all of the script here. And I'm going to open this. And here, I'm going to paste the script that
we've copied before, and we are going to
create a modification. Let's just delete
the bullion here, and we can remove
the second argument, and we want to run the
play music method. Okay. Let's save this, and let's head back to Unity. Now, we want to open
the weapon data script, to add the gun MT and the
reload warning sound effect. Let's just open the
weapon data here. And if we scroll up here under the gunshot Sound effect
Audio gaor field, we want to add a couple
more variables here. Let's just add a reload effect, the empty sound effects and
reload warning sound effects. And here, if you
scroll down below, we want to add a sound
whenever the MO runs out. But we want to play the sound only if we press
the mouse button. So we are going to change this statement to
an if statement. So let's just type
input the GT mouse button down and we want
to grab the left click. And here we want to
add a condition if the MO is less or
equal than zero. If it runs out, then
we want to play the empty sound effect. Let's just access the
audio player instance and the play sound effX method. For the name, we can just
pass the empty sound effect. And for the location,
we can just use the player script reference and access the
transform component. Let's just copy this line
here and paste it here below, and I want to also play the
reload warning sound effect. And we can copy this
line here and then paste it on the
machine gun code here, and we want to also
check this statement. So let's just copy the first
two condition and paste it here below and add the end sine. We create a similar condition, but with the current O equal or less than zero here
below. Save this. Whenever we reload the MO, we want to play
the reload sound. Let's just copy this line
here, and here below, I'm going to play the reload
sound effects and save this. Now let's open the
player script. Here on top here, we want to add a new
serialized field, and the type should
be audio gather, let's just call this
damaged sound effects. And basically, we want
to play the damage sound whenever we start camera shake. Here, we can just put the audio player instance,
play sound effects. And then we can just pass the damage sound
effect audio getter, and for the transform, we can just pass the transform
of this object here. Save this. The next
one that we need to modify is the
it effect script. Let's just open the
hit effect script, and let's add a ers
field of audio gather, and let's just call
this hit sound effects. And whenever we hit
something here, under the hit method, we want
to play the sound effects. So let's just access
the audio player. Instance, run the play
sound effects method and then pass the hit
sound effect fields. And for the transform, we can just pass the effects
cache particle system, but we can pass the
transform component here. So the sound will originate depending on
the particle position, and this will be a treat sound. The last one should be the
weapon pick up script. So let's just open the
weapon pick up script here. And basically, whenever
we hit the weapon, we want to play a
bonus sound effect. Let's just add a size field, Audio gather, and let's just call this pick
up sound effects. And we want to play the sound effect using
the audio player. And let's just pass the pick up sound effects field
that we've created, and for the audio location, we can just leave it null
because we want this to be a dy sound and save this. Now let's go back to unity, and we are going to set
these things inside unity. So here, if we select
the main camera, let's go to the players
script component and select the player
damage sound effect. And let's go to the
data folder also and select both of the default
gun and the machine gun. And we want to populate this, let's just select the gunshot for the gunshot sound effect. For the reload, we want
to select the gun reload. For the empty, we want
to select the gun empty, and for the reload warning, we want to select
the reload warning. And the other thing
that we want to set up under our prefect folder, we can select the machine
gun pick up here, and now the weapon pick up will have a pick up sound
effects field. So we can just select this and then choose
the weapon pick up. For the hostage, we
want to modify this, let's just select the hostage. And here, I'm going to add the play audio action and add hostage don't
shoot sound effects, and let's set this to the sound
so we can hear it better, and let's just add a
delay for 2 seconds. And now we need to modify the enemy wherever
enemy gets hit. Let's just select
the enemy prefect, and under the hit effect, we have the hit sound
effect fields enabled. Let's just select
the electric zap. For the hostage, we want to also set this to the electric zap. Then for the big explosion here, We want to add the
play audio action, and we want to set
the delay C zero, and we want to
disable the D sound. We don't want this
to be a CD sound. And for the audio SFX, let's just select the explosion. And now inside the scene here, if we expand the environment, select all of the
cube object here. All of them should have hit effects script attached to it. Let's just select
the hit impact slot here. Save the scene. And then we want to create
the music for our scene, so let's just create
an empty game object. I'm going to reset
the transform, and I'm going to
call this music, and let's just play music
action and set the delay 20, and we want to play the
BGM audio sound effects. And let's save the scene. And now let's press
play and test this out. So there you go,
we have a music, and we can shoot and
we have a sound. And as you see,
as you can heart, if I I shoot near on the edge you can hear
the impact sound. And if we get hit by the enemy, we have this damaged sound, and if we shot the enemy, we can hear the electricity. And we can also
hear the explosion. And we can kill the hostage, and we can hear
the hostage voice and also the electric
and the weapon pick up. There are a lot of sound
happening right now. But basically, all
of them are working and we don't have any
error in our console. So, that is basically how we assign the sound to the
objects in our scene.
41. 40 Shots Calculation Bug Fix: I found a bug regarding the shots fired and
shots hit calculation. And in this video, we
are going to fix that. So to check for this bug, we need to select
the game manager and change the inspector
type to D bug. And now we can see all of
our private variables here. So let's press. And now you'll see that I'm
going to mute the audio. And now you'll see that
if we shoot the enemy, we will increase the enemy
hit and the shots fired. But the shots fired always
shoot times the enemy hit. And this is happening
because inside our code here on the weapon data,
if you scroll down, whenever we fire the weapon, it run this method here, and it runs this method for
every hits on the object. So for example, on
the enemy here, let's just select the enemy. And now if you sell the enemy, you'll see that we
have two hitables, which is the hit effects
and the enemy scripts. And we can confirm this by checking the script
on the fiscal studio. If we open the enemy script, you'll see that this class
here, implement the hitable. And the same goes
with the hit effects. It also implement this hitable. So this may gets
executed multiple times, depending how much hittable does the game
object have or has. So in order to fix this, first, we need to separate the shot hit method with the shots fired. So if we go to the game
manager here below, we have the shot hit method
with a bleion argument. We need to separate
this. So I'm going to create a new public void. And let's just call
this shots fired, and we don't need to
pass any parameter. I'm going to cut this shots
fired line and paste it here. Basically, whenever
we fire a weapon, we want to increase
this value here. And whenever we
hit something that we want to count, we
run this method here. Let's just remove the argument and remove the if
statement here. Let's clean this a
bit. And save this. And now let's go back
to the weapon data, and we can just delete
all of this here, the statement and delete
the return statement. And now the shot hit doesn't
ask for argument anymore, so let's just delete
the bulion value. And I'm going to delete
also this return statement. And basically, This is
all we need to do here. But whenever we fire a
gun or fire our weapon, we want to run the
shots fired method. So we can just run the shots
fired method here above. So let's access
the game manager, instance, and let's just
run the shots fired method. This way, whenever
we fire our gun, the shots variable on the
game manager will increase, and whenever we hit something
that we want to count, for example, the enemy scrip, then the enemy hit variable
will increase also. If we go back to unity
here, let's just stop this. If we select this barrel here, You see that we have
this spawn on hit. And this creates explosion, and the other one
spawn the machine gun. So we want to take this
into consideration, the spawn on hit
as an enemy hit. Otherwise, whenever we
shut this both items, it will count as a shot fired, but not as a enemy hit, and this will penalyze
our accuracy. So we need to go back in the script here and
in the weapon data. Under the if statement here. We want to add a second
condition, sorry, not n, but using the operation, and we want to compare or check if the itable is a spawn on it. Another thing that
we want to check is, let's go back to Unity first and select the
barrel white machine gun. And we need to check the
pref two spawn here. If you select this, we
can check the object. And here, the machine gun also have a itable script,
which is the weapon pick up. So let's just open
the script here. And we want to also
check for this. Let's go back to the weapon Data and add another statement. If the hitable is
a weapon pickup, then we want to also count the
shot hit. Let's save this. Now let's go back to
unity and let's play. Now if you select
the game manager, we should be able to
see the variable, and let's try to
shoot the enemy. Now we have one shots fired. But if I shot the enemy, now we have two shots
fired and one enemy hit. So the shots that we aim to
the enemy are count as one, both the hit and
the shots fired. So let's just top this
and then test this again, and let's try to
not miss the shot. There you go. Let's
shot the enemy three times to kill it. And now we have three enemy
hits and three shots fired. So with this change of code, we have fixed the
bug issue where the shots fired is always two times larger
than the enemy hit.
42. 41 Rank Calculation: In this video, we are going to create a rank
calculation system. So if we go to the UI here and if we expand
the game manager Canvas, you see if you select the n
screen here and enable this. You see that we have
a rank UI here, and it always
display A currently. So we need to implement this. In order to implement this, let's go to Fico Studio and
open the UI Manager script. And for the UI manager script, first, we want to create
a new serialized field. And this should be a
type of tex mesh P GUI, and let's just call
this rank tax. And basically, we need to create some formula to
calculate the score, to convert this to the rank. So the max ratio of
the enemy kel is 100%. Also the Mx ratio for shuts
accuracy, it's also 100%. With this two score
or two measurement, we can decide on
creating a rank system. Now let's just define. For getting rank of A, we want to make sure that the enemy kel is
above, let's say, 90%. And since accuracy
is a bit harder, we want to lower this value. So let's just check if the
accuracy is above 80%. And for B, we want to
set if the enemy kill, it's greater than let's say 75%, but at the same
time, less than 90%, and with accuracy, let's
say greater than 70%, but less than 80%. For C, let's just
copy this line here. I'm going to paste it, and
let's just modify this. For, we want to set if the
enemy kill it's less than 75%, and it's greater let's say 60%, and for the accuracy, we want to make sure
that this is than 70, and if it's greater than 55%, then we want to give it rank of C. And if we copy this line, paste it here, then we
want to set the rank D. Let's decide that D
is the lowest ran. So if it's less than
60, the enemy kel, and if accuracy is
also less than 55, then we want to give it
rank of D. With this here, we can create a total average. We are going to create
a new variable. Let's just call
this total average. And this should be the total
amount is divided by two. This is 170/2, and we get 85. It means that total
average should be greater than 85
to get rank of A. This one, we want
to have a range, which is if the total average is greater than the
lowest value here, so we can just grab
the average 72%. And at the same time,
it is less than 85, then we want to
give it rank of B, and if we duplicate this and
then pace it here for ran C, then we want to calculate
the lowest value, which is 6055,
this should be 57, and for the maximum, it's always the lowest
value of our previous ran. So let's just set this
if it's less than 72, and here if the total
average is less than 57. So now we have
this formula here. We can add a penalty value for whenever the
player kill a hostage. For example, hostage kill
will penalize point by 15. So one hostage kill will subtract all of
our average by 15. Okay, with this formula here, let's just copy
this note bit here, and let's go to Visual Studio and with the UI
Manager script open, we want to scroll down. And then go to the show
and screen method here, and I'm going to add
a new method below. Let's just call this
void calculate score. I'm going to paste
the calculation that we've created and
set this as a command. So we can see it here while
we're writing our code, and let's just clean this up. Now we have this
Calcate score method. Cleanup. We want to
pass the argument of all the argument inside our
show and screen method. Let's just select all
of the argument here, and I'm going to copy this
and then paste it here. Okay, basically here, we want to declare a couple of
local float variable. The first one would be
the hostage penalty. And we can just calculate
the hostage keel, multiply by ten.
We get the value. Sorry not ten, it should be
15 here, as you can see, then for the float
enemy keel ratio, we can just calculate the enemy keel divided
by the total enemy. But since both is in integer, we need to cast one
of them to a float, so I'm going to
cast the divider, and then I'm going to
multiply this by 100. We'll get the percentage, and if we enclose this, want to subtract this with
the hostage penalty value. Then let's just calculate
also the accuracy ratio. And this should be the total hit divided
by the total shots. And also, we want to
cast the divider to a float and multiply
this by 100. And don't forget to penalize the point by the
hostage penalty. And now we have
this. So let's just calculate the total average. And this is quite
simple actually. We just need to add the enemy kill ratio with the
accuracy ratio. And then divide this by two. Now we've calculate all
of the formula here, we want to show the
rank depending on this classification
or the specification that we've created
for the score. Let's just create
an if statement and if the total average, it's greater than 85 here, as you can see, then we want
to print out A on the rank. So we can just access
the rank tax and run the set tax method and then insert a string as the argument. And we want to check if the total averages greater than 72%. But at the same time,
it is less than 85. Then we want to
print out B But one of the comparison here needs
to have an equal sign, so it will include
all of the range. So I'm going to
add include sine, so it means that
if it's equal or greater than 85, then
we want to print this. Otherwise, if we don't use this, then if it's exactly 85, then both conditions
will not be executed. So let's just add
the equal sign. Oh, sorry, I've typed
a percentage here. It should be F, so
let's just fix that. And now we want to print on the rank text a character of B. So let's just copy this
to make it faster, and I'm going to copy the
less one for the rank D, and add an L statement
and then paste it here. For C, we know that if
it's greater than 57, and at the same time, it is 72, then we want to print C.
Here for the greater value, I want to include
the 72 float here, so I'm going to
add an equal sine and also add equal sine
two, this one here. And for D, we want to check
if it's less than 57. So let's just type D
here. Let's save this. And now we have this
method created. We need to run
this method inside the show en screen method. So after we set up the
accuracy text here, I'm going to run the
calculate score method and then pass all of
the argument here. Let's just pass the enemy kill, the total enemy,
the hostage kill, the total shots, and also
the total hit and save this, and let's go back to
Unity and test this out. Now here inside Unity, first, we need to select
the game manager, and then open the
end screen object here, open the lay out. And for the score area, we want to open the rank and it has a title and a R game object. We want to direct this ring
game object as a rank tax of our rank tax field under
the game manager component. Let's just direct
the rank tax here. And it has a text mesh pro, so the fields register
the component right away. And now let's save
our scene here. Now let's test this out. And I'm going to try
to play as fast as possible without shot
mist and hostage kill. Let's just use this to kill
the enemy, use explosion. And let's just enable
the debug here, so we can see our stats, and let's kill the
enemy. There you go. We have a perfect
stats over here, and now let's kill
the enemy here. Now as you can see here, we have a very nice
stat and we have R A. But if we test this again and now try to do our best again, but kill the hostage this time. And now, as you can see here, even though we have
100% hostile accuracy or enemy kill accuracy, and also the accuracy. But since we killed two hostage, we have a rank of C. So killing hostage will
penalize by a large amount, which is 15 points. And now let's test
this one more time for the shots accuracy. Let's just try to shots e, so we have a very bad accuracy, and let's try to kill the enemy. And as you can see here, even though we kill
of all of the enemy, our accuracy is very low, so we have rank of C. Okay, so that is basically how we calculate the n of the gameplay.
43. 42 Pause & Volume Setting: This time, we are going to
discuss a past mechanism and also create a folum settings when we open the past panel. So in order to do
that, I've already prepared a pause
panel UI package, and you can download it in
the resource of this lecture. So after you don't know this, just import the package, so I'm going to import
a custom package, and I'm going to import
the PUI package. And now just import all of them. Once it's imported, you see
here under the U PAC folder. Under the prefbs, we
have a new prefps, and it's called pause screen. So now let's just drag this prefs onto the
cross hair Canvas. If we go to the scene view here, if we want to see
the pos screen, we need to enable it first because it's
disabled by default. Let's just enable this and
you'll see that we have this pus UI and it has
two volume slider, which is a slider object, and we are going to tie the audio mixer groups
with this slider. The next thing that
we need to create is, we need to create
the audio mixer. But before that, let's just
disable the Po screen again, and let's go to
the audio folder, and let's just create
a new audio mixer. I'm going to create
a new audio mixer, and I'm going to call
this main mixer. Now let's double
click the mixer here. Once the audio mixers
windows is open, we have this few here, and we need to create a
couple of groups here. So let's just select the master and then press the
plus button here, and let's create a
sound effects group. And select the master again. And the next group that I want to create is the
Begron Music Group. So now we have two
additional volume here. And now if we go to the game manager under
the audio player, it has two fields called Sound Effects Group and
Background Music Group. We need to assign this
object to the slot here. So let's just bros this, and then select the
sound effects group for the sound effect group slot. And for the Begron Music Group, let's just select the
background Music group mixer. Okay. Let's save this. And now we need to
create a new script. So let's go to the scripts
folder. Under the audio. I'm going to create a new
script called Volume settings. And let's open the script. Okay, now we've opened
the script here. Let's create a couple
variable first. But before that,
we need to import the Audio name space
and the UI namespace. Here above, let's just add using Unity Engine not editor engine, and let's just import the Audio namespace and
also the UI namespace. And let's just remove the
mono behavior inheritance here because we are
going to instantiate this script or class on the game manager like
the UI manager class and the timer object class. And let's just delete
the built in method. And now we need to declare
a couple of variables. I'm going to create a
new serialized field. And this would be a
type of game object, and let's just call this panel. And the next field
will be a slider. And this would be the
sound effect slider and the BGM slider. And the last field
that we want to create is the audio mixer field. And let's just call
this main mixer. We also need to create
a private string for saving the value of our volume
to a player preference. Whenever we restart the game
or we enter a new session, it will load the
previous volume setting. Let's just create a
new private string. The first one, I'm going to
call this sound effect key, and the second one would be
the Background music key. We want to also create
a getter for the panel. I'm going to create a new
public game object geter here. I'm going to call this
panel with capital P, and inside the getter, I'm going to retrieve
the panel field. So we can access this
panel from other script, but other script cannot
change the value. So the next thing that we want to create is
we want to create a new method to modify the sound effects and
the background music. So let's just create a new void, and let's just call
this aj sound effects. And we want to pass a
float as the value here. Because this method will
be called by the slider, both of the slider
on change event. We need to pass a float value
here, and this parameter, we grab whatever
the slider value whenever we change the slider. Before we can modify the mixer, we need to expose some of the parameter on
the audio mixer. Let's go back to Unity, and let's go to
the audio folder. And let's just select
the main mixer here. And if we open the mixer, we can select the
sound effect group and the background music group. And now we want to
expose the parameter. Here, as you can
see, we don't have any expose parameter yet. So in order to
expose a parameter, we can select the mixer, and then under the volume name
here, under the inspector, we can right click
this, and then we can expose the volume to a script. And if we have this arrow sign, it means that this
parameter is expose. So now if we press the
post parameters button, we can see an post parameter.
We can rename this. So I'm going to double
click on a name and then rename this
to SoundEx volume. And then I'm going to select the Background Music Group
and also right click here, and then I'm going
to expose this. Now we have two
exposed parameters. For these new parameters
that we've created, the volume of the
Bagrand music group. I'm going to rename this
two Bagran Music volume. Now we have two parameters. We need to remember the name and the capitalization of the
name because it's important. Now let's go back
to the script here. Now we have expose
those parameters. We can access the main mixer, and we can set its float, and it as for the exposed parameter name
here, the string name. Let's just pass the
sound eff volume name. And for the value, we want
to pass this value here. So let's just pass the value. This way, when we are changing the volume by
dragging the slider, this method will gets executed, and the value of the slider will be passed via
this parameter, and this parameter will modify
the sound effects volume. The next thing that
we want to do is, we want to save the
value of the slider. Let's just use the
player prefs class and it has a set float method, and it as for a string key, and this is where we are going to use the string
that we've created. For the sun effx key, we are going to
use the key here. And we want to save the value. And after that, we want
to save the player prep. So let's just run the safe
method from player prefs. We need to insert a string here, I'm going to create a string. Let's just call this
sun effect key, and this would be the
Background music key. I purposely make
this as a private, so the value can only be
changed inside the script, cannot be changed
in the inspector. And this way, the value
will be more consistent. The next method that we
want to create is we want to create the a
Bground music volume here. Let's just copy
all of this here. And I'm going to paste it here, and then I'm going to rename the method to adjust
Bground music, and we want to change the float, which is the Background
music volume. And for the key, we want to
use the Big GM key here. So we want to save the value to a separate key in
the player prefs. So let's just paste this
here and save this. Now we have this two method. We need to assign this
method to the slider, so we need to create a
new public init method. And this needs to
be public because this init method are going to be executed
from the game manager. So here, first, we want to access the Son effX
slider object, and then run the
value change event, and we want to add listener. And we can pass the method
to his listener here. So let's just run
the S Soon effX. And you see that this ad
listener has a float signature. Unity event float, and
we need this method to have a float parameter because it asks
for a float here. For the Background music
slider, we want to do the same, so let's just access the
on value change event, and then add the listener, which is the JS
Background music here. Okay, now we've created this. This should work,
but there are still things that we need
to add here above, we want to load the volume settings that we've saved from
the player prefs. So in order to load the volume, we can just run
the adjust method, which is the sound effects, and then we can pass
the value here. So in order to load
the player prefs, we can just access the
player prefs class, and then use the
GT float method, and we need to retrieve
by using its key here. Let's just pass the
Sound ex key string. And there is overload, which if I just select
the overload here, you'll see that we can
pass a default value. So if there are no value
from the player prefs, we can just pass
the default value, which is it can be anything, but I'm going to set this 20. And I'm going to run the jazz
Background music method, and I'm going to pass
the player prefs, also the Get float method. And here, I'm going to grab the Background key and then set the default
value also to zero. And now with this setup, we load the default value or the safe value of our volume both on the sound effects and
the background music. But we want to also adjust the slider value to
this float value here. Let's just access the
sound effect slider and grab the value
properties and then pass the value of the
player perps here. I'm going to copy this line
here and then paste it here. And for the background
slider value, I'm going to pass the
second get float here. Okay. Now we've set up
the folum setting script. Let's go to the game
manager and create a new SS field to
instantiate this class here. Here below the UI manager field, I'm going to create
a new suralS field, and this will be
the Folum settings, and I'm going to call
this all setting. And then let's just initialize
this and save this. Here in the game manager, we need to create a new
public properties type of b. Let's just create that. I'm going to create a public
b and I'm going to call this game paused and this
will be a public get, but a private set. It can be only set from
this game manager script, but all of the other script
can get its bulion value. And here inside the init method. We want to initialize
the volume settings. Let's just access
the val setting and then run the innate method. And this will execute
this innate method. And now we want to modify the update method of
our game manager class. So let's just crawl down here, and we've already
have a update method. Here, let's just
create an I statement, and we want to check
for a key down. So let's just use to
get key down method. And I'm going to pass the
key code escape for pausing. And here we want to modify the time scale of our game here. Let's just access
the time scale. And we want to check if the time scale is
equal then zero, then we want to set the
time scale back to one. Otherwise, if it's not zero, then we want to set this 20. And whenever we are pausing, we want to enable the panel
of the volume setting. Let's just access the
fs setting object here, and we have the
panel properties. Let's just run the set
active set and then pass the time scale equal zero. So this means that if
this conditions is true, that we want to activate
the volume panel, and if this falls,
the volume panel will get the activated. And we want to also set the game pas bulion properties
to this value also. I'm sorry, there is one
thing that I forgot here, in the volume settings. We need to mark this
as sterilizable. Let's just use the system s
attribute here and save this. Now this should work,
and let's go to Unity. And here, once the
script compiled, You see that we have this
volume settings field, and it has a lot of fields. So first, for the panel, we want to drag the
pus screen panel. And for the slider, let's just open the pus screen, open the layout object, and we have the slider
for the sound effect. So I'm going to open both of the slider game object and then drag the slider for
the sound effect to the sound efflder slot. And for the Background music, I'm going to drag the slider to the background slider slot. And for the main mixer, we need to browse this
from the project. So let's just use
this circle button and then pick the main
mixer from the assets. And save the scene. And
now let's give it a try. Let's press play, and
see if we press escape, the game pause, and I can
change the volume here. I can still shoot because
we haven't prevented that. But in a moment, I
will show you how. Now let's just try
to lower the music. There you go. If we
Push the slider. You can see we have lowered the music volume, and
we can increase it. So I'm going to set this
to this position here, and let's set the sound
effects to a louder volume. Yeah. And we can also make
it smaller. There you go. And if we sat at this position
here, and then we stop. And if we press play, and then we pause again. The value gets loaded,
as you can see here. It gets the volume settings from the player craft.
So we can save this. And this settings is also safe throughout
different session. Okay, let's check the
console for any errors. So far, no errors. That's good. The next thing that we want to do is we want to prevent the player from
shooting when it's passed. So let's open Visual studio, and let's open the
player script. And if we go to the top here
under the update method, we have this line, and this is actually
executing the update from the scriptable object of
our current active weapon. And we can add a condition here. We can check if the game manager instance Game pause is false, and we can just add an
exclamation mark in front here, and this means that if
this value is false, then we want to run
the method update. But if this value is true, then we want to skip
this code here. And let's save this, and
let's head back to unity. Now, when it's finished
compiled, let's test this out. And let's try to pause a bit later when
the enemy arrives. So now it's pause. All of the element
game is pause here, and we cannot shoot. As you can see, there
are no shooting sound, and the mo also doesn't
decrease. Okay. Yeah, that is basically
how we create a pause mechanism and
also a volum settings.
44. 43 Player Dead Setup: Now in this video, we are going to create a death mechanism. Currently, we don't
have that, so whenever our health
goes to zero, the game continues, we need
to add those features. So let's go to the fisal studio. And the first thing that
we want to modify here, we want to go to
the game manager. And inside the game manager on top here on the field area, we want to create a new
public bull properties for declaring whether the
player is dead or not. So let's just create
a new public bullion, and let's just call
this player dead, and we want to also set
this to a public getter, but a private setter. And here, inside the hit method, the player hit method,
we want to check if our current health
is zero or not. Let's just create
an if statement, and if the current health
is less or equal to zero, Then we want to run the
show and screen method, and we want to also set
the player that bul thru. And this will be set
to false by default. So we need to change this two true whenever the
player is dead. We need to modify a
couple other script, and I'm going to modify
the enemy script. So let's just open
the enemy script. And here inside
the shoot t here, inside the wil loop,
I want to check for the player is that state. Let's just access
the game manager. Statics reference,
which is instance, and we can just check for
the player that value. And if the player is dead, we want to stop shooting, and the stop
shooting method will execute the stop all co
routines method actually. So this co routine
will gets canceled, and the enemy will
stand idle. Save this. The next thing that we want to modify is the player script. Basically, here, we want to also add a condition when
the player is alive, then we want to run
the weapon update. Let's just add an N condition and check if the game manager, instance player
is dead is false. This way, when the
player is dead, the player won't be
able to shoot anymore. Want to also stop
the player from moving whenever the
player is dead. Currently here in
the shootout point. You see that whenever
this gets triggered, the player will continue moving. And this is a problem because
when we enter the shootout, we have a delayed action to run the set area
cleared method. And this won't be canceled if the player is dead currently. This will be still running
this core routine here. So we need to cancel this here. We can just add a condition
here, A or condition. If the game manager instance
and player is dead, then we want to return this. The method will still get
called by this delayed action. But when it's called, if the player is dead, it will return, then all of this code here will be
skipped. Let's save this. The next thing that we want to modify is the time
or object here. Here inside the timer object, we have this core routine
that runs the timer. This also won't be
canceled whenever the player is dead at
the current state. So we need to add that here
inside the wild statement, we can just check if the
game manager instance and player is dead, and if it's dead, then we want to break this core routine. We can just break
the cotine using the yield break statement
here inside the coroutine, and this will cancel
this core routine here. Now let's continue modify the last script that
we need to modify, which is the UI manager,
so I'm going to open that. Here inside the UI manager, we have this show end screen,
and we need to fix this. Because basically whenever we finish the game or whenever
the player is dead, we are going to show
this end screen. And there is a possibility that this total shots
value will be zero, and this will cause an error. So we need to check if sorry I'm going to create a new
sets of parents here. And I'm going to check if the total shots
it's equal to zero. Then I'm going to pass value
of one But if it's not zero, then we want to pass the
value of the total shots. So let's save this, and
this is a ternary operator. And here, we also need to fix the accuracy ratio on
the calculate score. Let's just copy this line here, and then paste it here since it's used the
same variable name, so this should work right away. And now we want to create
another LSF statement. But I'm going to create
this above the rank D here. So let's just create
new LSF statement, and this time if the total
average is equal to zero, Then we want to pass n of E. So let's just define that this is the
lowest ran possible. Let's save this. Now we modify
all of the script here. Let's test this out.
Let's go to Unity and let's run the game. I'm going to pause
and then lower the music and then continue. And now let's just stay still and receive enemy
shots as much as possible. So we can test if
the player is dead. There you go. Now,
we've been killed and the enemy stop shooting.
The times stop. We cannot shoot anymore. And since the times stop, the game will not progress. So this is how we create
the dead feature.
45. 44 Title Menu Scene: Now let's create
the title scene. In order to create
a title scene. Let's create a new scene here
under the scenes folder. So let's just
create a new scene, and let's just call
this title scene. Let's open the title scene. With the title scene open, I'm going to select the camera, and I'm going to set the
clear flag to solid color. And I'm going to change the
breakdown color to black. We won't be seeing the sky box, and now let's save the scene. The next thing
that we need to do is we need to create a Canvas. I'm going to go to the UI menu here and then create new Canvas. For the canvas object
under the Canvas scaler, let's change this to the
scale with green size and set the resolution to 1920 by 1080. This is the re resolution
that we are using, and for the match properties, let's set this to 0.5. It will be balanced between
the width and the height. And I've already
prepared a couple of prefbs for the title scene. So let's just import that. I'm going to include the
file in the resource, and here I'm going
to import package, and it's called
Title UI package. And when importing this package, make sure that you
don't import the funds, just select this, and just
unselect sprite also. Because we already have
this in our project, it might mess up the project. So let's just import the
UI pack folder here. Now that we have
import the prefabs, let's go to the U IPAC. Under the prefe we
have a new prefs. One is O option menu, and the other one is title menu. We want to put the
title menu first. Let's just drag the title
menu to the vas game object, and you can see the object here. There is an issue
with the button. In order to fix this, let's just expand the title menu prefs, and it has these three buttons, the start Option and quid, and it's missing sprite. So we need to generate
a transparent sprite. In order to do that, let's go to the Sprite folder
under our UI sprite. We want to open
this sprite editor, and I'm going to drag a new
sprite here to an empty area, and size. It doesn't
really matter. And I'm going to
rename this sprite to UI underscore sprites,
underscore transparent. Then once we've renamed this,
let's just press apply. Now that we have a
transparent sprite, we can use this
transparent sprite. As you can see
here, because I put the same name with the one where I set up
this prefabs here. Now it occupies automatically. But if you are using
a different name, you can just select all
of the button here from the start to qui button
and then just pick the source image using the transparent sprite
that you've generated. Now this button already
set up properly, it has a different
selected sprite here. If we press play, you'll see that if we click
on the button, it has a border. We are going to use
this to move using the keyboard up and down.
Now, let's stop this. The next thing that we want
to put is the options menu. Let's just put the options menu to the Canvas game object, and now we have this
nice options menu. Here under the
option menu prefep, we have a lot of child object. Here under the options,
we have an entry, the first entry, which
is the resolution, and the second entry
is the quality. And here we have a drop down for the resolution and also
drop down for the quality. We are going to populate the
drop down using a script, and we are going
to retrieve all of the resolution supported
on the player PC. And for the quality,
we are going to grab the quality settings
from our project setting. If we go to the
project settings here, and under the quality, you
see that we have this. We want to grab this and then fill this dropdown based
on this value here. And we want to make sure
that this dropdown can change the resolution and the quality on the final
built of the game. Now let's save the
scene and we need to create a new script
for the title scene. Now let's go to the
scripts folder. I'm going to create
a new subfolder, and I'm going to call
this title scene. Here under the title scene, we want to create
two new script. The first one will be
the title menu manager. Let's just create that one. And the other one would
be the option script. Now let's open this script here. I'm going to open the
option script first. Here inside the option script, we want to create a new variable for holding the drop down. Let's just create a
new serials field. For the type, I'm going to type the name space of the text Mash pro because the drop down is the text
mesh pro dropdown, and here we can just use
the TMP dropdown class. And let's just call the first
one resolution drop down, and the second one,
quality, drop down. Then we want to create
a new private variable for holding the resolution data. Let's just type
private resolution. And this will be a array, and I'm going to call
this resolutions. Here inside the start method, we want to grab all of the supported
resolutions on that PC. Let's just type resolution, the array that
we've created here, and then we can
just fit this with the screen class resolutions. This will return all
full screen resolutions supported by the monitor.
Let's just use this. Here we want to create a
new list of string for holding the options or the resolutions in
the string format, so we can fit this list of string to the
resolution drop down. Let's just type these options and let's just initialize this. And then we want to look through the resolutions that
we've just grab here. Let's just pass the resolution
and grab its length. And for every resolutions, we want to create
a new string to be added to the options
list of strings here. Let's just call this rest, and for the string, we want to grab the
resolution index of I width, and we want to add
this with a string x with a space on the both
sides of the x character. And then add this with the resolutions index of
I and it's with value. The next thing that
we want to do is, we want to add the string to
this list of string here. Let's just add the rest string, and this will fill
up the list of string options with this string, but with all of the resolutions that the current
monitor supports. Here below, we want to grab
the resolution drop down, and then we want to run
the clear options method. This will clear any
previous options. Then we want to fill the drop down with a new options using
the At options method, and it asks for a less off
sprite or less off string. So we can just pass
the options here. And now we want to set the
resolution drop down value to the current selected resolution or the current active
resolution on that monitor. In order to do this, we need to create a new integer variable, and let's just call this
current screen resolution ID, and set is 20. And here we want to
check if the screen and let's just grab the
current resolution and grab its width, and we need to compare both of the width and the height with the resolutions
value at index of I. For the width, we
need to compare with the width of
resolutions index of I. If the value is equal, we need to also
check the height. Let's just add an n operator, and let's check for the screen current resolution equal to resolutions,
index of it. I'm going to break
the line here, so it's easier to read, and if both of the
condition is true, then we are at that resolution. We want to set this current
screen resolution ID to the value of I. Now we have set this value. We want to go here below and access the
resolution drop down, and we want to set its
value to this integer here. Let's just pass this here. And this will set the drop
down to the active resolution. And now we want to populate
the quality dropdown. This is much simpler, so we can just
access the quality dropdown and we can
clear the options. For adding options, we want to access the
quality dropdown again, run the add options method. Then we want to pass
the quality settings, and Quality settings class has an array of
string called names, and this is the index list of
available quality settings. But since it's a string array, we need to convert
this to a list because at options as
for a list of string. And if we just pass the array, it will throw an error here. In order to convert
this to a list, we can use a
namespace system link inside the link namespace, we have a two list function. We can use this and
this will convert this string to list. Here, we want to set
the quality drop down value to whatever quit settings that we are
currently set at. Let's just set the value here to equal the
quality settings class, and we want to get
the quality level. And this will return an
integer of the index, the quality that
we are currently set at. Let's save this. And here, let's
go back to unity, and let's give it a try here. First, I'm going to put the option script to the
Canvas game object here, and let's just open
the option menu, and for the
resolution drop down, we want to pick the
first drop down, and for the quality drop down, let's just drag the
second drop down. We cannot test the
resolution dropdown because it only
works on a build, but the quality dropdown, we can test this on the editor. So let's just open
the project settings. Currently we are at fantastic
if we press play Now, you see that we have a
lot of resolution here. And we also have all of
the quality settings, which is the fastest,
fast, simple, good, beautiful, and fantastic. And we are currently
set two fantastic. And here, if we change this, nothing will happen right now
because we need to register a method where we
are going to change the quality settings to
this dropdown listener. So let's just do that. Here we want to create a
new public method. Let's just delete
this update method. We are not going to be using it. I'm going to create
a new public method, and I'm going to call
this set quality. And I'm going to pass
an integer variable. Let's just call this quality ID. And here we want to access
the quality settings, and we want to set
the quality level, then we want to pass this
quality ID here. And save this. And now we want to assign the listener for
this quality dropdown, whenever the quality
dropdown gets changed, we want to run this
set quality method. And whatever the value gets changed in this
quality drop down, we want to pass that value
as the quality ID here. So here on the start
method down below, we can just access the
quality drop down, and then we can search for the on value change event here. And then we can run the
ad listener method, then we can pass the
set quality method. And here, as you can
see, it asks for an action with a
signature of integer. So it's a method. But with an argument, that is an integer. So if we have another method that doesn't have an
integer as the argument, this will throw an error. And the way this work is, whenever we change
the quality drop down, are changing the value. So when we are
changing the value, we are actually
changing the integer, representing the
options from zero to the maximum length
of the options. And whenever we
change those value, those value are being sent to this method here to the
set quality method. And when we run this method, those new values are going
to pass as an argument here, and this argument will change the quality level on our
quality settings here. And now let's create a
set resolution method. Here, let's create
a new public void and let's just call
this set resolution. For the resolution, we also
want to pass an integer. Let's just call this
resolution ID. And save this. And for the set resolution, we want to create a
temporary resolution data. So I'm going to call this RS, and I'm going to grab
the resolutions array, and we want to grab the ID
that we've passed here. So let's just pass
the resolution ID. And this works exactly like
the Set quality method. This will be registered to the resolution dropdown
on value change listener. And now once we've
grab the resolution, we want to apply this to the screen Set
resolution method. For the set resolution method, it asks for a integer of width integer height and the
Boolean full screen option. Let's just pass the rest
width and for the height, we can just pass
the rest height. For the full screen, we can just access the screen
f screen. Let's save this. And we want to also register the set resolution to the
resolution drop down. Let's just type resolution
drop down and access the value change event and add the listener and pass the
set resolution method. And now we've set
up the listener for the resolution drop down. I've noticed there is an
error in my code here, here, in the string res. This should be the height. Let's just change the second
resolution parameter to the height value and save this. Now if we go back to unity here, and then if we try to run this, let's open the project settings. And let's try to change
the quality here. If I change the
quality to simple, you see that we've changed the quality settings on
our project settings. And this will affect all of the visual fidelity
that we have set up here or that Unity
have set up here. It will lower the texture depending on the
quality that we've. Yeah, we are going to continue finishing the title
scene on the next video.
46. 45 Title Menu Continued: Now, let's connect
all of the button, so we can start the game or
we can open the option panel, or we can quit the game. In order to do that, let's open the title menu Manager script. Here inside the
title menu Manager. Let's create a new variable, and let's set this
two game object, and this would be
the option panel. And the other
variable that I want to create is a string variable, and I'm going to call
this game play scene. And we want to also
add a serize field, and this will be a button. But in order to use button, we need to import
the UI namespace. Let's just automate this here and need to
automatically create this. I'm going to create
a new start button, option button, and then
option close button, and the last one would
be the Qi button. Let's delete the update method. We won't be using that. Here we want to create a couple method. The first one would be
the start game method. And the second one would
be the open option panel. The third one would be the
closed option panel method, and the fourth one would
be the quid game method. For the Quid game is
quite easy, actually, we can just access
the application class and then run the QID method, and this will quit the game
on the built standalone game. It won't do anything
on the editor. And for the start game, we want to load
the gameplay scene based on its name here. So we need to import the
scene Manager Name space. I'm going to type using Unity
engine Scene Management. And here inside the start game, I want to make sure
that we cannot start game if the option
panel is open. So we want to check if the option panel is
active in hierarchy, then we want to return this. But if it's not, then we want to access the scene
manager class, and then we want to run
the load scene method, and we can pass either the scene built index
or the sen name, and we are going to
pass this string here. So we can fill the scene
name on the inspector and save this For the
open option panel, I'm going to access
the option panel, and I'm going to run
the set active method, set the value to true for
closing the option panel. We can just access the option
panel game object again. Run the set active method and
then pass the false value. Whenever we are closing
the option panel, we want to select
the option button. Let's just access
the option button and then run the select method. And select method will set this button to be the selected
button on the scene here. On start, we want to set the start button to
be the selected one, using the select method. Then here we want to assign all of this method
here to this button here. We can just access
the start button, and then we can access
the unclick event. Then we can add listener. And here we want to pass
the start game method. For the option button, we want to also access the
click event at Listener, and we want to open
the option panel here. For the option closed button. We want to pass the closed
option panel method. The last one for
the quid button, we want to pass the
quid game method as the listener
here, and save this. Here inside the start method, we also want to hide the
option panel right away. Let's just access
the option panel on start and then run
the set active method, past the false value as the
argument, and save this. Now we've created the
Title menu manager script. Let's go back to Unity, and let's add the script
to the Canvas game object. Set the Canvas game, direct the Title menu manager, and now we need to populate the variable on the inspector. Let's go to the scenes
folder and I'm going to type the scene
where our level is, which is the sample scene. For the option panel, we can just drag the option menu here. As the game object.
For the start button, we want to open the title menu and drag the start button here. The option button, we want
to drag the option button. For the quid, we want to
drag the quid button. For the option close button, we want to pick the button from the option
menu prefabs here. Let's just drag
this button here. This is actually
this button here. We want to close
the option panel whenever we click this button. Let's save this. Now
let's give it a try. It will start the scene with the start button selected,
and if we go down, we can select other options, and if we select the options, this will open the window here. If we press okay, it
will close this again. Let's stop this, and let's fix one thing here inside
the title menu manager, whenever we open
the option panel, we want to select the
option close Paton. Here I'm going to access
the option close Paton, and then I'm going to run the select method
here and save this. This time, whenever
we play this, and if we open the option panel, This will automatically
select this patent here. If we press enter, it will
close again automatically. We can navigate the menu of the title scene
without mouse. Now let's test this again, and let's press start. Currently, the load scenes
won't work because we haven't set the scenes that should be included
in the build setting. In order to fix this,
we need to go to the file menu under
the build settings. Let's just drag the scenes that we are going to
include in the build. I'm going to drag
the title scene here and also the sample scene. I'm going to save the project. Now if we press play and if
we press enter on start, you see that it will
load the scene here. Now if we go to the
sample scene here, we need to also have
a way to go back to the title menu or the title scene once
we finish the game. In order to do
that, I'm going to open the game manager script, and let's just go to the
UI Manager script here. Here under the sore properties, let's just create a
new serialized field, and this will be
a type of button, and let's just call
this back button, and we need to
create a new method to go back to the title scene. Let's just create a
new private method, and let's just call
this go to Title scene. And we want to use the
scene Manager class. We need to import on
top of our class here. Let's just type using Unity engine scene Management and below the button
variable here, we want to create a
new string field, and let's just call
this title Scene name. Here below on the go
to Title Scene method. We want to access
the scene manager, Load scene, and then we can just pass the Title Sen name here. We need to register
this method to the back button that
We've created here. Inside the init method, we can just access the B button on click event and
then add listener, and then we can just pass the method go to Title
Sen Method here. And save this. Now if
we go back to Unity, we need to set this back
button and the title C Name. For the title C name, we can safely type title scene, and for the button, we want to expand the game
manager and open the Canvas. Under the end screen here,
we want to expand this, and we want to
drag the button as the back button
here, and save this. Now let's just play this to test this modification
that we've just created. And this is not working because the crosshair
is blocking the cast. So we won't be needing this cast target under the
crosshair game object. We can just safely disable this. Now if we disable this and then we try to click the back button, you see that it will work. So I've stopped the game here, and since I've changed the
options on the run time, we need to set this back here. Let's just select the crosshair
and check the RC target. Okay. Now here, we can go back to the title scene and
from the title scene, we can also go to
the gameplay scene.
47. 46 Build the Game!: Now we are going to
finalize the game. We want to build the game
into a standalone apps. It's quite easy, actually. We can just go to the file, build settings, and then
press this built button here. We can decide whether we want
to set the architecture to, the 64 bit or 32 bit.
This all depends. If you are using two bit, you will have a wide
range of audience that are using windows
two bit also included. Because if you built to 64 bit, then the built won't
run on Windows 32 bit. In the future, I think all of the user will
switch to 64 bit. So I'm going to lift
this 264 bit in my case. And the next thing
that we want to do is we want to open
player settings here. And here we want to
change the company name. So I'm going to change the
company name to my name here. And for the product name. I'm going to change
the product name to the name of the game,
which is nexus. Version, you can
just set this 21. We will also need to
apply the default icon. I've already prepared the icon. I'm going to go to the
texture folder here and I'm going to open my icon here, and I'm going to include this icon in the
resource folder, and let's just track this icon
into the Textures folder. Now we have import the icon. We can pick the default icon
and pick the app icon here. And the next thing that
we want to set up is here under the resolution
and presentation. We want to disable the
display resolution dialogue. Let's just disable this. And now that we have set
up the player settings, let's just check for the
other settings. A looks good. And if you want to change the bundle identifier,
you should do it here. So we can just set this
to the company name, which is my name, and
then for the game name, you can just type the game name. That will be all for setting
the player settings. And now let's just
save the project, so all of the player
settings get saved. I'm going to import
a free asset from the asset store for the
bugging on the build. If you go to the asset store, you can search for log fewer. And this is a really good asset. You can use this, No this one, but this one here, the log pewer, it's free. And basically, we can show
the console on our built. This is really helpful when
you are debugging your built. So let's just import this. And now once we have
impart the package, we can go to the Unity logs viewer here and we
can check the Rhythm. So let's just show in the Explorer and let's
just open the Rhythm file. And here, as you can see, in order to use
the console logs, you can just make a circle
gesture using your mouse, click drag or your
finger on the mobile. Now let's add the assets
into our start scene here. Here we have a new
menu called Reporter, and let's just press create, and this will automatically
create a game object to enable the logs on the
build and save the scene here. Now we've add the
reporter to our scene. Let's just create a
sound modification on our option script and
the title menu manager. Let's go to Fis Studio, and I'm going to add a
sound for the clicks here. Here on the option script, let's just create a
new Sudo S field, and this will be the Audio ger. And let's just call this
click sound effects. Here, whenever we change the resolution or
setting the quality, we want to play that sound. So let's just access the
audio player instance and run the play
sound effects method, and then we can just pass the click sound effects
without transform. This will be a two D sound and we can just duplicate
this line here, and then paste this here
inside the set quality method. Now we want to also add the sound into the
title menu manager. Here, whenever we
start the game, we want to play the click sound. And also we want to
play the click sound whenever we open
the option panel. Also whenever we close the panel and whenever
we quit the game. But we don't have
the click sound effects variable defined here. Let's just copy the
one that we have here, and then paste this here as
a new field and save this. Now let's head back to Unity, and we need to import
the click sound also. I'm going to go to the game
folder under the audio. I'm going to open the file here. And I'm going to include this new sound on
the resource folder. So here we have the
UI button dot wave. Let's just direct this to Unity, and then put it the file
inside the audio folder. And now we have this new sound. Let's go to the data folder
and update the audio library. So I'm going to add a new entry, and for the last entry,
I'm going to set this. I'm going to call
this UI button. And let's just pick the
UI button dot wave here. And let's save the project. Here in the title screen, we want to create a
new empty game object for the audio player. I'm going to call
this audio player, and I'm going to add the
audio player component. For the library, I'm going
to pick the audio library. And for the audio source number, we don't need six audio sources, so we can just set this 21, and we can set the mixer to the mixer that we've
created before, and save the scene again. Now if we go to the
Canvas game object, we'll see that we have
the click Son effect. Let's just change
this two DUI button, and for the title manager, let's just also change this
two DUI button, So effect. Now save this, and now
let's build the game. Press bill settings,
and then press bill. And here we can just
select the built folder. I've already created folder
and choose select folder. You can just create the built on any folder
that you want. Create a new folder and select that folder. Now it's building. I'm going to cut the
video and we will continue after the
building is finished. Okay, now it's finish built and Unity automatically
open the folder. So now we can test this. And now let's try
the option panel. And somehow it doesn't work. Let's try to quit. Okay. There is an issue here, and we need to check for the issues. That's why I've installed
the log viewer. We can just create a circle motion using
the mouse by click and dragging and once There you go. And as you can see here,
there is an issue here. You see we have an error, and let's just check this here. Okay. There is an issue with the audio getter
sound effect here. So when we run the
open option panel, somehow it cannot play
the sound effects. So we need to fix this, and there is a couple of
error here, as you can see. So let's go back to Unity. I'm going to quit the
game by pressing F four. Okay, so I suspect the issue is on the
audio getter class. So if we open the audio library here, in the audio getter, whenever we retrieve
the audio name, it actually retrieve from the audio names list
of string here. And this is actually
a static class, as you can see here, and if you go above here, you'll see that it's
actually a static class. The problem with this code here, the unvalidate only
runs on an editor, whenever we change the
value in the inspector or whenever we select the
object in the project panel. But this never gets
executed on the build. So we need to make sure that we execute this code
here on the build. Luckily, in a scriptable object, we can force this by
adding the awake method. And inside the await method, we can just add the
validate method. So this will call
this method here, and then we'll populate the
static audio name list here. So once it's populated, whenever we are retrieving the audio name to play
the sound effects, this static list of
strings will be available, and it will return
the correct string. And there is another issue that I've noticed on the built. On spawning the enemy here, we will have one frame of missing reference of
the ym component here. And this is happening because the animator move can be
executed before start. So we need to make
sure that whenever the animator move is executed, we need to check for the
anim object reference here. So here I'm going to add an if statement and
if the enim is null, then we want to return
this method and save this. I've know this because I've
checked the built before, and this will cause
a minor issue. It's just a message,
an error message, but it would be best to also
prevent this kind of error. So let's just add this
line here and save this. Now back here in the unity. I'm going to try to build
this one more time. But before that,
I'm going to go to the project settings folder
and under the audio. I'm going to set the
global volume back to one. I've set this to 0.05. So on the editor, it doesn't chow out, but I'm going to set
this back to one. And I'm going just to meet
the audio here on the editor. It's okay, and I'm going
to save the project. And now let's just build
the game one more time. Now the game is built, and let's just run this. And now I'm going to try to press the option,
and there you go. We have the sound playing. And now if you change
the resolution, you can see that it lowers
the resolution and we can just switch back to
the maximum resolution. You can pick a resolution,
for example, this one. And we can also
change the quality. Here. I'm going to change
it to fantastic and press. And now let's start the game. Watch. Watch it. Now,
let's test this. Let's go back to the yeah, there is one issue though. When we go back to the menu, we cannot see the mouse cursor
here. Let's just fix that. And here if you
go back to Unity, go to the is studio here. Under the UI manager, we are hiding the cursor
on start of the level. So let's just copy
this line here, and we want to make
sure that the cursor is back visible on
the title screen. So we can just add this line here to the Title menu manager. And inside the start method
of Title menu Manager, we can just add
that line and set the cursor back to
through and safety. Okay. So yeah, that
is basically how we create a real
shooter games in Unit, and that is basically how
you prepare the build. One tip from metho, whenever you finish testing the build, you should remove this
rapporteur game object. So the player cannot enable the console debugging on
the built by accident. But for the development built, you should leave this portur
game object on the scene, so you can always debug
the development built. I want to thank you all for
enrolling in this course. If there are any issues or any bugs in the game,
please report to me, and I will update
the course to fix the regarding bugs because
I know as a person, I'm not a perfect person, I can make mistake
in this course, so I really appreciate any
input from all of you guys. Thanks again, and
I hope I'll see you on the next course of mine.
48. 47 Area Cleared Bug Fixing: Hi, in this up date video, I'm going to show you a fix of a bug that has been
reported by Kyle Cooper. Thanks a lot for reporting this. So basically, in the shooter
game that we have here, the area clear mechanics
has a bit of issue. That is, if we select the main camera and we set
the Inspector bug here, we can see that the area clear
is not clearing correctly. So for example, if
I press play now, And in this example, I've set our health to 100. So it took a while before enemy kills the
player just to debug this. And here, I'm going just to
kill one of the enemy here. And I'm going to
lift the time as it is until it runs out and we can check the area
cleared value here. So here you see that the area cleared doesn't get increased. So now we cannot finish
the game in the state, even though we have fh
cleared all of the area. And this will cause a
game breaking bugs, actually where the stat
panel doesn't get shown, and we cannot progress
further in our game. Because we have in this scene, I have three shoot
out groups and the area cleared only
increased to two so far. So the value never gets equal, and this never triggered the UI. So in order to fix this, let's go to the
Fisial studio here, and on the shoot out
point script here, Basically on the set
area cleared method, we need to call this
player move area cleared. So basically, this is calling this method on the
player move script here, and we need to do the
same in this code here. So I'm going just to copy this line here and paste it here above the area cleared equal
to true method and safety. And this will fix this issue because whenever
the timer runs up, we are going to call this
set area cleared method. And now let's give it a try. And take a look on the area
cleared variable here. Let's just kill one of the enemy and leave
the other ones alive. Okay. Now you see that the
area cleared gets increase, but there is another problem. If we kill this enemy, then our area
cleared increase 22, even though we haven't
cleared the second area, and this will cause issue where whenever we
cleared the second area, it will show the
end game screen, and we don't go to
the next pine here. So in order to fix
this, Basically, we need to go to the
player move script here, and we need to add a
Boulan in this method. I'm going to add a
Boulan argument, and I'm going to call
this previously clear. Basically, I'm going to check if the previously
cleared value is true. I'm going to return
this function. So now we need to pass a
bulion into this method here, and we will have an error in
our shootout point script. Here in our shootout
point script, basically, whenever we call the
area cleared method, we need to pass this area
cleared bulion properties. So I'm going to pass this here. And basically, this will pass the bulion
to this method here. So if we haven't
cleared the area yet, then this bulion will be false. And whenever we run this method, all of this coat heil
will gets executed. So we are going to increase
the area cleared value, and we are going to trigger the event and we are going to continue the player movement. And if this, for some reason, gets executed
again, for example, if we cleared the area, then this method
will gets called, and the area cleared
will be passed, and this will be false because we haven't changed
this value true, and we changed this after
we are calling this method. But for some reason, during the transition to the next area, we killed an enemy and we finished killing
all of the enemy. This will get called either. But now with this bulion check, the area cleared will
be true previously, even though we are setting
this to true because it has been already changed
to true previously here. So if this value is true, then basically this will
skip all of the code below, and this will not
be called anymore. And this will prevent
the area cleared increasing more than once in
a same shoot out point here. So if we go back to unit here, I'm going to run this again. Now we can take a look on
our area clear variable. And let's do the same here. I'm going just to kill
one of the enemy here on the first shoot out point and
leave the other ones alive. And once the times runs out, let's try to kill the enemy. So now the area cleared
is increase here, and let's kill the enemy. And now once I've
cleared the enemy, the area cleared stays 21. It will be only
increase on per area, and now I can kill
this one here, and now we have
cleared two area. And then for the third one, if we kill this enemy here, it will show the stats. And now let's make another try. And this time, let's just leave the second enemy alive on the first area here
and don't kill it, even though we are transitioning
to the second area. So I'm going to check against every possible
scenario in our game. Now the time is runs up. Let's continue playing
the other area here. And it counting correctly. The area clear variable here.
Let's just kill this one. Yes, there you go. So basically, we succeeded to
fix this bug here. And thanks a Kyle Cooper
for reporting this issue. And if you guys found
any other bugs, please just report it
on the Key A sections, and I will try to
address this quickly.