Transcripts
1. Course Introduction: Hi, welcome to this Unity course where we will create
a point and click game similar to Monkey Island or other point click games
with me Rome Fozzy. Okay. In this course, we will delve into
multiple topics such as setting up
scene lighting, light probes,
character animations, object oriented programming, scriptable object for
defining items database, and inventory, safe
load progress, safe entity system for saving scene states such as NPC
states and item states, a synchronous scene
loading, audio, and we will also discuss
editor scripting heavily to create a custom
inspector that will ease the development
and many more. On each lesson, a completed
script will be included, so you can check
them against yours. So I hope you'll able to expand your knowledge with this
course and we'll also be able to apply
the knowledge to any kind of game you'll
create in your project. Lastly, I want to thank you
for enrolling in this course, and I hope you'll enjoy and find this course
useful. Okay.
2. 01 Project Setup: D Okay. Now, we are going to start
to create a new project. And for this course, I'm going to use the unity
2018 0.2 0.8 F one really. So you can use any other version as long as not two
different from this one. I think 2017 would be okay. So now I've just
started the unity and it's still loading.
I'm going to cut this. Now it's loaded, and we
should create a new project, suppress new here and I'm
going to browse the path, and I'm going to create
a new project here. Okay. Yeah, for the time plate, we are going to just to
leave this to three D, and I think that should do now, let's just press create project. Once the project is open, we are going to import a
couple of assets first. Now our project is open already, and we have this new
sample scene here. We can just use this for
prototyping first, so yeah. And this sample
scene is going to be our basic setup scene for created scripts
in this project. And later once we
created enough scripts, we are going to create a new
scene to build our game. So yeah, We are going to use
privatives for this purpose. So let's just create
a trey object here under the hierarchy
right tick and then go to the treat object
category and press plane. We are going to use this
for our base ground. And I'm going to
make this bigger. So I'm going to increase
the scale by twice. This under a project
folder here. Let's just create
a new material, create new folder
materials and here, create new material inside that folder and
call this ground. So we can create a darker shades of material and drag this
ground to this plane here. So we can see the
object over there. Let's create a couple of first, I'm going to create
a empty game object, and this will be
used as a group. So I'm going to reset its tran form and rename the
game object to environment. And I'm going to
direct our plane to be the child of
this environment. So I'm going to
create a couple of different object cube
here and move it upwards. So it sits nicely on
top of the ground here. Okay. And just scatter this, just duplicate the cube
here and there. So we have some
sort of scene here, and this will be for
testing ification mesh. So under our environment, we want to mark all of this game object as
a static objects. So just enable this and
press change children. Now we are going to
create the player object. I'm going to create a
new to game object, let's strike this to sits
above the ground here. I want to disable our
to generate lighting. So I'm going to go to the lighting tap and if there are no lighting
tab over here, you can just go to the window, and then I think it's
rendering lighting settings, and we'll open this window here. I'm going to disable to generate so it doesn't generate
every time there are changes in our scenes
here. Let's save the scene. Now, we need to create some
sort of navigation mesh here. So let's just open our naviigation window and
it's under AI naviigation, and this is the window here, we can just go to the big tab and press big button
and it will create a blue naviigation mesh
and this is the I as the layer we'll be
able to walk to and it can avoid the holes
as shown here, and this will be useful
for the player and also for the NPC for
moving around the sea. Now let's create a new folder and this one is going
to be called script. This folder is for keeping
all of our scripts over here and I'm going to rename the capsule game object
to be our player. Rename our player. The next thing we
want to do is we want to add the AI
agent component. This type and AV and here, Unity will sort out the F
mesh agent component here. This added. I think we will leave the default setting for now and we will
tweak this later. Let's save this our
scene and we are going to create a simple
click to move script. Go to our scripts
folder under our sets, and we want to create
a new C sharp script, and this will be the
player script file, and let's open this
C sharp script. Now we open our script here. We want to import the AI library first under the Unit engine
and using Unity engine, we want to insert
new line and type using Unity engine AI. With this imported,
we can create a new variable
type of pas agent, and we need this component
to grab our phase agent from our component and access
it via this script here. I'm going to just
call this agent. On start, I want to grab
this agent component. I'm going to get the reference. I'm going to fill
the component in our game object to be
referenced by this variable. Next time we are going to use
this phase agent component, we only need to access
this agent variable here. So this type get component. And I think it's NMS agent. Save the script. Now we want to create
a click method. I'm going to create
a new method oi on click to make sure whenever we click the
left button of our mouse, we want to execute
this on click here. Like this. Oh, sorry,
not get buttoned down, it should be get
most buttoned down zero is the left click and one is the right
click and two, I think is the middle click. Save this for, I'm
going to just to the this method and just
type left click like this. Yeah. And let's save
the script here and now we can go back to our unity and direct the script
to our game object here. I'm going to minimize
the component here so we can see it better. And I'm going to direct
this player script to our player game object. So save our scene and let's
try to play this game here. And now whenever I
press left click, as you can see here, the click method gets executed
by our left click here. Now we can extend this to create a movement for
our player. Okay.
3. 02 Basic Player Movement: So now let's create this movement routine
here in our script. Now, let's create a new
method called move player. And I think we should
input the vector three as our parameter
target position. And we can use this agent here. So just type a set
destination and we can pass the target
position variable that we add here as a parapeter
or as an argument. Now on our click, we need to modify this. First, we need to create a variable and call this hit and we need to
create another ray variable. I think I'm going
to call this to screen and this will be
a way from our camera. So in order to define
this variable here, we need to get a
reference to our camera. So I'm going to try new
private camera variable, and I'm going to call
this main camera. Upon start, I want to
grab this main camera. So we can easily grab
the camera using the camera class and
using the property main. And as you can see here, this is the first enabled
camera tag main camera. The camera has to have
a tag of main camera, and we can check
this if we select our main camera here
under the inspector. We can see the tag is untagged
we need to change this to the main camera and we can save our scene here and
once we change that, this code will work and
it will store our camera. And we need to store
reference in our start. So the next time we need to use the reference to the camera
or to the other object, we can just access it
by this variable here. And all of this reference is
being cache in the memory. So it makes the
program more optimal. So now we can use
the main camera and we have this
screen point to ray. So what this means is actually
from our screen position, it will shoot array
to our scene. Okay. And we can grab our
input mouse position. And we can use this ray to
recast to our scene and get whatever objects or position or information on where that
ray is intersecting. So we can just type
this f physics, as and open parentheses
and there is 16 overload for
this method and it will return bullion
whenever it hits something. So whenever it hits something, all of the code inside
this F will get executed. First, I'm going
to pass our ray, this camp to screen here. For the max distance, I'm going to use
the math infinity, and I'm not going to use layer mass at this
moment, but later, we are going to add this
and we need to pass our raycast variable here in the middle between the first perimeter
and the second one. I'm going to add a new coma
here and we can just type out it what this out means is actually whatever
happens in this method, whenever the ray hits something, that something has a
lot of information. It can be positions,
it can be normal or the collider or anything that information are being safe
inside our hit variable here. Inside this if we can extract any information that we need to create this
movement here. So let's just create curly
brackets and inside this, we can check whether if we
hit the glider is not no. So it means that if
we hit something, then we want to do some
routine inside this. For this time, we are
going to move our player using the player method that
we have created before. This type move player, and we can grab the hit point and this will
be the position, save this. Let's see if this works or not. Let's go back to our unity
and now if we press play, And then if you click somewhere, you can see that our
player is moving. So if I try to click somewhere behind this
box here, I will avoid. So let's get a better view here. I'm going to change
our camera position by moving our view inside
our scene editor here, and I think I'm going
to use this view here. Yeah. So select our
camera and press control, shift and F. It will line our camera,
choose this view here. So if we go to our game view, now we have the same view
as our scene view here. That is one trick to
position our camera easily. And we can save the scene.
Now if we press play here, if we click this point here, it will avoid our box
and it will go there. As you can see, and
if we press here, it will avoid and
it will go there. So, we have created a
movement for our player, but of course, we are going to expand this script as we go on.
4. 03 Player Movement & Interaction: So now we are going to
continue our lessons. And in the last video, we have created a basic
player movement system. And in this video, we are going to create a
simple interactable element. So let's just create
another capsule. Object we can pretend
this is an NPC, and just rename the
game object to NPC, and let's create a new script. And let's call
this interactable. Okay. And now we need to
declare a couple of variable. We can just delete the start
and uplet method first, and let's create a new public
void method, interact. And for this method, we are going to pass our player
script into this method. So we can just type
the class name, which is player script. And let's just call this player as a temporary variable
for this method here. And we are going to create a print debug
log to the console, let's just type this
click by layer. Okay. And we can
go back to unity, and let's add this to our
NPC here, interactable. Now, we need to add an event
here inside our click. Whenever we pick the
interactable or the NPC, we need to check that. Insert here inside
hit collider is not. We want to create a new
temporary variable, and it is interactable. And we want to grab if our hit
clider has that component. So we want to check
does the object that we click has this interactable
class attached to it. And if interactable is not null, then we want to run
the interact function, and we want to pass
the player script. And since this is
the player script, we can just type this keyword to pass this component here, this class two this
function here. And we can just type we want to move the player
to the hit point, Maybe we can just type
the game object name, type gave object
name and add it with this ten k. So let's go
back to unity and try this. Now if we open our console here, it's still compiling the script. Yeah. If we open our console here, if we click somewhere, I
will just move our player. But if we click the NPC, it will tell us that the NPC
is clicked by the player. But it doesn't move because
whenever we click the NPC, we only run this intra variable. So we need to expand
this interractable. So it works with
our player script. And the next thing
we need to do is we need to create a public void
inside our player Script, and this will check
if our player arrived on the destination for this example to
the interactable. And for checking this
out, it is quite simple. We can just return an
argument or a condition if our agent pass pending. If there are no path
pending currently, so we add this exclamation mark, So it means the path is
not pending currently, and if the remaining distance is smaller or equal than the
agent stopping distance. So it means that we've arrived, and if we arrive, then
we should return this. But I forgot that we should
have a return type here. It should not be void,
but it should be bullion. So I'm just going
to change this and this will return whether the
condition is true or false, then it will return that value. And now we can
create some sort of routine and just call this
wait for player arriving. And we are going to also pass the player script to
this core routine here. And since we pass this player, we can check while Player, the check is arrived. We can run this method, but we can make sure
if it's not arrived, we add this exclamation
mark in front. So it means it's not, then we can just delay or we can just return
the whole routine, and when it's true,
then it will run the code below when
the player arrive. So with this statement, we want to make sure that if the player is not arrived yet, then we want to
delay the routine, and once it's arrived, then we want to run whatever code that
we have below here. And for now, we can just
type player arrive. But this won't work yet because we haven't tell
the player to move. But in order to make
the player move, we need to declare a position
for the intractable. So we can just create
a new function, a public function that
return type factor tree, and this will be the position. Okay. And we can just return the position of the game
object, transform the position, add it with our
transform forward, and we can multiply this with some value because transform forward has a distance of one, and if we want to make the
object closer than one, we can just multiply it by
a value smaller than one, for example, value of
half unit like this. Okay. Let's save this. And now we can
whenever we interact, we want to move player to
the interact position. So let's just type the
interactable variable that we've declared here and get the
interact position like this. And then whenever we
execute this interactable, we want to run
this coro routine. So we can just start
the coro ruutine here and type the
coroutine method that we've declared below, and we need to pass the player parameter or
the player object here, that we passed from
the script here. And save this and now let's save our scene
also, and let's try this. Now we can go here and
if we click on the NPC, it will run to the
front of the PC. As you can see here, it showed
the player has arrived. Let me just show you again. If I click on, there is an issue here because
when we click the PC, the player arrives
show straightaway. So we need to fix that. Okay.
So yeah, I found the issue, why it shows the player arrive right away when
we click on the NPC. So basically, we need to
switch this line position, we need to make sure that we execute the interrat
after we move the player. So yeah, I'm going just to cut this line here and
paste this below. And why is it like this? Because for the interact, it will run the
routine right away. But if we assign move layer after the interact
is being executed, then this will return
true on the first frame. But if we move the player, it means that our
agent already has a destination and it wouldn't be arrived yet
on the first frame. So after we move the player
and then we run the interact, then this will return
false on the first frame. So it will loop this
coroutine until this is true, then it will print this out. And let's test this to see
if it's working or not. Now I can go here and there. But if I click on the NPC, as you can see
here, player arrive prints out after we
arrive on the scene here. And as you can see that 0.5
is near as a target position. So probably we can just create a new float
field or variable, and this will be the
distance position. And set this default one and we can multiply our transform
forward by this position. And basically, what
this interact position is returning is basically the position of our NPC, and it is added by the
transform forward. So Transform forward is
basically the blue axis and it's local to
the NPC game object. So if we rotate this, the transform forth will
be this direction here. So probably the player will
stop around this area, and if we rotate to
this direction here, then the player will
stop to this area here. So let's give it a try again. And I'm going to click this. And as you can see there, sorry, I think we don't
click. There you go. And if I change the distance
position, for example, two three, and go
somewhere else, and then click on the NPC again, You see, we have
a distance here. And if I change to also, it will change the interact
distance, as you can see. So yeah, that is basically the very basic script
of our interactable. Now we have faces that
detected by the script, whenever we clicked it
and whenever the player start moving and when
the player is arrived. So in the next video, we can continue to
expand this feature.
5. 04 Player Direction: So we are going to continue here with our player and
interactable script. And now, if we play this, we have no way to know in which direction our
character is facing. So let's create a new child
object to the player. And this will be a
cube here and I'm going to change its scale
on the x axis to 0.5. And also w x 0.5, and I'm going to leave on the z one and move this
slightly forward. And I want to make sure
that this cube here is aligned to our z direction
here as you can see, and this is set to local, so this is the local direction. And we want to do the
same for the NPC, so let's just create a new cube here and scale it the
same value as our player, and let's move this
on the z axis. So we know in which direction the NPC is currently facing. So let's just save this. And now, if we move the player, we can see the rotation right. And if we press this, it will go there, but it doesn't face the NPC, and I think one is to close, so we need to go to
our NPC game object, and for the distance position, we can safely change
this to maybe 1.7. And now let's try this again. Okay. And as you can see here, the player is facing to the NPC when it's arrived
so we want to fix that. I'm going to change this first. And for the player, I want to set the NaF me
agent component here, the angular speed to a
very high value here. So let's just try this seven 20. So whenever we move, it will rotate very quick to the direction,
something like that. This is better or maybe we
can increase this value here. Okay. Maybe 960 a good value here. Now let's go to our script
to our player script. So we need to create
two new variable here, and it's going to be private. The first one, it will
be a bullion variable, and we'll call this turning
and it will default to false. And for the second variable, it will be a quaternan variable, and this will be the
target rotation. And now we need to
create a new public method void and let's just call this set facing
or set direction, we are going to pass
a ectrore parameter, and let's just call this
the target direction. Now inside this method, we want to set the turning
to then we want to set our target rotation value to a new quaternion and we are going to use the
quaternion class here, and it has a method to
define a quaternion using the rotation method
and it creates a rotation with the specified forward and
upward direction. And we are going to
use only the forward. So in order to get our forward direction
to face our player, we can just the
position of the NPC, which is the target
direction here that we pass here and substract this
with our position. Okay. Let's say this after we
created this method, we need to update
our update method. Basically, what we want
to do is we want to check if turning is true and our transform rotation is not the same as our
target rotation. Then we want to
rotate our player. Let's just set the
rotation using the quadrant class and
it has a slap function, and this will interpolate
between the first rotation, the a rotation here to the B rotation with
interpolation value, the speed value here
with the float t here. For the first rotation, we are going to get
our carbon rotation, and for the target, we want
to use the target rotation. And for the lot value, I'm going to set this
to a higher value, 15, and we want to multiply this with
our time Delta time. So the speed will be
consistent across different specs of computers with a different frame rates. So it looks okay, another thing we need to do is whenever
we move the player, we want to set the
turning back to falls. Okay. Okay, yeah. Let's try this. Oh, sorry, we need to run this set direction and we
haven't done that a thing. We need to, we need to do
this inside our courting. So whenever the player arrived, then we want to run
the set direction, and we are going to pass
this interactable position. So just type transform the
position and save the script. So basically, when we arrive, then we want to
set the direction. So our player will be
facing this NPC character or any other object that has
this interactable component. Okay. Okay. So yeah, let's try this. Okay. I'm going to
click on the NPC here. Yeah. And there you go. As you
can see, once it's arrived, it will face the NPC here. So yeah, that is for
facing the direction.
6. 05 Base Action Class: Okay. So now we are going to create an action class
and we are going to create a base class of action and many action will
inherit from this class. So let's just create a new folder and I'm going to call this
folder base class. And inside this
folder, I'm going to create a new Cb script, and I'm going to
call this action. And I'm going to
name this with S on the word because I figured that if we
use the action name, it will conflict with the
system action name class. So just add an S at the ends, and for the class, I'm going
to rename this as actions. And I'm going to make this
class an abstract class. So basically an abstract class is a class that we
cannot attach to a game objects we can only attach a class that inherit from this class to
another game object. I'm going to create a
contract method and this will be the type of public abstract
and turn type is void, and I think this
will be like this. With method with an
abstract definition, we cannot create the body
code or the code inside. But all of the other
class that inherit from this action class must
implement this g method. So when we later iterate
an actions class, we can always execute the class, but each of the
class that inherit this actions class will have
different definitions of g. So we can create a vast variety of
actions component later. This is the base class, and I'm going to
go back to unity, and I'm going to create
a new action folder. Inside our script,
and this will be all of actions derived
script will be saved here. I'm going to create
basic message action. And instead of inheriting
from mono behavior. I'm going to change this
to inherit from actions. And as you can see
now, we have an error, and it tells us that it does not implement inherited
abstract member Act. So withFsotud, we can just type the show
potential fixes and then implement this
and it will implement a new method if you
are using other IDE, you can just write this down
and we can just remove this. And I'm going to delete the
update and start method. Okay. So now for
the message action, we want to create a string
array to show this message. But for this video, first, I'm going to create just
a single string variable, and later we are
going to modify this. Okay. Whenever we
run the g method, we want to print
out this message. Just type the log, and we can pass the
message variable to the parameters inside
the debug log function. Now we can go back to unity, and if we go back to unity, we can add this message
action to our NPC here. So just drag our message action. We can go back to
our interactable. And when the player arrived, we can execute all of the actions that we have
inside this interactable. Let's just create
a new variable, and this will be
type of actions, but it should be an array, so we can add
multiple actions to it and just call this actions. Here below the set direction, we can create a new loops, and we can look through the members of this actions
array variable here. Type actions length length, and length, we'll grab how many members do we
have inside this variable. On each of these entry index of, we can run the function. And this is why we
create the base action, so we can just implement the ag or we can
just execute the ag and this will run different a method across
different type of actions. And since all of the
actions will derive from the actions based class, this interactable will
recognize this ac method. Okay. Let's go back to unity
and let's test this out. But there is a neat trick here. We can do in the message action. We can add and attribute here
and just type multi line, for example, if we want
to set this to five line long for the message
inspector window here. Just insert five as the value. And if we go back unity, we will have a bigger
message window, so we can type a clear
message or anything, a dialogue or anything. Let's just type, for
example. Hi. How are you? And we can expand our actions under the interactable and
direct the message action. Now, when the player
arrives to the NPC, the console will print out this message. Let's
test this out. Let's go to this here. And yes, there you go. Once arrived, it prints
out this message. Later, we are going to create
a dialog window that will show this message and
also a dialogue button, so we can choose
between two options when maybe an NPC
ask for something, then we can just pass a yes
or no answer to it. Okay.
7. 06 Basic Dialog System: Let's continue from
our last episode and now we are going to
expand our message action, so we can show multiple message, and we want to also
create some sort of UI or UI for the message
on the game window. Instead of showing the
message in the console, we want to show the message
in the game window. Let's just create a
new UI component, and I'm going to
create a new image here as you can see in our game, we have this image, and this
will be our dialogue panel. I'm going to create a
new empty game object. And this empty game object basically will fill our canvas. Currently, we have this big. So I'm going to change this c preset by holding the button on the keyboard and press this lower preset and it will
fill up our screen here. And for the canvas, we want to change
the setting also, and I want to set the UI scale mode to scale
the screen size and set the reference resolution
to 1920 by ten ADP. Then we want to set this anti game object should
be our dialogue system. And we want to drag our dialog panel to be the child of our
dialog system here, and we can set this around 300 as the height or
250 should be okay. I want to set this to
fill the bottom area. I'm going to hold the L
key again on the keyboard, select this preset here. The one that above last preset that we
have choose before, this one, click and
now we have this. But I think I'm going
to make this back to 300 and set this again. I positioned correctly. We want to create a
gap on the left side, maybe around 25 and the right. And for the position, we want to add 25 to it. So just type plus 25, and it will calculate
the new position. Now we can change its color
to a dark color to black and set the alpha value
to around 0.5 or 0.3. Yeah. So now we have this
semi transparent panel, and we want to create
a new tax object. We want to use the Tex mesh P if you just create
a new project, then you need to import
this TMP essential. Just click this button and
it will prepare the package. And now it's Tex Mesh P is integrated building
with the newest unity. Okay. So now we can close this window.
Now we have this text. And I want to make
the text window to be the same size as
our pattern game object, which is the dialog panel. So hold the button
and then just address this three sets again and it will fill out this
dialog panel size. And we want to also add
a gap to the edges here. I'm going to set this
to 25 for each of the position like this, and this will be
our dialog text. I think we need a larger gap on the right side, also
on the left side. Yeah. And we can save our scene here. And now we want to
create a new script, which is our dialog system. So just create a
new C sharp script, and this will be
our dialog system. So now we can open the script. I'm going to increase
the size of our editor. And now we need to create
a couple of variables. So I'm going to create a
new SS field variable, and this will be the
type of text mesh pro. But we need to add the name space in front
of it and then search for the GI class here, and this will be
our message text. And we need to create
another field, which is a game object, and this will be our panel. We also need to create
a new private variable, and this will be
a list of string. I'm going to name this
current messages, and we need to initialize it by typing type of string here, and another private variable, which is an integer
type of integer, and this will be our message ID. And this will default to zero. So we want to create a static reference to
this dialogue system. So I'm going to create
a new properties here, public properties, and
this will be static. I will mark this
property static, and the type should be
our dialogue system. And I'm going to call this
instance with capital I. And I'm going to set
this properties to be a public yet but
with a private set. So we can set this instance
properties inside the script, but script cannot change this. A script can rip this properties because
we have this public eta. So yeah, and I'm going to create a new wake method for
initializing our static incense. So we can just type incense, and we want to set
incense to this. Okay. And now on the start method, we want to disable
the panel right away. So we want to set
active to false. I don't think we need
an update method, so we can safely
delete this method. So we want to create a
new public void method, and this will be
the show messages. And this will ask for
a list of string, and we can just call
this parameter messages. And now we want to set our current messages
to this message here, type current messages,
equal messages. And then we want to
display our panel here. So we can just type panel set act I forget
the semicolon over here. Then we want to start
a core routine, which we are going to create
the co routine after this. But I'm going just to
show multiple messages. And since we haven't
created this, it throws an error, but we
are going to create it now, so just type the
return type numerator because every coroutine need
to have this return type, and we can just copy
this name here and paste it and close it with
a set of parentheses. Now we can set our core routine. Basically, we want to look this coroutine while
our message ID, This integral variable
that we have created is less than our current
messages count. So whenever this is
still below our count, then we want to loop
this code here. But once it becomes greater than the
current messages count, then we want to stop
this core routine here. So while we'll make sure
if this condition is true, then we will keep
looping every code inside this while block here. So now we want to check if
the user press a space bar, or if the user pressed
the left mouse button, then we want to increase our message ID and
show the new text. Here on the start, we want to show the first
message that we have. So we can just access
our message text here, which is a text me
PI variable here, and we want to change
the text properties. To our current message and the index of our first
message, which is zero, and when we start running
this score routine, the message ID will be zero, and when we press space bar, it will increase this and
it will show a new message. So we can just copy
this line here, and whenever we increase this, we show a new
message or we change the text text with the new current messages with the new message ID value here.
Let's just test this out. Type panel set active two falls. Okay. I think we need to make
sure check whether our message ID is still less than our
current message dot count. Because C is the total members of our list here list of string, but the highest or the maximum ID is
usually count minus one. So if message ID
is equal in count, we don't want to increase
the message again. And with rot, we need to put
a yield return statement. So I'm going to
return our coat here. So it will look this while. Otherwise, it will
crash our unity, and let's just try this first, go back to unity and go to our dialect system and we can direct
this script here. And now I'm going to
direct our panel, and the panel is the child object that have
this background here. And for the text,
we want to direct this text mesh pro game object. Save this. Now we need to
modify our methods action. So basically, we want to instead of using
the debug log here, We want to access
our dialog system do incense this is
the benefits of using the static method static way of setting this up because
with static reference, we don't need using get component
or fine object of type. We don't need that at all. So yeah, we can just type the static name and we
can run the method, the public method that
we've created here, the public show messages, and we want to pass
our list of string. So I'm going to change
this to a list of string. And we want to pass this
message to our show messages. Now let's go back to
Unity and test this out. Hopefully, it works and
choose our NPC here, and now we have a list
of message. There's 16. I'm going to just change
this to maybe message, and I'm going to type you and the NPC will be an officer and maybe
we can just reply. Okay. So let's save our scene here and let's test this out. Okay. Okay, now you can see here, we have this window
dialog system, but let's press space bar. Okay. It shows our next message, and if I spacebar
again, it close. So yeah, as you can see, it works really well. And if we try to conversate
with this NPC again, it will give an error that
the index is out of range. So yeah, we need to
reset our message ID. Let's go back to
our dialect system. And whenever we set
this two falls, we want to reset our
message ID two zero here. Okay. So let's try this again. Okay. Good day, sir. Good day, to two. There's an issue
here, as you can see, whenever we try to. Okay. Yeah, it works, but if we click
again right away. Okay. We have to leave this first. For the clicking NPC issues, there is a slight changes we need to do and
it's a simple one. Here in our statement, we need to make sure
that we are using the mouse pattern down, not the get mouse pattern, but the get mouse pattern down. So it will only gets executed in the frame when we are
pressing the mouse down. Otherwise, if we using
input mouse pattern and it might get executed
twice in a row or more, and this will cause issues. Okay. So yeah, that should be our basic dialogue
system, but later, we are going to extend
this to Heaven options, so it can create some
sort of dialogue box and it can give answer. If the NPC asks a question, we can answer it with
two options. So yeah. Okay.
8. 07 Check Mouse over UI: Okay. So now we are going to continue to create
our dialect system. And in this video, I'm going to create
an extension method that we can use on other script, and this method we'll check if we click on
top of the UI or not. So, because as you can see here, if I try to conversate
with this NPC here, and then if the user tried
to click on the UI here, it will show the next message, but it will also move our player to this point here,
where we click. This would be an issue
and we need to fix that. So I'm going to create a
new a folder extensions. And I'm going to put our
extension class here, and this will be a static class. Okay. And let's open the script. And we are going to delete
this mono behavior, so it doesn't inherit
from the mono behavior. And this will be a
static class atension we are going to
delete our method. And for checking if our
mouse is on top of the UI, we need to import the
unity engine event system. Okay. And we are going to create a new public static
and this will return a bulon going to call this
mouse over UI method. And we want to return
this event system. Current is pointer over game object, since
this is a method, we need a set of parenthese
this will return true if we are on top
of UI and it will return false if we
are not on top of UI. Now if we go to our
player script here, we can add a new condition
in our left click here. So now we can add
a condition using the N operator and we can use our extension
class static class here, and we can check if the mouse
is over UI method here. So this will written through if it's on top of
UI, but we don't want that. We want to make sure whenever
we click outside of the UI, then the player
is going to move. So just add a exclamation sign in front of this statement here, then this will negate the condition and should check whenever this
value is false, and under our dialogue system, we want to make sure also
we want only to left click and if The mouse is
on top of the UI. And this time, we want to make sure if the mouse
is over the UI, it's not outside of
the UI but on top of the UI because we want to click on the message
window here. Let's go back to
unity, and let's play. Now I'm going to try to
talk to this person here. And if we on top of the UI, our player will stay there. But if we click somewhere
else, our player will move, but this UI will stay so we can click on the
UI again and to hide it. Okay. Yes. So this is a neat trick using
this event system here. And with static class, we can create a
static method that can be used on many classes, and we are going to
expand our extension here with many other
functions later. Okay.
9. 08 Message Dialog Window: Y. Okay. So now we are going to continue
with our message action, and let's open our script here. I've already opened
the fiscal Studio, and let's go to our
message action. So now we want to create an
option for the message to have a dialogue system the player can choose between
the right or wrong answer, and we can launch an actions depending on the
reactions of the player. So now let's create a new buls
field and type of bullion, and we are going to call
this enable dialogue. And we want to create
a list of actions. And this will be
our yes actions, and we want to
create another one, which is the actions, and we are going to pass this
value to our show messages. Now we need to modify our
show message method first. Let's go open our
dialogue system. Under our show message, we want to pass our dialogue, and we want to also pass our
list of actions for each of the actions and also the list of actions
for the actions. Okay. But we want to add a default value
here in our parameter, so we don't have to pass every
time we use this method. We can simply assign
by default to u by adding an equal sign and then keyword null
after the equal sign. For the action, we
want to do the same. So now, basically, we can opt out if we don't
have any actions. If we only want to show
messages without dialogue, we can just opt out using the actions here in
the show messages, and we will see it here. Okay, now, I think we need to modify
the message action first, so let's go to message
actions class. Here, we want to pass
the enable dialogue, let's just pass enable
dialogue, enable dialogue. And for the yes
and the no action, we can pass here,
both of the list. And if we didn't insert
any actions to it, then it will simply pass the nual value, so
it should be okay. And save this. And now we need to modify
the dialect system. First, we are going to import the Unity engine UI name space. So we have access to
the button class. And here below our game
object panel field, we need to create a new field, and this will be
a type of button, and we can create a new button
and also the no button. And now we can modify
our show messages. Basically, before
starting the routine, we want to assign value
to our button here. We want to check if
the dialogue is true, then we want to
pass those actions. Otherwise, we don't need to pass those actions because there
won't be any dialogue. And for this to work, we need to create a
custom method here below. So I'm going to
create a new method, and I'm going to call this
assign actions two buttons. And here we want to
pass a list of actions. And let's just
call this actions. And we want to create a new local variable
here inside this method. And let's just call
this local actions. And this will have the value of our past actions from
the argument here, and we want to look through our local action
do count because we are using list
and the length of this list or this array here
is using the keyword count. And now we can look through the actions and on each member
of the actions, we want to run the method. And we need this
because we are going to pass this method
to the button here. Now, if the dialogue is enabled or if the message is
using a dialogue, then we want to set our action, but button, click, and this is the event and we want to first remove all
previous listener. Then we want to add the new ten sorry using the on click
event and then add listener. Now we can create
new delegates here. With a curly brackets, and we can insert any codes
to this statement here. So the first thing I
want to do is I want to run the assigned
actions to button. And we need to pass a
list of actions here, so I'm going to pass
the yes action. And then on the second line, I want to disable our panel. We can run the set active method from our panel game object
and set it to falls, and as usual, put a
semicolon at the end, and we can copy all
of this here line. And then we can change
this to the no button. And also for the second line, we change this variable
to the button, and then we want to
pass the actions, and we want to set
this to falls. We can just pass this or
we need to check this if the actions is not null, then we want to pass the action. And same goes with
our no actions. So we want to check if
no actions is not null, then we want to
pass the no actions to the signed actions to but. Otherwise, this code might throw an error if our local actions is null and trying to lot that. Okay. So we are finished
with the show messages, but we need to modify
our core routine here. We need to pass a bulon here and just name this
use dialogue. So we know. Since we have this
dialogue, bon, we can pass this whenever we
execute the core routine, and since this value can be true or false,
we can check that. We can check if use
dialogue is true, then we don't want to deactivate
the panel right away, but we want to show our button. Let's just access the
transform the parent the game object set active
two true otherwise, then we want to set the
panel to the activated. We want to hide the window
if there are no dialogue, and we only want
to hide the panel. Once we click the
button if we are using any dialogue system. One thing we need to make sure is whenever we
show a new message, we need to hide the
button group first. Why are we accessing the parent? Because later, we are going to put those two button,
the yes button, and the no button to
a anti game object first before attaching to
the dialogue system panel. So we can just hide it when we show the
message first time. We can set this to
false and save this. Now let's go back to
our unity and open the Canvas window going to
access our dialogue system. Here. And we are going to
create a new empty game object. We can just put it anywhere. I'm going to put it below
here using this e st here. And let's call this
a button group. Let's create a new button. I'm going to create a new button here and I'm going to
make the hight to be around 70 and for the
width to be around 240 or maybe longer, 350. I'm going to remove
the source image, so we don't want to
use any source image. I'm going to set
this color to black. Also going to remove the text. And we are going to add a
new text mesh pro here, and let's just fit
the transform. And we can use to size. And also, I'm going to add an outline to our
button game object. So let's just type
outline to the component. And for the outline, I'm going to set
this two wide and with the effect distance value, also five on the y. So we have a border, and you can see here in the game,
we have a nice button. And I'm going to put
this on the right side, and I'm going to push this. This would be our button. Let's duplicate this
by pressing Control D, and this would be our no button. And let's move this to the side. Yeah. Okay. So now
we have two button, and we need to create a couple of more
modifications to the code. So let's go back to
our official studio. And under the message action, we want to create a string
for the button text. So let's create a new string. And this will be the
tax and the text. We are going to
pass this, but we need to modify our
dialog system. Inside our show message, we need to add a new
string after the actions. This is yes, and we can set
a default value to yes. Like this and no string, we can set a default value to. Now we can pass to our
mesh pro on our button. But we need to create
a variable for it. Let's just add this. Let's call this tax and the tax. Now if we are using dialogue, we want to modify the text. So we can just access our text, the text to be our string that we pass
here on our parameter. Copy this and for the
we can change this to our string like this. And for the message action, we can pass our text
and no text to here. Now we are passing all of
the field that we have declared or defined on our message actions and
hopefully this work. Let's go back to unity and
save this for the NPC here. Now we have this under
our message action, we have a couple of
new fields here. Let's create two new message
action for testing out. For the first one, we are
going to enable the dialogue and we can just set this
to and this to know. Now we can create a new message, and if we are responding yes, then we can show message nice. If we respond no, then we can show
message bed we can direct this message action to our yes action and
direct to the no action. Now it will have differ response depending on
our play or reaction. Let's just save this and play
let's go to our NPC here. Okay, there is an
error. Oh, yeah, sorry. Under our dialog system, we haven't set this text
and also the button. Let's just direct
the button first to each field,
corresponding field. Expand our button,
and for the text, we direct to the tax, and for the text mesh object, direct this to the text. Save this and run it again. Okay, so let's go back
go to our NPC here, and it will show message. And when we click and
then we click again, it will show a text. Let's just press. Yes. Okay.
There is an issue here. Okay, so there is a slight
mistake in our code here. Instead of running the
assigned actions button first, because if we run
the actions first, when I click button and then set the panel to disable,
it will cause an issue. So we need to make sure that this panel set active falls is executed first before we are executing the actions
for each of the buttons. So I'm going to move this on top of above the
actions code here, and same goes with the actions. And save this, and
we need to also move our code where it handles
to reset the index message. Let's just cut this code here and put it on top when we
started showing message, we reset the index here. This makes more sense, and it's much better because
with the previous code here, when the message index equals zero is still on this coroutine, it can cause issues. Let's save this and
let's go back to unity. Now let's one game. Now if we go to our NPC here, it will start dialogue, and if you click and
then click again, it will show the button. If we press, yes, it will
show the nice response. But we cannot click anymore
because the button is gone. We need to click
on the NPC again, and then try What
if we press no, and it will Response bed. But there is still one
issue here as you can see, if we click once and then click twice, this
is the last message. But to show the dialogue, we need to press it again
and we don't want that. Basically, what we want to is whenever we arrive
to the last message, the button showed automatically together with the last message. Let's go to our code
here and let's fix that. Here in our routine in our show multiple message routine under the
dialect system, we want to check if The message ID, our message
ID is equal to our con, but substract with one because the last message index is always equals to the
con substract by one. So when we arrive on
our last message, then we want to show
this button here. Okay. But we want to do this whenever the
use dialogue is true. We need to add a
condition use dialogue, and we arrive on the last message on our
list of strings here. Now we can just remove
this and we want to disable the panel if
we aren't using dialogue. We can add a exclamation sign here before the use
dialogue keyword. I used dialogue is false, then disable the panel. Now I think this should work saved this and
go back to unity. And let's play this again. Go to our NPC and once
we have dialogue. Yes, as you can see on
our second message, it shows right away the
Button cannot click anywhere. But if we go, the message stay. So we need to fix this also. But it's okay right now. We can just test this
out. Yes, it works. And if we talk again,
we can press no. Yeah. So we want to test also if the custom
labeling works. So let's just set
this back to normal. And for the first
message action, we want to type
awesome, let's say, for the no text, we can just use Let's save
this now go to our NPC, and there you go. We have custom button text. Okay. Another thing
we need to do is we need to make
sure whenever we interact with an NPC and then
we suddenly we cancel it. Let's say by moving
to somewhere else, we need to disable our message. I'm going to create
a new public void it message or hide dialogue, and we want to set the panel, set act two false. And this needs to be public because we want to disable
this from other script. Since we have a static pattern
with our dialogue system, we can go to our player script. Whenever we move the player, we can hide by accessing the dialogue system,
instance dialogue. Save this. Now
let's run it again. And try clicking on the NPC here and don't click on the message, but
go somewhere else. Okay, let's get disabled. And if we talk again, it will show the message again. And if we click on
the second one, if we go, it will also disable. But since the
message ID is always reset to zero whenever
we show new messages, it will start the
conversation all over again. Okay. So yeah, that is for our dialogue
system, and later, we are going to create a
custom editor for this to make this much more neat and easier for us to design the
game later. Okay.
10. 09 Item & Database: Or Okay. Now we are going to create our item class and also item
database scriptable object. While we are using
scriptable object, because scriptable object
is very easy to share across the scene and the
content or the values. The data will persist on
each scene and basically we need a reference to all of the items that we declare
on this game here, and item database will be
different to our inventory. Inventory, we can add or remove item on runtime when we play
the game, but item database, we need to define this on the
editor and once it's built, I cannot be changed the
item Davise basically, item database will hold all of the items
available in this game. Let's just create a new
folder in our script folder. I'm going to create
a script folder. I'm going to call
this script object. And inside our base class, we want to create a new C sharp, and let's call this item. And we are going to open this item script by going back to unity and double click on
our newly created C sharp. This class, we are going to
use mono behavior because this is basically a
custom class that we are creating to
define our item. We want to mark this
item serializable, so we can just add square brackets and type
system serializable. Basically, if we mark
a class serializable, then we can show this value
on our inspector later. It means that unity can
serialize this class. Now we need to create a
new serialized field. And this would be an ID. So we can call this item ID. And for the second field, this would be a type of string, and this would be the item name. And the third one
is also a string, but this will be the
item description. And another thing we need
to add is the pride. This would be the
icon or the image, itempt The next one
would be a bullion, and we call this low multiple. There will be a couple of items that we can
have more than once. So we need to have
this bullion flag, and the last one will be a integer and this
will be the amount. We can only change
this later if we enable the allow
multiple flag here. Safe now we have
this custom class. I think we can just remove this two libraries
that we are not using save this and
go back to unity. And under our scriptable object, we want to create
a new CSO script, and this will be
the item database. Open this and we want to
change the inheritance, instead of mono behavior. We want to set this
to scriptable object. And we want to create
asset attribute. Let's just type
create asset menu. Basically, we have these
properties for the file name. I'm going to set
this is a string, just type item database
and them name. We can set this to customa
Blas item database. Okay. Now we need to remove
this two method here, and basically for
our item database, we want to create a
new serialized field, this would be a type
of list of items, we can call this
items and we can just initialize the second field is going to be a list of string, and this will be the items name. And initialize this also. But we are not going to change this value later and
we are going to create a custom editor so we can just hide this in our
custom editor later. But we need this list of
string to create a pop up later when we are creating a custom editor
for our inventory. Let's just save this
and go back to unity. Now, if we right
click on our asset, we have this new custom
data menu under create, we have this custom data and
we have this item database. I'm going to create
a new data folder, And inside this data folder, I'm going to create
this item database and now if we press enter, it will have this item database. And as you can see
here, we can add members item and we can add definitions to
each of the items here. But we are going to
automate a couple of processes here using
the custom editor. In the next video, we are
going to discuss how to create this custom editor
for our item database.
11. 10 Basic Custom Inspector: Okay. So now in this video, we are going to discuss on how to create a custom inspector. And here, I've already created some temporary script
called some class. And inside this script, we
have a couple of variables, and we are going
to try to create a custom inspector for
these fields here. So let's go back to our unity, and in order to create
a custom inspector, we need to create an editor. And all of the script that
derive from editor class, which most of these
classes are going to handle the way we
inspect our class, creating custom inspector here, need to be reside or to be
safe inside this editor folder because assets that resides in this editor folder will not get included in the final build. Let's just create
a new CSP script, and I'm going to rename
this to some class editor. First, I'm going to create a new empty game object
in our scene here. We set its value, and let's just drag our SM
class here to our game object. Here we have this inspector. This is the default inspector that gets rendered by unity, and we can customize this. Let's go to our editor folder and let's open this
SM class editor. Now we need to import or we need to using the Unity
editor namespace. We need to also make sure that this class derived
from the editor class. We need to tag this
class with an attribute called custom editor
and open parentheses, and then we type the
keyword type off. Then the class that we are going to create
custom inspector. In this case, we
are going to create a custom inspector for this
class here, some class. I'm just going to copy this class name and paste
this inside the parentheses. Save this now let's create
the custom class. Okay. So as you can see here, we
have two types of fields. And basically, the first
one is a public field, which I don't
recommend this because if you need a field that
accessible to the inspector, I would recommend using
the serialized field because if that variable should only be changed
in the inspector, then this is much safer because other script
cannot access this. With this example
here, the top one, communication with other
script may cause issues if we modified some of this
variable from other script, and maybe we forgot with
those modifications, it can create issues, and it will be hard to
track the bug case. So let's go back to
our subcase editor, and I'm going to show
you two method on how to create a custom inspector for both of this kind of variable. Let's delete all of
this default method, and let's create a new
override if we press base, official studio will
show us auto complete and we need to create the
uninspector GI and safety. Basically, this is where the
custom inspector happening, and this base method is for
drawing the one that we have, the default inspector here. If we just comment
out this code here, as you can see and save
it, go back to unity. We can see that now if we
select this game object, you can see that some
class doesn't have fields. Somehow it's hidden because we oprite the basic
inspector drawing, GI, and we don't create
a substitute for it. Now, first, we need to create field with the
type of our class here, some class, and it can
be named anything, but usually we call this source. And we can create
a void on enable. Then inside this enable, we can initialize this
source by assigning a from the keyword target and target is basically the
object being inspected. But since target is an object and source is a
type of some class, we need to cast this
object to some class, so we can just add a
type casting in front of it and type the
class here and save it. Now, once we declare this or we define this source and
also initialize it, we can create a
custom inspector. For the public fields, we can easily type
the keyword source, and then the player name, and this will be our string
variable player name, and we can create
a new inspector by typing the class editor Glayout then we can use the
text field or text area. Usually for typing input,
we use text field, and then we need to pass
this variable inside this. Here. Save this, and we can
add level in front of it. So we can put label in
the first parameter. So the string text, type let's say, we
can type player name. Okay and then save this. Once we save this, if
we go back to unity, You'll see that after
our script is updating, we have this player name field. Same goes with our float factor. For float, we can easily
type speed and type editor GI layout and we have this float field and just
pass this source speed. And for the factor
three, of course, we can let's say we want to
access the player position. We want to draw the
custom inspector. We can just type editor
GayoutFctor three field here, and we can pass the value
right away like this. But we have an error. It needs a label. So let's just add
label in front of it. Okay. And this will be our
player position, for example. Let's save this and now we will see that once our
script is updating. We can see There you go. We have this custom editor. For game object, since
this is an object, the same goes with the
type of sprite and stuff, we will need to create a
new editor object field. Let's just type variable, the fields that we
want to set it for this case is player
prefs and the editor. Layout object field, and here
we need to pass our source prefep here and let's check
the other overload method. This one, this is the
object and the type. We need to put a type
of game object here. But still we have
an error because we cannot convert this
object to game object, so we need to cast this as a
game object in front of it. This is an obsolete warning, I think we need to
add a parameter which is the allow scene object. Basically, what this allow scene object is for
this inspector. Should we allow the designer
or whoever using this script to pick scene object or it has to be a prefab
inside or asset. If it's true, then we can pick
an object from our scene, and if it's false,
then we should only pick object from
our asset folder. Let's just type through here. Once we type through, there
are no warnings anymore. So, let's go back to unity. Then as you can see here, we have this game object slot. So this is basically
how creating G and we can create
a group like this. So if you type I lay
on top of it and then we can type let's
say begin vertical, this is vertical group, and then we close it
with G layout, vertical. This will group and
nothing will change now, but if we pass a string called box inside the argument of
megan vertical method here, it will create a box for all of our fields
here as you can see here. It creates a nice
dificient group. This is basically how we
do with the public method. And now we are going to try to create a custom inspector
for the serialis field. The problem we cannot
use this method here, because if we type
the source and we search for the
variable, as you can see, it doesn't show because
this is basically a private field that has been
marked as serialized field. Now we need to find a way
how to create this variable. First, we can create a
serialized property object here, and we can type player name
here, and we need speed. Also, we need player position. And then last one we need
to create player prefabs. Okay. Now for each of
this property here, we will find using a
serialized object. For the player name, we can simply type like this. We use the serialized
object, and basically, this is a serialized object representing the object or
objects being inspected, like target, but it can also represent the
serialized object. I'm going to re type
this and enter this, and then we use defined
property method. Inside this fined property, we need to pass the name of
the fields here in string. Just type this here. Okay. Now we can just
duplicate this line here, paste this a couple of time, and change this to speed, change this to player position. Change this player prefects,
for the speed here, we can just type
underscore speed, and this would be as
underco player position. And this will be our
S player prefabs. The naming it has to match with the one
that we have here. We if we change one
of this field here, we need to also change
this accordingly here. Now, let's create a new
grouping here, G layout. Sorry, begin vertical, and
I'm going to create also a box here and close
this with the vertical. Now, save this. Now in order to create a custom inspector
for this type of object, we can just using
the editor I layout, and then we use a
property field. For this property field, we need to only pass this
serialized property. We can pass each of this
field here together. Let's just copy this field, paste this a couple of times. Then we can put each of this property field that
we have declared before. And it will automatically
render this. Let's go back to
the Unit editor. Now once it's finished compiled, it should update our Suss. There you go. We have this here. But the problem is it has the default name
from the script, which is S player
Speed and stuff. If we want to change that, We need to create
a custom label. We can create a custom label
by typing a new I content, and then we can type a label name inside of
this parenthese here. Let's type the player name like this and we can copy this I content and paste on all
of this property field, and this should be the
speed or player speed. This will be the
player position, I think, and this will
be the player prefabs. Let's go to our editor here. Now once we finish update, it will have a custom name as
you can see like this one. If we put a label for this object field and
the speed field here, it will render the same
as the one below here. Now, since we are using
a serialized property, we need to put and apply
modified properties here. Because if we don't
put this method here, any changes that we've done in the inspector would not be safe. So make sure if we use the serialized property
here, put this below. And in this example, we are going to mostly use this method in order
to create a custom. One thing though,
one neat trick is, we can create a button, for example, if
you create button, you need to create
statement and then row the button inside
as an argument. And you can type the
name inside this. Let's say randomize speed, K, and this will return
true whenever we press this button
in the inspector and we can run some code inside. With this button, we can
create custom action here. For example, we can
change our source speed here using a random
dot range 5-25. If we save this, go
back to our unity here. Once it's update, it will
show this button here. If you press this,
as you can see, we can create random
number. Okay. And if we want to
change the value of the serialized
properties one here, we can just type properties. For example, speed here, the serialized property, and we access its value by typing
the float value here. Save this and this will do the same for our serialized
field one here. Yeah, that is basically how
we create custom inspector.
12. 11 Item Database Inspector: Okay. So we have learned how to create custom
inspector on our last video. So now let's start create a custom editor or custom inspector for
our item database. So first, I'm going to go to the scripts folder and I'm going to create a
new editor folder. And let's call this editor. And all of editor related
script or classes, we are going to put it
inside this folder. So let's create a new C script, and let's call this
item database editor. Once it's created, let's
open this on fisico studio, I'm going to increase
the phone size. Now from our previous video, we need to import the
Unity editor namespace. And then we need
to make sure that this class is derived
from the editor class. And we also need to tag this as a custom editor and set the type off to
our inspected class, which is the item database here. Save this and we are going to delete all of this
built in method. Okay, now let's check our item database and let's open our item definition class here. Let's just go to definition, and now we can inspect
the fields that we have and which of
this field that we need to show in
our custom inspector. Since most of the fields
is serialized field, then we are going to use
the serialized object. So now we have
declared this editor. Let's create this
custom inspector. First, we need to
create override. On Inspector GI, and we want to create a couple of
button to create new items. Let's just comment
this out here. We need to create two
serials property, and the first one
would be the items, and the second one would
be the items name. And we need to create
an unenabled method to find this property here. So for example, for the items, underscore should be s here. And for the S items, we want to find properties from our
civilized object and use find property method, and then we want to search for the items array or items list. And for the items names. We want to also find
the properties here. Let's check our item database. Okay, copy the Yeah, copy the fields name,
to be on the safe site. Then we want to look
through our items. We can look using the
serialized property. We can access the array size, and it will return the
members amount from our list or ray if
it's another ray. Then we want to draw
the item entry here. We are going to draw the item, but first we are going to
create a new button here. Maybe we should create
the button on top. So let's just use the I out button and then type
the button name at item. And we can create actions
to create an item here. We need to create
a new constructor. Since all of the item
is serialized field. Let's create a new constructor, type public here,
item, the name class. Then we need to pass the constructor here
inside the parentheses. We need an integer with
which is an item ID, and then a string, and this should be the
string, description, and then sprite and also I think they should do up to description or let's just create
it like this first, and then we can pass the item ID to we are passing whatever value that we put it here later when we
construct the item, and we pass this series
field of this item ID here. Okay. And pass the name
item name item name to our name parameter
item description, and this will be
the description. And since this have
different name, we don't need this keyword here, so we can safely delete
this and save this. Now we can go back to our item database editor
and we can create a new item and we need
to pass our item ID. For the item ID,
I'm going to pass this array size because if
we don't have any members, then this array size will
be zero and zero is usually the first index of any
kind of array data. So it suits our need and whenever we increase
the array size, the item ID will also increase. For the name, we can just put an empty string and also
for the description. And now once we
create a new item, we need to We need to pass this item
into the item list here. But since this is a
serialized field, we need to create
a public function. So let's just create a
new function add item, and we can pass an item
data for our item. And whenever we
create a new item, we can add this
to our list item. And then we need to pass
also the item name. So just type items name, add and we can pass the item Okay, just add an anti string first. And we will change the items names as we fill out our custom inspector later. And now we can just
run this method. But in order to run this method, we need to create a source. Object for our item database, create a new source object
and we can initialize this on enable source and
then equal target, but we need to cast
this target to our object class.
Inspect object class. Cast this to item database, and then we can run the item method and pass the
new item that we have here. Okay. And now we need to
draw an item entry. Let's just create a new
method to draw this. So I'm going to call
this item entry. And since we are going to pass a serialized property
off from the array size, we just type here
serialized property item. Okay. So now we are going to draw this using we are not going to create a property field for
our item ID because our item ID will be set automatically when we
construct a new item. So we want to use only a
label ID to show this. Let's just create
a new GI layout, and this would be
a begin vertical. We want to create one box
for each of the entry, and I'm going to pass a
box string parameter. And at the end, I
want to close this with the vertical method. Okay. Now we can safely create our interface inside this blocks of code here. The first we need to do,
we are going to create an editor GI label field right away because we
don't want to modify this. This will be a static inspector. We cannot change the value here, and this will We can put a text here and we
can add this with our item, find property relative and get our item ID and then
get its integer value. Since item ID is an integer, then we need to extract the integer value using the
integer value properties. And we can set this
two a very narrow UI, maybe around 75
should be enough. And this is in pixel, if it's too narrow, and it clip our
information here later, we can just increase the size. I'm going to create a new
UI layout in horizontal. But without the box perimeter, I want to put this item ID in the same line with
our item name entry. Now I'm going to create a new editor GI
layout and this time, it's going to be a property
field because we want to able to rename the text area. Now we can just fine
property relative and get the item name like this. But now we need to close this with a GI
layout and horizontals. Otherwise, it will
throw an error. Let's just create
spaces between, so it's easier to read and
save this. Now we have this. We can just type item three
inside our loop here, and we can pass as
items index of I here. Okay. Oh, sorry, not in x of, but since this is a
serious property, we need to use get
array element at index and pass the index inside the parenthes save this and let's go back to unity under
our data script object, select the item database, we can see the custom
inspector here. Basically, we have
two item, I think, if we go to the buy let's
just reset this two zero first for the item
size and go back to normal. And if we press that item, Okay. It doesn't work. Let's check what went
wrong with our code here. Once we add item, they should update. Sorry. We need to type
serialized object, apply modified
properties at the end of the inspector GI
because we are using the serialized property here.
Let's go back to unity. Okay. Let's remove
this first here. Okay. And add item. Okay. Okay. Once I click
Add, it actually adds, but, it shows when we press again, go to the bug. Yeah, as you can see here. It creates new entry, and it assign automatic ID, and the first one is zero
and the second one is one. And if we go back to normal, it will show two entry here. As you can see, but
there is an issue. Whenever we press at item, show it doesn't update
the window automatically. So we need to inspect this. Okay. So after adding the serized object apply
modified properties, we need to also add a
seriz object update on top of our inspector GY. So any changes, it will
update on the next frame. Yeah. So let's save this
and go back to unity. Now if we highlight the
item database here, and let's clear
the member first, type zero in this item
on the debug mode, go back to the normal mode, and if we press add item, it will add new items. There you go. Now we need to modify this add more entries, and since we already
have this item name, we can just copy
this line here and below on our horizontal, we can type the item
description or I'm going to copy name here. And then I'm going to create a new begin horizontal again for grouping our sprite
and the bullion total. Let's try to create
our sprite first. We can create sprite using the property field here
and type the name, which is the item sprite. But With this case here, if we go back to unity, it will only show our sprite as a name entry
here as you can see, it's not an image type. So we can modify this instead of using
the property field. We use the object field, like the one in our example, when we creating an object field for our game object field
in the previous video. But here, we are going to Get the property relative
from our items sprite. Then we want to access it
object reference value. Then this time we want
to use the object field. For the seres property, we want to grab this
item sprite here. Okay, now we declare this, we need to declare a
couple of argument. The first one would be
a string for the label, and the second one
would be the object, and we'll need to access the object
reference value again. And I think I'm going
to break the line here. And then after the object, we need to specify
the type, which is, And for the last entry of
our parameter argument, we are going to
set this allows it object to fall
since it's sprite. So whenever we assign
sprite for our item, it should be from our asset
asset folder. Save this. Now let's go back to Unity. Now, as you can see here, we can see the sprite window, and we can pick and
sprite like this. The last thing we need to
do is not the last thing, we need to add Bullion toggle. Inside this big horizontal, we can just copy this editor
property field and pass this B field its name to the fine property
relative argument here. Okay. So if we go back to you today, we can see the window. Okay. There you go. We have this items sprite
and allow multiple. We want to create some
sort of delete button. So let's just put
it on top here. Let's create a new button. I side by side with our
item name field here, just type I lay out
button for the string, we are going to set this two x. So and then we want to make
sure the width of our button. It's small, so just
pass the width and I'm going to set the width to 25 or
20 should be enough, and we want to delete the item. Now in order to delete the item, we need to access as items
here and delete the entry. First, we can access the item names and we can just use the delete array
element at index of, and this would be our item ID. We can just grab
this integer value. Since the item ID will always
be equal as the index. And now we need to
also delete this one and pass the and
once we delete this, we need to recalculate the ID because if we
delete the index, the rest of the object
that we already created, will not change
automatically the ID, so we need to create a
method to recalculate this. Let's just save this here below, I'm going to create a
new recalculate ID. Basically, this is quite simple. We need just to look our
item cells property. And then we need to
make sure that the items and get the
element at index of. And once we get the index of I, we need to find the
relative property, which is the item ID here, then we need to change
the integer value. As you can see here,
the property of our integer values can be get or set so we can
change this value here. We need to change this two. Because the index will
automatically shifted, but the item ID will not
be automatically changed. So whenever it's shifted, we want to set the
we want to set the item ID to be equal as
our value or our index value. And for the items name here, we want to also updated. And sorry, since this
is a less off string, we can just grab the
string value right away. We don't need to use the
fine property relative. And we want to set
this string value here to our item name. So we can use this and we can grab the fine
property relative, the item name and grab
its string value. Like this. I'm
going just to break this here so we can see
it better in our code. Now, once we created
this method here, we can just run the
recalculate ID. Once we recalculate ID, we can just return
this function. Once we recalculate this, we can ignore this line here and it will render the whole item
entry in the next frame. Okay, let's go back to unity here and wait
until it updates. Now we have this x button, and let's give it a name.
This one is chicken. For example, sorry, chicken. Then let's say this is a key. This one is a gun,
and this one is a chest and this one is money. If I delete this key here, You see, the gun
will have item ID of one and the chess
will have item two. And if I did the chess, the money will change this
I automatically to here. And if we add a new item, it will create a new entry. Now with this custom editor, we can create a very
nice custom editor, and we can set up our
item entry and all of the item that will
be available in this game or will be
used in this game, we need to declare this first
inside this item database.
13. 12 Base Inventory: Okay. Next, we are going to
create inventory system, and this inventory system will be also a
scriptable object. So let's just create a
new script here inside our scripts folder under
the scriptable subfolder, right click and then press
create and then create a new subscript and let's
just call this inventory. Open the script here,
and we want to change the inheritance to
scriptable object. We want to also
create an asset menu we can just copy from this item database attribute
here and paste this here and change this
value to inventory Let's call this inventory for the menu we want to delete both of this
start and update method. The first thing we
want to do is we want to create a serialized field, and this will be a
type of item database. Let's call this item
database with lower sei. Basically, this will be
the slot for accessing or reference this item database
object in the inspector. Because for our inventory, we will need this
item database data to create each of
the items entry. All of the item entry
will be based on the item in our
item database here. Let's create a new
serialized field also and this will be a list of item. Okay. And this will
be our inventory. Okay. Now we have this
inventory created. Let's save this and
go back to unity. If we go back to unity, after it to finish compiling, we can go to our data folder, and then we can create a
new scriptable object, create under the custom data, we have this inventory ta, and this will be our inventory. Now as you can see here, we still have the
default inspector and we can add
inventory over here, for example, and we
can create item. But we don't want to
create this manually. We want to copy the item automatically from the item
database and for this case, we need to create a
custom inspector for it.
14. 13 Inventory Custom Inspector: Okay, so now we are
going to create a custom inspector
for our inventory. So let's just create
a new class script inside our script editor folder. Let's call this inventory
inventory editor. And let's open this. First, we want to make sure that this inherit from our editor, but we don't have
that yet because we haven't using the Unity editor name space,
so we need to do that. Then type inherit from editor, and we can safely delete
the start and uppit method. Now we are going to create
a couple of methods. The first one would
be the nenable this is for initialization. The second one would
be an overwrite for inspector I this is where
we draw our inspector. We can just comment this out. Okay. And let's tech
the the item editor, but let's just move to this
side here, so it's easier. Basically, what we want to do is we want to do
quite like this. Inspector the item
database editor, but a couple of things are
going to be different. First, we need to
open our extension. I'm going to go to
our extension folder and oper our extensions. Here inside our extensions, we need to create a new public static method
for copying item. Let's just do that and this
will return type of item. And let's call this copy item, and we will need to insert
the item and call this item. Then we are going to
create a new item. Let's just call this new item. We are going to build this
using the constructor here. Okay. But since the constructor only have three parameters,
we need to modify this. Let's go back to our
item class here. Now we need to
also extend to add the sprite parameters and also the boon parameters to complete the
constructor parameters. Now we can just assign this
to each of the fields here. Sprite be assigned
to the item sprite. And this allow multiple
to be assigned to allow multiple parameter
that is passed from this argument here,
and save this. Now if we go back
to our inventory, inventory editor,
our extensions, we can bill this using
the item currently, all of the object is serialized, so we cannot access it. But we can create a public or
to it. Let's just do that. And basically for
creating public getter, we can just create a new public
field and the data type, which is integer
for the item ID, and I'm going to
create a new item ID, and this will have a
capital in front of it. So to differentiate that this is a property and
this is a variable. Inside this, I'm only
want to give a getter, so I've created a curly
bracket and get keyword and then here we're going to return the item ID
with lower case. We want to return the
item ID value here. And the next one
should be a string, and this would be the
item name with capital. And then we also want to
only get and then return the item name item name
variable value, and so forth. So for the description, we want to do that also. Return the item description. Then for this sprite, we
want to create a sprite. Then we want to also call this item sprite with capital I, and then return the items
with the lower case, which is the variable here. Then for the bullion,
we want to also create a new get a public er, and we want to return this
allow multiple variable. Now once we create this, if we go back to
our extension here, we can access the item ID, but we cannot modify this. We can only get the value, we cannot set the value here. This would be safe from any unwed modification from a script for the second
parameter, it should be the name. Third one should be
the description, and the fourth one
should be the sprite Okay. And the last one will
be the multiple billion. And once we create
this new item, we want to return this item. So let's just return new item. Okay. We want to create a public fight for adding
item to our inventory. Okay. Just like the one in
our item database here, but we want to add this
item to our inventor here. Type inventory list here, and we want to do
the add method and pass the item as a
member and save this. Now let's create
our editor here. First thing, first, we need to grab our inventory
serialized field here. Let's just type
serialze property, and this will be the
underscored inventory. And we want to find this
inventory here by accessing its serialized object and then find property and this should be search for the
inventory list, and we can just
copy the name here. And we need to also create the serialized for
our item database. Okay. And let's just search
for the item database object. Now in our inspector, we want to render
this property field for our item database first let's just do that
editor UI layout, property field, and we can just render this as item database. Now we need to create some loop to draw our inventory
member here. We can look it's size, and then we need to
draw every item entry. I think we are going to
copy this method here. Let's just copy the method. But we are going
to change a lot of things and we will still use the serious
property item as an argument. But instead of using
property field, we want to use only label field. Most of the item names
value will be read only. We cannot change
this value here. Let's change this
to label field. Let's grab its string value. Since we know that item
name is a type of string, and we can create item name. Plus this value here, just like the one we
have this label field, we want to have a delete button, but we are going to
change this later. Let's just set up the other properties
that we need to render. Let's grab this string value. And I think we can add
a label like this. Then we can make the UI lay
out to have a certain hide. Let's try for 70 pixel, so we can read the
description better. Now we want to draw sprite but we don't want to
make it editable. We are going just to delete
this all of this here. We are going to create a label that are going
to show a texture. We need to create a variable. Let's just call
this sprite fewer. And we are going to
use the asset preview. We're going to use the asset
review here and we can get asset preview and this
will ask for an object, so we can grab the item, find property relative
and grab the item sprite, and then we can get the
object reference value. This will return the type object that we have here,
which is a sprite. Once we create this
variable here, we can create a I layout, label, and we can put this
sprite viewer as our label. So label can show a text or
it can show also a texture. This asset preview
get as a preview. We'll render this
sprite as a texture and it will be saved into
this texture variable here, and we can render it using
the layout label here. And we don't want to show
the allow multiple here, but we want to
render the amount, we want to make sure
that this is editable, so we can just use the editor Go and this should
be the property field. And we can get or we can find relative property,
which is the amount. I think, let's
check this amount. We want to get this. Let's just close this here and save this. We need to delete this here. And we want to delete
only this inventory here. Something like this, we can delete this second line
here and save this. Now, once we have
this draw item entry, we can just use that method and pass our
inventory by accessing the array element at index of I Now we are going to create this debugging function for adding items
for the inspector. But before you do that,
we need to expose a couple of field
that we have on our item database and also from our items from our inventory. In this case, we need to access
this item database here. I'm going to create a
public getter for it. This will be a public and
type will be item database, and for the name, I'm
going to call this also item database with capital. And we want to get
and we want to return the item database value here. Okay. And we need this because we want
to access the member, especially for this we want
to access this item names. Via this public ter, and we want to get
this list string form. We don't want to
get an object form or anything because if we use
the object reference value, like this one here, if you see our sprite here, this will return an object and we need to return
this as a list. That's why I expose this and we will also need to
expose item names here. We need to create a public
der and I've created this. As you can see here, I create a public and type list of
strings with the same name, of course, but with a capital I, and then we want to only
return these item names. I'm going to just lay out this. It doesn't take too much space, so let's just type this, return the items names here. Now, once we have this, we
have access to our item names, we can read this from
our inventory editor. One thing I forget, I forget to create a custom editor tag here, attribute, and this
would be the type of our inventory class. We need to create a
couple local variable inside our editor and
this will be the item ID. I'm going to also create
the inventory field here and this will
be our source we can read this right away
from our so our cilize object on our enable, we want to grab this inventory
class from our target. Okay. Now once we have this
access to our source here, we can check if our item
database is initialized or not. So we can check this
using the public error, and if it's not null, then we want to draw
the inspector below. We are going to put
this four loops inside of our F statement here. And we want to create a
drop drop down field, and basically, we want to
create an editor GY layout. We want to create the
pop up for the pop up, it asks for an integer, so we want to pass this integer. For our pop up. Then if we
check the other overload here, it asked for a string array. This where we are going
to use the item names. Then we can access
the item names together since we make
this item names public inside our item database
here. As you can see here. Now we have this. But since
this is a list of string, we are going to
convert this two array using the two method, and basically, we need to set this item ID to whatever
value that we select. We need to put this item ID in front of us and then
equal sign this. Whenever we change this pop up, it will assign the new
item ID to our item ID. Now, another thing we need to do is we need to
create a button. Okay. And let's call this item. And let's create a new item. This time, we want to use the extensions copy item
that we have created. But to copy this item, we need to grab a
specific item from our item let's just create
a new public method. Okay. And this will return item, and let's call this get item, and we can just pass
the ID here as peter. Basically, what we want
to do is we want to look our items here, items do count, and we want to check the
items ID if the items I items item ID properties equal to the ID that
we are searching for, this ID here, then we
want to return that item. Return the items I here. Else, we will return null. If the get item doesn't found the item that
we're searching for, it will return type of null and we can check if the
item is or not. Okay. Now we have
this method here. We can go to our
inventory editor, and then we can use
the extensions class, we created and copy item, and then we can get
the item by accessing our source item database here, and we can run the
get item method here, we can pass our item ID. Basically, the item ID will be the item from our pop
up that we selected, and it will save the item ID
and then we can get this. Once we create this new item, we want to add this to our item. Let's just access our source, and we have this add item function and we can
pass the new item to it. Okay. Let's save this
before we try this. Let's run the apply
modified properties from our ers object. Since we are drawing the
most of the inspector. A couple of inspector, we are
using property field here, so we need to make
sure that we apply modified properties and on top, we need to add the
update method. Let's save this. Next thing we need to do is let's go back
to unity here and try this. Okay. Okay. When we change, the constructor here, there is a couple of things that
we need to also change, for example, in this
item database editor. Before we only have
three parameter inside this line here,
and if I save this, I've already changed this
before, but if you save this, it will throw an
error because it as for five argument
instead of three arguments. In this case, I'm
going to pass null for this pride and falls for the default allow
multiple options for our item database editor. Once we add this, it
gives no more error. Go back to unity. Now, if we
highlight the item database. I'm going to check
this first DD bug because we have a lot of item names here
as you can see here, and we need to make
sure we only have one. These item names and items should have the
same member value. Let's try this if we add another item and
then we delete this. As you can see, maybe those other seven entries and the entries are the leftover
from our previous testing. But now if we add new item
and then remove this, it will also remove
our item names here. Now, if we go back to
our inventory here, we have this pop up.
This is the pop up. For example, if I create another item and let's
call this key here and just add
description some key for opening certain locks. Or type A K and a big size chicken meat for
the chicken description. And for the key, let's
just add a check mark. Now, if we go to our
inventory system here, we will let's just refresh this. Let's just browse the
item database again. Okay. There is a small issue
with our item database here. Whenever we add a new object
here for the key here, if we go to debug mode, as you can see here,
it adds an item names, but it doesn't save our key because this item names
gets created when we add a new item and then after we add we are setting the name
for the item name here. Whenever we change the GI, we need to also update
this item names here. It's quite easy to fix this. We can just go to our items
database editor here. And here below our
four loop here, we can create a statement, and in the editor, we have the GI class
and we can check if change return
if any controls, change the value of input Data. It checks for any changes
in the inspector, and if there are any changes, then we want to
recalculate the ID. We run this recalculate
ID because basically this recalculate ID refreshing the ID and also refreshing the items name here,
as you can see. Once we recalculate
this ID here, if we go back to unity and
go to our debug mode here, You see, we have
this empty element. But if we go back to
normal and let's say I delete this and I
retype this to key, and if we go to the debug mode, you can see that we
have the key saved. Once we have this
modified this fix, we can go to our inventory here, and if we browse,
we have two items. We can add chicken and
we can add the key item. And this pop up and this button for adding items
is going to be used for the bugging or if we want
to create a couple of items that should be already inside our inventory from
the start of the game, then we need to add this
and the other items will get added some item will be removed if our player
gives some item later. We will create an
action for that. This is for the
inventory inspector. Now we can just delete
this and for the key, we are drawing the amount
here. Let's check this. Oh, there is one
thing we need to fix because we
have disabled this allow multiple here
as you can see, but in our inventory, the options for amount is shown. We need to make sure
that doesn't happen. Let's go back to official Studio here in our inventory editor. We can check its value here. If the item find
property relative, and get the allow multiple. Ban here and gets its ban
value b value like this. If it's true, then
we want to draw this amount property field here. Otherwise, if it's fall, then we don't want to draw this. Let's go back to our unity and now it won't
render the amount. But for example, if I
change the chicken to a low multiple and then go
back to inventory here, Let's remove this
and draw this again. As you can see here,
we have this amount. If I add the key here, then we have this object
without the amount field here. This amount will get rendered
depends on the options, if this is enabled or disabled
inside our item database. Basically, we are going to
define the item database here, the name, the
description, the sprites, and the possibility to
have multiple or not, and in the inventory, we can change how many amounts
that we have currently. There is a small bug in
the inventory editor here, when we try to delete the entry, it will try to delete the
entry based on its item ID. Since the order of the item in our inventory
lease won't be the same of item ID because
the item ID will be the order of the item
in the item database. In order to fix this,
we are going to modify the draw item entry in
the inventory editor and create a new
argument or parameter, and let's just type the
type, which is integer, and we can just call this ID. Then whenever we want
to delete that item, instead of finding the item ID, we need to pass this ID here, when we look through the inventory list of items
and row the item entry, we need to pass in the I
value here, the iterator. Let's just type I and save this and this should
fix the issue.
15. 14 Basic Data Manager: Okay. So now we are going
to create a manager, and for this manager, it will do it will do mostly holding our player inventory
that scriptable object. And this manager will be
a Singleton so it can be accessed from anywhere or
from any other script. And this manager will also
handles and safe load, and we will going
to do that later. Now here, I've opened our scene here and let's
create a new folder. And let's call this
folder manager. Okay. And inside
this manager folder, let's create a new
C sharp script, and let's call
this data manager. And we are going to open
this manager script. Now we have this
de manager open. First, we need to declare a static reference
to this class here. Let's create a new
public properties, public static and for the type, it should be our Datager itself. Let's call this instance
with capital and open the brackets to define
our getter and tear. We can get public and we
can privately set this. In order for this
singleton to work, we need to create
a awake method. It gets initialized
before any other script or any other start method in
other script gets executed. We want to check first
if instance is equal null or if no other object already used this
instance keyword, then we want to grab that. We can set instance
to this class here, this object and we want to
don't unload this game object. Okay. But else or if the incident is not
in this condition here, then we want you to destroy
this one, destroy game on. This is how we
create a single ton and this will make sure
that our object here, this data manager or the
game object that holds this class here will stay
throughout the scenes. If we put data manager
in the scene one and also another one in the
s the one in the s will get destroyed automatically
if the data manager from SN one already exists and persists when we transition
to another scene. Save the script,
I'm going to delete our start and update method. And We are going to expand this da manager
further in the later videos, but right now we need to
create a series field, and this would be our inventory. Let's just type the
class here inventory, which is our script
object fields here and we're going to put our inventory in
this field here, and let's just call
this inventory. But we want to create
a public getter. Let's just create
a new one here, public type inventory
as the class here. Let's call this inventory with capital since it's a property. And we want to get
and we want to return our inventory
field here, this one. Ascrip can access
this inventory, but can only get its value. It cannot change its value here. It cannot set its value. Let's save this and let's go to unity and let's
set up our data manager. Let's create a new
empty game object. Let's call this anti game
object data manager. Let's rename this. I'm going to reset its
value, it sets 20000. It doesn't have to be on
the center of the world, but I like it this way. Let's save this and then drag
our deaminator script here. If we drag our deaminator
as you can see here, we have our inventory
slot and if we go to our data folder, we can drag our inventory
to the slot here. Okay. So now we have access to this inventory
from any other script. And in the next video, we are going to create
the item action, so we can remove or we can
add item during runtime. So for example, another NPC as for an item or another
NPC gives an item. We can use that item action to add or remove some item
from our inventory.
16. 15 Item Action: Now we are going to
create the item action, and we should create this class inside
our actions folder. And this item actions
will functioning as a class that can give or
receive items on run time. Let's just create
a new Cb script, and let's call
this item actions. We are going to create the base structure of
this class first. Basically, this class should be derived from the actions class. And since the action class implements act abstract method, we need to also implement this. Let's just implement
with is Studio, you can automatically
implement this. I'm going to just to cut
this and put it here. We won't be needing
the start function too so we can safely delete that I'm going to remove this throw new system not implemented and We need to create a couple of fields here
and most of them should be sterilized field
since we only want to change the value
from our inspector. This would be our item database and call this item database. We want to also
create a new bulon Maybe we can call this item and this will decide whether we are giving or
receiving the item. Another one would
be our actions, depending on true or false. So let's just
create new actions, and this would be the Yes, actions, and no action. Oh, sorry, I think that
should be an array, so we can run a multiple
action, last one, we need to create a private item and this will be
our current item. Whatever item we pick will be
safe in this current item, and we are going to
use this item data to compare whether the user are going to give this item or not. Basically inside, we
are going to check if g give item is true. Then give the item
else receive the item. This is the basic condition
and inside giving, we need to check if
we own the item, and also if the item are multiple or if the item has the allow multiple
option or not. And if it does have the
multiple option or not, check how many items needed. And we can create here L
give and remove the items. Here we can add give
and substract item. This is the basic structure of our condition and we are
going to extend this later. But first, we need to create the editor for this
item action script. So we are going to create
that on the next video.
17. 16 Item Action Inspector: Now, let's create our
item action editor. Go to our editor folder
under our scripts, and then let's create
a new HR script, and let's call this
item action editor. Let's open this.
Once we open this, I'm going to search
for our item actions, and I'm going to put it besides
the item action editor. It's easy for us to switch
between this class here. For the item action editor, we want to make sure
that it's using the unity editor namespace, and we want to derive
this from our editor. We want to also make
sure that we are giving an attribute
custom editor and set the inspected item, which is the item actions here. Okay. And we can just remove the start and update
method. Okay. And we need to create erz
property for our field here. Let's just save this
and then create a new item actions class first. This is for the source object that of course we are
going to need this. We need to create a
couple of seriz property. Basically, this would
be the item database. I'm going to as usual,
for the seriz property. I'm adding the underscore prefix and this will be
the item database. And this would be the gift item, and this would be the actions and the underscore, no actions. Language to add this
at a S at the end. We know that this is an array. It's for plural. Now we can create for enable and also an overwrite for
our inspector GY. Let's just delete
this based on in y. Now we need to find for each of this property and also
initialize the source here. For the source, as usual, we can just type the keyword
and then equal target, but we need to cast this
target into the item action. Just type the class name here. Then for the
serialized property, we can just assign one by one by assigning the
serialized object, lower case, and then
using the fine property, and we can just search for
the item database field. And we can just copy the
name if you are not sure and then paste it inside
the string argument. And for the next should
be the gift item and just find the give item field. And for the actions, we are going to find the actions and the same
goes with our no action. Now we have created all of
this serialized property. We can start draw this
serialized property. First, we need to
make sure that we update if there are any changes, and then we need to apply any modified
properties at the end, and we will create the code
between these two lines here. The first one, we want to
render this item database. Let's just type an
editor I layout, and this will be the
property field here, and we can just pass
the item database, and then we can add a new
I content with the string, and let's call this
item database. And then end it with semicolon. The next one would
be our billion. I'm going to just to copy this, paste this below here, and pass, past the as give item variable, this would be we would
label this give item. And for the actions, for now, we want just to draw
the property field, but later, we are going to
create a custom drawer. Pass the serialized
property and also the serialized, the actions. And this will be D. Yes. And this will be the
no action. Okay. Save this. Now if we go
back to our two unit here, I'm going to create a
new empty game object debug our item ed here. Let's just create a
new empty game object. Let's just call this
item action debug. Let's add our item actions class to this game object here. There you go, we have
this custom inspector, but it looks like
just the default because we are not
using the UI layout, begin horizontal or
begin vertical or creating a box
between entries here. It's okay. We have this. For the yes and no actions, I think we need to
pass we have this over we need to pass a true value to
include the children. It will draw the
array window here. If we go back to unity, we will see that now
it draws the array. But we will create
a custom editor for this action. Don't worry. For now, just for testing out and we want to be able
to pick a database. Once we pick a database, we want to show a pop up or in field just
like the one we have in the inventory to pick the
selected item that we want to set as the item to give or to receive. Now let's continue. For the item action here, we want to copy
whatever item database that we pick to
the current item. Now we are going to create a new integer and this for
keep track of our item ID, and it will be type of integer. Let's just call this item ID. Now let's go to our
item actions editor. Here, we want to create
some sort of a pop up. Between this give
item and the actions, we want to draw the item entry. In order to draw
this item entry, we can safely copy the method
from our inventory editor, draw item entry,
select all of this. Then copy this and we can paste this to our item action editor. Here, let's just
paste this code here. And we don't want
this delete button. So we can just remove this
eight method because we can we don't want to delete any item from
our item access. We only want to
change whenever we change the item pick
using the pop up. Here we can just
throw the pop up or for selecting items. Now let's do that. Since we
already create this item ID, we can just access this
using the source and then access this item ID, then set the equal and
we are going to create a pop up and for this pop up, we are going to pass
this source item ID. Value, and we want to
display an array of string. For the second overload here, we want to show the display options using a array of string, and this will be the item
names from our item database. Let's check that. Go to
our item database here. We have this item names and
we can get its item names. So in our item actions, we can create some capsulation by creating a new properties, and this will be the
time of item database. Let's call this item
database with capital I. We want to make sure
that we can only get its values we want to return the item database field that we created on top here, but we only want to get this. Doesn't want to set this now we can just grab
from our source and then grab the item database
and get its items name. This is a list so we
need to convert this to array like this. Save this. Now if we go to unit here, you'll see that we have this
pop up below our item abase. We can access between
chicken and key. So far, good. Now let's
draw the item entry. Now we want to make sure that
we want to draw this GI or the custom editor
whenever we already pick a item database. We want to make sure that if the item database is not new, then we want to draw all of
the code here below here. Let's just cut this
and paste this here. Now we can draw the item using the draw item entry
and we can pass the item that we have
from our item database. Let's just grab this as item database and get
the array element at index of our source
item ID like this. Now we want to Whenever we
change this value here, we want to fill this
private current item. Let's just create a public
method to change item, public void, change item, and we want to pass an item. Whenever we change this item, we want to set the
current item to our pass item or we
want to copy this. We want to use this
extension copy item and pass the item here, save this. And we want to make sure
that we can only do this if our item database is not T, not. We want to also check if our current item ID
is equal to the item, the item ID that this
item we pass here have. If it's equal, then we are basically
selecting the same item, so we don't need to
change the items. So just execute return, so it will skip the code below. Once we have this
public method here, we can save this and we can
make sure if the I changed, then we want to run the
source change item, and we want to pass the item that currently we are changing. Basically, we can grab
this item database and get the array element index, and we can I think it's
object reference value, this one, but I think we can just grab this property here. And then we can
pass the item using the get item and pass
the source item ID. Let's check our get item method here inside our item database. Get item, it will return our
items using the item ID, we can just use that, save this. Since the source item ID, it doesn't using
the property field or it doesn't using
the serialized object. I want to mark any changes here. We can just use the
editor utility, and we can run the set
dirty and we can just pass the source object like
this and save this. Now let's test this out, see if it's working or not, go back to unity, and I'm
going to save the scene first. Now we have this. But there is an error,
as you can see, there is a unexpected
layout group error. Let's just check this out. There is a mistake on my end and upon drawing the item entry. We need to draw not the item
from our item database, but the copy it item
from our current item. Since the current
item is private, then we don't have
access to this. I'm going to create a
public inspector instead, I'm going to change this
basically to a public item, but this will be with capital since this will be a properties. I'm going to get allow
script to get this value, but I want to set a private set. Now we have this and
I'm going to compete this and change this keyword here using the capital C
one that we modified here. We want to set this
via this method, but we want to able
to get the value. Another thing we are going
to create is we want to create a real field amount. And this amount, it will be how many amount that we are going to receive
or we are going to give. We can set this on
the inspector later. We aren't going to
use this amount here because this integer
amount on our item, this would serve a purpose to keep tracking amount items
that we have in our inventory. And this is the item
action, so it's different. This is the amount
or the amount or the receiving amount when we finish running
this item actions. So now we can just access our source object and
get the current item. But here we have
an error because the draw item entry is asking
for serialized property. We can just change this to item class of item, and
this will work. Now we need to modify
this. It's okay. We can just grab the ID, and we have the properties
for each of this here. Let's just delete this
and then grab the name. For the description, I'm going to delete all of this value here and grab our
item description. For the sprite viewer, we are going to grab the
item sprite right away. So we can get these items right. For the allow multiple, we can just get the allow
multiple properties. And on this is true, then we want to draw
our the integer on our serialized field here. Now we need to get
a reference to it. I create a entry first on
our serials property here, and then we can just search for it by accessing the
serialized object, find property, and then type the variable name,
which is amount. Now we can just draw
that serials property. And save this. Another thing we need to do is we need to mark the scene also
dirty upon changes. Let's just do that to
mark the scene dirty, we need to using the Unity editor scene
management name space. Then we can I think
it's the scene manager, Mark dirty to the scene here. We can grab the source. What's wrong with
my keyboard here. Now, source the game object, we can grab the scene
of the scene where this game object is a part of the game object recite
we want to mark that. Whenever we change the value, it will add asserc
sign on our scene name so we can save it
and those changes will get safe in our scene. Let's go back to unity here and let the script compile here. Now if I select the
item action debug here, and let's select
the chicken object. Now, it works, as
you can see here. If I select chicken, then it asks for an amount and it shows the object that we
want to interact, whether we are going to
give or receive this item. If it changed the
key, it change also the value since the key is
not allowed multiple items, then we don't have
this amount option. This is basically for
our item actions. Later, we are going to create a custom drawer for our actions, so we can create a better
representation for our actions and easier modification for adding and removing the
member of these actions here. Now, let's create this custom
inspector for our actions. In order to do that, we need to create extensions
for our editor. I'm going to go to our
extensions folder and I'm going to create an editor folder
inside this extension. Now inside this editor, let's
create a new CHF script, and let's call this
editor extensions. Let's open the script. We are going to remove the
mono behavior and we want to use the Unity
editor namespace, and we want to make sure
that this classes static, so it can be accessible from
anywhere from any script. Now, let's create a
new public static void and this will be our
draw actions array. We want to pass the
serialized property for the array. Save this. Now we want to create a GI
layout and this will be the begin vertical
and we want to mark this argument with box. It creates a box, and we want to end this
with the vertical. We want to also create a label. But we want to create a custom level that we can
pass through this function. Let's just create
another argument, which is a string and
just call this label. Then we can pass
this label parameter inside our label field here. First, we want to check if
our array size is equal zero, then we want to create a button. Let's just create a button here, and label this
button at actions. Whenever we add actions, we want to insert an element to the array
using this function, insert array element at index, and if it's zero, then we should insert at zero. Here, we want to look
through our array size. We can just type the
array size here. Inside this four loop, we want
to create a new G layout, and this would be a horizontal, begin horizontal, and
we want to close this with a horizontal. Like this. Now we are going to draw
the properties field. Let's just use the layout
and property field. We want to draw this array using the array
element at index of, and we want to set
the content to none, so it doesn't draw anything
as a label or stuff. And we want to add
a delete button. Just use the GI lay
out class and button, and then for the
deleting button, we can just simply
use the x character, and if it's pressed, then we want to
delete the array. I think we have this delete
array element at index of and we want to make sure that if our i is equal to the array array
size minus subtracted one. This means that if we are at the end of the array or
less member of the array, then we want to
add a plus button. Let's just copy this code here, paste this here and set this
two plus or positive sign. Then we want to insert
array element at x of i. Index of I but our array size. Since we are at
size negative one, then we want to get
the array size. It will insert a new
member after our index, after our last index, so
it will create new entry. For each of this button, we want to make
sure that it only has a width, a small width. I've tested it before and 20 pixel seems to
works great with it. I'm going to copy
this and then add a parameter also to the positive
sign and then save this. Now we have this editor
extension script. We can go to our item
actions editor here, we can draw the actions. Instead of using this, we can just type our editor extensions and
then draw the actions array, and we can pass this
serialized array, which is the S actions. Then for the label, we
can just grab this. Okay and then close it
with copy this line here. I'm going to paste
this here. This will be for the no action, and rename this no actions. For this old code here, just comment this
moment and later. If we want to, we can
just delete this. We have saved this
and let's go back to unity. It's still compiling. After it's compile, let's go to our item action debug
and now we have this. Yes, actions and no actions. If we delete this,
it looks like this. If we don't have any member
in this actions array, we need to add actions first. Once we have actions, it
will create a new actions, and it will have
this plus pattern to add another one. We
can just plus this. The last one, the last
the last member actions will have this plus
icon to add more icons. To delete this, we
can just safely delete this by
pressing the pattern. This will certainly makes
setting up our scenes, our actions quite easily. I did the draw actions array on editor extensions on purpose. Later, we can use
this method to draw a custom actions array field
on another class easily.
18. 17 Item Action Continued: Okay. Now we are going to
continue our item actions, and if we open our
item actions script. As you can see here, we
haven't really defined the act function over here.
Now we need to create this. In order for this
method to work, we need to create a couple
method inside our inventory, and we are going to do
that in this video. Now, let's just start
creating code inside here. First, we are going to check
if the gift item is true, then we are going to give
item from our inventory. Let's just copy
this comment here. Cut this and put it inside
and we want to make sure that the inventation is right
and else is for receiving. In this blocks of code here, we are going to
receive the item. First, we need to check if we own the item and in
order to do this, we need to create
a custom method. Let's go to our inventory
and we can create a new public and this
will return integer. We can just name
this check amount for the item to be check, we need to pass the item
that we are going to check. First, we are going
to look through our inventory and
check if the item in the current loop
have the same ID as our item that is
being checked at, then we want to
return the amount. Let's just look through
our inventory list. Since this is a list, we are
going to access its count, and we want to check if
inventory index of the item ID is equal to the item item ID that we are currently
passing here as a parameter. Then, first, we want to check if the item in inventory index of Alow multiple
bullion is true. We want to return
the amount value. But we need to expose the
amount from the item. Let's go to our item database
here. Items the item. Go to the definition and open
this and we need to create a public integer and this will be the
amount with capital A, since it's a property,
then we want to return the amount value here. Save this and we
can close the item. Now we want to return the
inventory index of amount. Else. If we do have, but this allow multiple
flag is false. Then we want to return one. Since object that doesn't
have this enabled, we can only have one
in our inventory, so we can safely return one. If we don't find the item, the item that has equal item ID, it means we doesn't
have that item. We can just safely return
zero at the end here. Basically, if it's
more than zero, we do have that item
and if it's zero, then we don't have that item. Let's go to our item actions. Now we can access our inventory from our dinger
access our single t here, instance and inventory, and we can run the method
that we just created, check amount for
the item that we want to check is
our current item. This one here, let's copy this. Once we paste this, it returns an integer. Here, we need to make sure
it's greater than zero. If it's greater than zero, it means that we have this item. If it's greater than zero, then we want to check if the item has the allow
multiple option check. Let's just check from
our current item. If it's low multiple,
then we want to check the amount that
we have current item that amount is less or equal than our item that
we have inside here. Since this will
return the integer, how many items that we have, if the current item
amount we need to give is less or equal than the one
that we have in our inventory, then we want to pass that. Pass the item L. We don't have enough or
simply don't have the item. It can be not enough
or it can be that we don't have any of those items and we need to
create another LF. This is for if the item
doesn't low multiple, then we need to make sure
that the item that we have is at least equal one. It means that we have this one. But to make things simple, I think we can just create an
integer called item owned, and we can pass this method
to this item own here. Then after that, we can
just only compare this item on and we can replace this, it's much easier, and of course, it's faster because we already
the result of this method already being cash into this variable, it
should be faster. And pace this. So to reiterate the conditions. First, if the current
item inspected has its allow multiple
bull to true and currently we have more
than needed or asked, then we want to give the item. And if the current item allow
multiple bull is false, and currently we have one, then we want to remove the
item from the inventory. And then invoke actions. And for this, so we
want to do that. Invoke actions. Now we need to modify
our inventory. We'll need to add more methods
inside this class here. Let's just create a new and this will be a
type of void and remove item and the first
type will be the item, and the second one would be
the amount integer amount, we will set default to one, it will default amount of one. Now inside our remove item, we can just our inventory. Look our inventory count. Then we can check if we do have the item
with the same ID. As our item ID here that
we pass as a parameter, and we want to remove that. Basically, what we
want to do is if inventory has the allow
multiple flag enabled, then we want to
remove the amount. Let's just grab the
item from inventory. But in order to remove this, we need to create a method
to subtract the amount, let's go back to our item. Let's just open this again. And we can just create
a new public void, reduce amount with
the value here that we have here or we can just name
this modified amount, which sounds better we
can add or we can reduce. Basically, what we want
to do is we want to set the amount to be added
with our value here. Okay, let's go back to
our inventory here. Now, if it's allowed, then we want to modify the amount by negative
amount that we use here. And if we don't allow multiple, then we want to just remove
the item from our inventory. Remove at and using
the index of. Just remove the item
from our inventory list. Here, we want to check if inventory amount less
or equal and zero, then we want to
also remove this. Let's just remove
from our inventory. Here. Let's save this and let's go back to
our item actions. Here we want to run the
remove item method. Let's just access atature
instance, inventory. Then we just remove item and we can pass the current item. For this case, actually, I create a mistake here, as you can see here. The one we want to check is
not the current item amount, but this amount we are
setting up in our SLS field. Let's just change that. Amount. Yeah, this should do and we can just pass
the amount here. It will remove our item, and if we can copy this here, and then we want to remove also. But this time we don't need to pass the amount value here, just pass the current item. Since we are successfully
giving our item, we want to execute the actions. We are going to create a
method in order to execute all of the method inside our
actions or actions array. If we don't have the item, then we want to execute
the no actions. We can just remove
these nodes here. Since we are going to use
a lot of actions array. I think it's best if we
put this method inside our extension class here.
Let's go back to UT. I'm going to open
our extension folder and open the extensions class. Here, just create a new public static void and run actions and pass the actions
array as the actions, and then open this and then look through
our actions length. And then we can just access the actions index of i and
then run the method like this. Once we have created this, we can use this inside
our item actions here, for example, we can just
access our extensions class, and then we can run actions, and then we can pass
the actions like this. Same goes with our
second condition here, and for the last one, we
want to run the no action. Save our script, and this
is basically done for the give item and for
the other condition, we should receive it. Basically for the receive
item, there can only be two. First, we need to check if
current item low multiple. Then if it's low multiple, we want to modify. Do we have the modified amount or we can just change
this to modify item amount and think we don't
need a default value here. Like this. I think we can just go back to
our item actions, and this will throw an error because we modify the method, change this to
modify item amount, and this is also to modify item. But this will throw
an error because we need the item amount. We can just remove
using the item on integer because if
we don't low multiple, then the item on would be one, so we can just pass
negative item amount. Now we can use this method
to also add our item. Let's just Okay. Now, let's just add a
return statement here. Whenever we found
the item that has a matching item ID that we do whatever we need to do
here and we return it. And here below, we can
just add another code, which we're going
to add the item. We need to copy the item
first. Let's just copy it. And item class item new item, and then using the extensions, we want to copy item and the item that we want to copy is basically the item
that we pass here, and then we want to
add this inventory add item or we can just
run this add item. It's better this way.
We add this item here, not the item, but the new item. Then we also want
to add the amount, how many amounts that
we are going to add. Maybe before adding
this new item, we can modify the new item. Modify amount and then we set this value to the amount that
we passed as a parameter, and then once we set
up the new item here, then we add this new
item to our inventory. Yes. Let's go back
to our item actions, and now if it's a low multiple, then we can just pass
and pass the amount. Let me want to add and If we don't low multiple, there will be two
conditions here if we already own the item. So let's just using the
check amount again. If it's equal one, it means that we
already have this, then we want to already have and we can
execute the actions, and we want to add this, we want to add the item. Let's just use the modify mon, and we want to add just one. Then if I succeeded, then we want to run the actions. For the one on top, we also want to run the succeeded actions. Now we have to sort out. Let's test this out, see
if it's working or not. Let's go back to unity. Let's test this out and
for the item action debug, or we can just disable this
thing here, and for the NPC. Let's just create a new PC
by duplicating this one. And on our scene editor, disabled Judy Togo
here, go to our object. Let's move this to
somewhere here. Now we can just remove
the message action. All of the message
action here, remove. Now let's add the item action. And get the database. There is an error here. I found the issue and it's
quite easy to fix this. Instead of checking if
our property field here, the item database or the
seized property is null, we can just check
the item database, the properties that we
expose in our source class. If it's not null,
then we want to do all of the remaining actions, and we are going
to copy this here and when we change
anything inside our GI, this will get executed. If the item database
is still null, then this will throw
an error because we are trying to access
the item database. Let's just create a flag here. To make sure that if the
nata base is not null, then we want to change the item. Otherwise, we want to only mark the editor utility and
the C manager to dirty. Save this and let's
go back to Unity. Now if I press our NPC here, we can browse our eatase. Click and now we have this. Perhaps we ask for a chicken. And press give item and then
create two message items. This is the first one and
the other one is the second one and increase the
message size to one, so we can create two
different messages and say thanks and say, you don't have enough. For the yes and no, we can just pass
this first message, four tanks and the second one
for, you don't have enough. We can test against key here for chicken test
against chicken. Let's say it as for two
items for two chickens, then we can try to open
our inventory here. Here, we only have zero
chicken. Let's is two one. Let's try test this out. Let's go to NPC, the
second one here. There is an error. There is a slight error on my end
and I apologize for that. But here, as you can see here, if we run the play button, it will throw an error with our item actions. I
found the issues. Basically the issues are
if I go to the b here, for example, We have this current item with the idea of chicken
and stuff description. And if we play this, it will reset two
and empty item. That is an error here. And since we are on the bug,
this doesn't shrow an error. But if we go back to our normal, this should throw an error,
as you can see here. So We can easily fix this. Let's go to our item actions. Now, basically, we are using a property and property
is not serialized, and that's why this
was causing an issue. We can simply fix this by
creating a public field, but I want to recommend that because if we create
a public field, we might accidentally change those value from other script. So I'm going just to create
a new serialized field. And this will be the item
and with a smaller C item. This is a new field,
but we are not going to draw a field for this
serious field here, but we want to only
for holding data, and this is basically
what we want to do. We want to set the gap to return this current item
that we just created. Now we have this, this will draw an error. Because we cannot assign value
to the properties again, we're going to copy this
current item field, the serious field
and paste here. Whenever we change
this, we want to change this seriz field. The rest should be the same. Hi. This is rooming
from the future. I got a bug reported
by Dario Munez. Thank you so much for
spotting this issue. Basically, we need to create
a slight modification on the inventory script and also
on the item action script. First, on the inventory script, the changes that we
need to create is here. Under the modified item amount. We need to add a
bullion parameter to decide whether we want to give or we want to
receive the item. So here I'm going to
declare a bullion here, and let's just call this gift, and I'm going to assign
a default value. So we don't need to change
all of the other object that don't need this
bullion value to be true. So it will set default to false. But if we need to give the item, then we can set
the bulon to true whenever we are
using this method. So here inside the
modified amount line here, we can check if the gift is true and I'm going to
use a ternary operator, and if this value is true, then we want to subtract
based on the amount. But if it's false, then we want to add based
on the amount value. Okay. So this is
basically we'll tell the code here if
this value is true, then substract by
the amount value. But if this falls,
then we want to add by the positive
amount value. And here, whenever
we remove the item, we want to make sure that if the amount is less than zero, and if we are giving the item. So we need to check if
the gift bullion is true. Otherwise, we don't
need to check this because we are going
to receive the item, so we will never run out of items if we're
receiving the items. And the next one would be
on the item actions script. So if we scroll down here. Okay. Under the give
this part here, we need to modify the code here. Whenever we are using the modify item method that
we have just modified before. We need to pass the true value. So the method we
know that we are releasing the item or we
are removing the item. And here, I'm going to also add the boolean value true here. Here, I forgot to remove
the negative sign, but please remove
it on your code. Otherwise, it will
reverse the result from modify amount
method on the inventory. And for the rest
of the code here, we don't need to change
anything because this will default the
gift value to false. Okay. So yeah, that is the
fix that we need to do. And thank you again, Dario for spotting the issue. Okay. Let's go back to it. Now, let's script compile. Once it compile,
I'm going to change key and chicken, basically, this PC will ask for
che chicken and if we have those items,
it will say tanks, but if we don't have this much amount of the chicken or if we don't have the item, it will say, you
don't have enough. Let's go to the inventory. We have only one chicken. Let's run this and
let's click on the NPC, and it will tell us that
you don't have enough. If we change this
value to three, for example, and then let's
go to that NPC again. I will say tanks and our amount will be
subtracted two one. It will be subtracted
by two because this PC as for two items,
as you can see here. This is basically works. Now if we try to as for key and since key doesn't have the
amount allow multiple added. Let's just check
this and play this. Now if we go to this NPC, it should delete this entry. As you can see, thanks, it gives our key and we don't
have that key anymore. Now let's test if we have a
multiple item, for example, chicken again and it
as for che chicken, and then we set the amount to. Let's play this and see if our chicken gets removed from our inventory.
Yeah, there you go. So we have test all of the conditions and
basically, it works, and now we have a
working item actions, and we are going
to need to create a couple more different
actions after this video. Okay.
19. 18 Message Actions Inspector: Okay. So we already created the custom inspector
for our item actions. And now we want to create
a custom inspector for our message actions also, so it's much easier to manage
like this actions here. We have this custom drawer. Let's just go to our editor
folder and let's create the new Ccript let's call
this message action editor. And let's open up. Once we open this, as usual, when we are going to
create a editor script, we need to use the Unity
editor namespace and we should make sure the class
derived from the editor class. We want to also add a tag
custom editor tag and then set the type using the type of keyword and then the base
class that we want to modify, which is our message action. Let's save this and just
delete this two method. Now if we reopen
our message action, we can just go to definition
and then keep it open here. We can see that we have a
list of messages string. A bullion enable dialogue, string for yes and no text, and then list actions for
our yes and no actions. If we are using the
enable dialogue option. So let's just open our
message actions editor, and then we are going to create a serialized property for
most of those fields here. Let's just check. Okay. The first one should
be our messages. The second one will be
our enable dialogue. Third one would be the tax, and then the no tax, and then the action. And then the actions. Now once we've created
all of this here, let's just grab each of those erized property here
inside our own enable. For the messages, we are going to grab from
our erzed object, and then we can easily find properties using the string values,
which is the messages. Oh, message without second one, it should be ale in total and this would be
the enable dialogue. And I think it is
called enable dial. I'm not sure. Just copy it. Okay. Yeah. That's correct. And the third one, we can just copy this
line here and then change its its member. So its name. This is for the text, this is for the no text. This is for the S action. Okay. Then this is for the action we don't
have no action yet, so we need to paste another line and then
copy the no actions name. Now, let's check the name. We can safely type the name. This is not test tax, and this is the no tax, and this should be
the yes actions, and this is the actions. Once we have this initialized, we can start drawing
our inspector GY. Okay. Let's comment
this out sir first. And then I'm going
to make sure that we are updating if there
are any changes, and at the end of
the unexpected GI, we are applying any
modified properties. Okay. So now, first, we need to look to
our message list. If there are none,
show a message, first we can show a message, and then look through our
message list and then show enable dialogue Tgle enabled, then show the
dialogue properties. Okay. Okay. So now for drawing
our message list, we can simply create
a new method, and let's just call this
draw messages entry, and then we can pass our
serialized property, and this should be
the message array. Oh, no, sorry, message entry. Because we are
going to look this inside our uninspected UI, and now we need to
create a button first. So let's just create a
new UI layout button. And then add message. Okay. And whenever we're
adding a message, then we want to insert a new array element
at index of its size. So we can just grab the S
message that array size. And we are going to look
through our messages size here. Let's just look through
the array size, and then we want to
draw the message entry, and we want to pass the
message member at index of I. Now we have this. We want
to draw the message entry. Since we know that message
is a list of string, then we can easily draw the field editor GI layout
and dro property field, and we can draw
the message entry. For the I layout option, I think we can create
a string label, and we can draw a new content and pass
the label value here. We can also make the width using the with the height to around let's say 50
pixels, it's quite big. It's easier to write a long
messages or long dialogue. In the inspector, and
then we need to pass the string here message, and we can add the
i plus one string. If it's zero, then it will be message one and if it's one, it will be message two, so we can see the
order of the message. Save this and think for
every message entry, we want to create a layer layer and we want to begin vertical. And we want to create a box. And then we want to close
this with the vertical, and let's save this now. Once we save this, let's go
back to our let's go back to unity and check how does it render this message
actions once it's compiled. Now as you can see here,
we have this message, good day, sir, and the
officer good day to you two. But we want to create button on the right side
on each of the message. In order to do that,
we can just pass an integer After the label, we can pass an integer ID, and after the label
here, we can pass the. Now we can create
a G layout begin horizontal because
we want to set the button side by side with
our property field here. Here we can just close
it using the horizontal. Before we closing it, we
want to draw the button. Let's just create
G layout button, and set this x character, delete, and then we can set the width the width
to around 20 pixel. So it's it's quite small. The button should
be small and then here we want to oh sorry, the layout should
be reside inside the button argument
here like this. Now we can just delete
the S from our messages, and then the delete
element index of ID here because we already
passed the index on our loop. So let's just save this. Okay. Now if we go back to unity, we can see we have
this delete button and I'm going to save
the scene first, whenever we delete
this button here, it will delete and if
we create message, it will create new message here. We can just change this. We can just undo this. Save the scene. Now we have a very
nice custom inspector for our message actions. Now we need to define
this part here, show enable the go.
Right now we can just Create a GI layout
and begin vertical, I guess box here, and then we want to close it. Becomes a group. Then
here we want to draw the property field of
our enabled dialogue. We can just this
enable dialogue. Then we want to pass a text here using
the new I content class, and the text will be
the enable dialogue. Ks and we want to we want
to get this value here. If enable dialogue
b value is true, we don't need to
put anything else because this means it's true, and if we use exclamation
means it's false, shorthand version of checking whether the bully
value is true or not, then we want to the
properties here. Let's just copy this here. And I think, make it twice and make the layout
smaller, the smaller, and for the level, this
would be the button label, and this will be
the button label, and we want to draw
the text and the text. Underscore text. And for the text
and the no text. In between, we want
to draw the action. Now, we already created
this editor extensions, how to draw an array. We can just use that. Let's use our editor extensions and then draw actions array. Then we can pass the action. Once we created this here
because this method is going to be used by a lot of classes that implement
actions array. That's why I put this
in editor extensions. And we can just pass a new
UI content for the label, and then we can just
type this actions. Close this. Oh, the content. We only need to pass
a string value, so this here because we declare
it using a string label, and it will draw the
label automatically, and then copy this paste
this below the button, no text here, and pass
the as no actions. The thing is that with
this bully and checking, we will only show these
options if the designer or we choose to enable the naval dialogue using
this property field here. Let's just save this
message actions editor and let's check how
it looks in unity, go back to our unity. Once it's compile,
it should have a here as you can see here. It has the enable
dialogue options. If we disable this, then it will not draw
the dialogue again. As you can see here, it's empty because we haven't declare the actions and the label for this one here, but this one is empty because
we already set this before. That is pretty
much how we create the message actions
editor. Okay.
20. 19 Activate Actions: Okay. Now we are going to create a
new actions and this will be the game object actions. Let's create a new
C subscript under our scripts folder and
the action subfolder. Let's call this script
activate game action. Or we can just call it
this activate actions. Much shorter, and let's
open this up and we want to make this class derived
from the actions class. We want to implement
the abstract class, and we can just put it here. Okay. Let's just
little line here. For this class, we want to
create a custom class to hold our specific data for
activate actions fields here. Let's just create a new
public class below here. Let's call this
custom game object. We need to mark this class to serializable. Let's
just do that. And we can open the class. Inside this class, we want to create two new serials field, and this first one would
be a type of game object. I think we can call
this GO game object. For the second one,
we want to create a bulon this will be
the active status. Now we've created this. We want to create
a public getter. Let's just create that. I'm going to rename this
to a smaller case GO. And I'm going to create
a new public getter for our GO game object and we want to get and we
want to return the GO value. And for the bullion, we want to also create
a bullion getter, let's call this active status, and we want to return
the ban value. Something like that.
Now let's create a new serialized field here, and this would be
a type of list, the custom game object that
we have just created below. Then we want to call this custom game objects
and initialize it. Then here for the action, we want to look through our
custom game object members. Okay. And each of
these loops here, we want to activate the GO, the game object,
the public getter, we want to set active. Based on it, active status. Let's just grab the accessor here and find the active status, equals, but the active status. Okay. Now we have
this script created. Let's create a custom
inspector for the script. Let's go to our editor and
create a new CSO script. Let's call this
activate action editor. Let's open this newly
created editor script. First, we need to import the Unity editor and
also make sure it derived from the
editor based class and add a custom editor
text on top of it, attribute and pass
the type inside this parentheses and access
the activate actions. I'm going to move my script to be side by side with
its editor class. Once we created this, we can safely delete
this and now we need access to our sterilized field, but serialized property
to our custom GO list. Now we have created this
serialized property. Let's create a new
unenable function and let's search for it for the erilized property by accessing its erized object
and then find property, and we can just copy its name
here, and paste this here. Now we need to create
a new override method, which is our uninspected UI. Let's just delete this
base on inspector I. I want to create a custom method should row
our custom object fields. We can just pass the
serialized property here. Call it custom list
and save this first. Let's look through its member, since this is a sized property, we need to access it array size. For every member, I'm
going to divide it using a begin vertical method and add a box parameter to
it, it will draw a box. Then at the end, we want
to close it with vertical. And inside this loop here. Let's draw the custom list here, and we can also create or
since it's quite small, we need to only access activate the game
object and the bulon we can just use a big horizontal
and horizontal also. We can draw using the editor, Go property field, and we can
get from the custom list. Element at index of
I and we can find its relative property for
the relative property, we want to access this here, the game object and
the active status. Let's just grab this. I forgot to add the quote sign. Then we want to draw
this active status. Just going to copy
from this class here, and let's paste this. But I'm going to make sure that the toggle here will only have a width of a very small
width, 25 pixels. Most of the space will be
occupied by the game object. For the game object, we can add a label using
the new GY content, and this will be the
game object. Okay. Let's save this. Now
in order to draw this, we need to make
sure that first we run the api method from
our cerlized object. And then at the end
of the inspector UI, we need to apply any
modified properties. And we can run the method that we just created and
passed this custom GO list. Let's save this. This
is not a semicolon. It's a full column, so
I need to change that. Now we've already saved this
and let's go back to Unity. I'm going to create a
new empty game object. To test this editor, go to our actions and
activate actions. And we forgot to create a
button, so we need to do that. Let's just create a
new button here on top of our draw custom
object fields here. Using the GI layout, and this button is
for adding at three. We can call this
button at entry, and this is for adding a
custom object fields here. Let's just access our custom
GO list and then we can insert using the
insert array element at index and we can just
pass its array size. Yeah, we already
created a button, but if we add entry feature, we also need to add
a remove button, so we can just add a
remove button here and let's just draw button. With an x character, and then set the
width to also 25. And inside this statement
here. Oh, I forgot. I need to put this GI out
inside the argument here. And then we can just safely delete the customer list here. And we can use the delete
array element at index of because we look through the index inside
this function here, so we can grab the
index right away. Okay. And perhaps to make this tidy, we can just create a GUI layout, and we can run a big
vertical, and we can end it. We can also put an
n vertical here. The activate toggle and the delete button
will lay on top of each other in a single
horizontal space. Let's save this. Let's go back to unity and
once it compiled, render our buttons, and once we press at entry, it
will create this. Here is an issue, as
you can see here. The active status label
it gets rendered, so we don't want to do that. A we can fix that by adding another entry as a parameter
before the GI layout. Here we can just
type GI content. We don't want to render
any label on it. Let's just save this. Go back to unity and wait until it updates. Now we have this here, but the position is off here
and we can delete this, we can add another
entry and we can delete this is the
activate status, but the layout is
not really nice, so I think we can fix this. Now in order to move
activate status and delete button to be at the
edge of this inspector here, we can just copy this
I layout with value. And put it on the begin verticle inside the parentheses
of begin verticle. I think I'm going to make
this button smaller, maybe 20. Save this. Let's go back to it and see
Now here as you can see, it lays out nicely and we can delete and
add more entry to it. Now we need to test
the script out. Let's just use our PC here or I think I'm going
to create a new MPC. I duplicate the first one and
put it here and rotate it. I'm going to delete
the message action. And I'm going to use
the activate actions, and I'm going to direct the actions as the actions
for our interactable. And I want to enable some of the box and
disable some of the box. I'm going to create
a new cube here, and I want to disable this one. If I go back to the
second PC here, I want to direct the Q five
to be the first slot here, and then I want to enable this. I want to disable the third one and put the third as the second three on
our game object slot here. I want to make sure
this is unchecked, so it will be hidden. Let's go to let's run this
and test this out and see if those game objects start appearing and
disappearing. Let's click this. There you go. Once we arrive, it can enable and
disable the object. And we need this class for enable and disling status later for disling the child
object of every NPC. So whenever we already give some items or
receive some items, we want to set a different
behavior, different message, so we can enable and disable the child object and those
child object will contain the message actions or any actions that
we need to create a different behavior. Okay.
21. 20 Importing Assets: Okay. Now in this video, we are going to import the assets needed
for this project, so we can go to the asset store. Once we open the asset store, we can search for tiny people. This one. We are going to
download this one, this free one, this demo, and this will act as our
character in our game, press import or if you
haven't downloaded, press download and
then import it and just import you our assets. Now it's already
imported on my end. Another thing we
need to import is the everyday animation
motion pack. But we need to search
for the free one and this will have
a sitting animation and we are going to need
those sitting animation. Let's just download it and
inport to your project. Okay. Once we finish to download this, the next thing we need to
import is our environment. For the environment,
I've already provided the package file, the Unity package file
on this lesson here. The link is on the
video description, and once you download
it, just import it. I'm going to import package
and choose custom package. Then here, I'm
going to browse to my environment package that
I've already prepared. Once you found the
file import all Now, after it is imported, we will have all
of the environment needed to build this
point and click game. I think this is all for
importing and after this, we are going to continue
develop this game.
22. 21 Player Animation: Now we are going to
change our player here. Instead of using a
cylinder or a capsule, we are going to use
our character objects. Let's open our T people and
under the prefabs folder, we have this male B and this
will be our protagonist. Let's just direct this
male B to be the child of our game object and
reset its value. Okay. So now we can just delete our mesh mesh render also, and we can move the capsule
collider on the y axis. Should be around and we can just delete
the cube here also. Now we need to
make sure that our game object is
facing the same way as our z direction of our
parent de player game object. Let's go to the child object and reset the y rotation two zero. Now we need to adjust our capsidr and our knife
mass agent gizmo here. Let's just moved and make
this smaller, maybe 1.7. Then we can see it
from the side here and I can just slid our y
offset of our capsule here. It rests It rests on. Something like
this, for the nin, we want to set the height
to the same value as our capstort's just
set it to 1.7. Then I think Can we
just offset this? Yes. Using the base offset here, we can offset it and
set it two zero. It will sit nicely
with our game object. Yeah. I think I'm going
to set this 20.5. We have this set correctly. Let's go back to our
perspective view. Let's move our player to rest
on top of the ground here. And now if we go to our
player game object, we don't have any animator
controller currently created. So let's just create
a new folder, and let's just call
this animator. And inside this animator, let's create a new animator
controller for our player. So let's just call
this player animator. Then we can drag this
player animator, this object but the
child object and drag this player animator to be the controller of our animator. Now if we open the
imator we have this empty mechanism state, we can just create a new
blender and this will be the idle state. We are going to use a blender it will blend nicely from
idling to moving. Let's just go to the ileu click it and we can
open the blender here. If you can see here, It creates a new parameter
automatically. We can just delete this and create new one or we
can just rename this. I'm going to rename
this from blend, change the speed and this
parameter has type of float, is a type of float,
now it's being used by our blenre let's create
two different motion. The first one would be
the male idle and we have this I think we have
this maid A or B. We only have male idle A, so let's just use that
for the movement, we want to use the male walk. The mail walk, the one without the underscore r at the end. Now we can see here
if we play this, let's say we scrap
its value here. It will switch to running. To walking running, but walking. Now we have this setup. We need to change the
speed here via our script. Let's go to assets folder and let's go to the
scripts folder. Let's create a new script script and let's call this player. Okay. Animation. Now we have
created player animation. Let's open the script. This will be actually very easy to do I want to try to minimize using a mono behavior class
in a game object. We can just remove this mono
behavior class and we will intandla animation onto
our player script. Since we are not driving
from mono behavior, we cannot use methods such as get component and
final object of times. We need to create a
new private animator. Let's call this animator. We want to create
a new public void and let's just call
this initialization. Or we can just call this and we'll need to pass
an animator component, and we can pass this
to our animator. Let's just type
animator and equal. We need to run
this public method on our start method
inside the player script. We need to also create
a new public method, and let's just rename this. I'm going to delete
the command here, so it's not confusing. Let's just call this
update animation. To make the animation
work nicely. We can just access our animator. The basic thing we need
to do is we need to modify our speed parameter. We can just access that by setting the float
here if you see here, we have a string name or
we have also a string ID. We can just create a hash
ID for this purpose. Create a new integer
and let's just call this speed and we can set this value to meter string to hash and we can
pass the string name. In this example, we have our parameter is
called speed with capital S. We need to make sure that we type the name
of the parameter, and this will convert
this to a hash ID. We can use this hash ID for passing or setting the
float that use this parameter. This will be much quicker
instead of if we're using string for
every frame because we are going to run this
method every frame. For the value, we need to
we need to pass the speed, but we don't have speed here, so we are going to pass
a float this method, we are going to use this
speed to drive our animation. Let's end this with
semicolon here. Save this. And now we
have this class created. I'm going to delete this
unused libraries here. And let's go to
our player script, and we are going to create a new private and let's
just get player animation. And let's just call
this animation. On start, I don't think we can use this. Let's
just call this player. Let's just instantiate new player animation
here on declaration. On start, we want to initialize, so we can just access and
call the init method. But since it as
for the animator, since the component is
not on this game object. We need to search
for the animator in children and search for the
type, which is animator. Since this is a
method, we need to add parentheses and this
will return animator, and this animator will
be passed to our player A we want to pass our falsity
from our anthems agent. We can just run the
method inside update player AM and I think
it's update animation. I asked for a flute here, so we can just
pass the agent and I think we have not
current fellow City. This is the setting for
velocity, but let me check. Yes, we want to use the velocity and since
this is a vector, we want to get this magnitude. But because magnitude it requires a root square
root calculation. It's not very performance. We can just use
the SQR magnitude and it doesn't really matter. The value difference
if we are idling, this value will be
zero and if we moving, of course, it's going
to be greater than one, because our maximum
speed is around 2.5 or three in the FMS
agent in the inspector. Let's just set this like
this and save this. Now, once we finish modifying our player script,
we can see here. Now it's already added, and we don't need to add
this player animation because it's not a mono
behavior, actually, we are running the script via our player script by creating
a new instance here and then initialize it because this class here cannot
find animator on its own. And then we are running the
update animation every frame inside the update function
of our player script here. Let's just save
this and go back to unity and save the scene also. Now if we press play, it should play the
animation here. Right now it's link and if we
move here, as you can see, if we move there, I play
the walking animation. So this is how we create
walking animation, and as you can see it creates a very nice effect
using the blendery here. Okay. So that is basically for the
player animation. Okay.
23. 22 Animate Actions: Okay. So now let's create a custom actions for
playing custom animation, and this actions is
mainly for the NPC. Let's just create a new script inside our scripts,
actions folder, create a new CS script, and we can call this
animate mate action. Once we create this, let's
open this in Studio. Now we need to make sure that this class
derived from actions, and let's just implement
the abstract class here, the override, and we
can just past this here and replace our
update function. Now, we are going
to need to create a custom classes and
this custom classes will act as a serialized field to setting up
animation parameters. Let's just mark this
as serializable. We can access this from the inspector and
let's just create a new public class and
let's call this parameter. Okay. Basically what we need
to have as a field here, we need to create a new SS field and this should be
the type of string. Let's just call
this trigger name. The next one is a type of float and this will
be the invoke delay. The last one we need
to do to create this, we need to create a
public properties and the type would be integer. Let's just call this hash ID. Hash ID is basically
a integer values that can be accessed for
accessing the trigger name, and we can convert the
string teger name to a hash ID using a special
function from the Amter class. For this public properties, I'm going to make sure that we can get it from under script, but we can only set
it as a private set. Like this, we need to end the private
set with a semicolon. Then I'm going to create a new public method and this
would be called hash ID. Basically, what we want
to do is we want to set our hash ID to be the value
from the animator class, and then we can search for a method called string to hash, and we need to pass
a string name. For this, we are going to
pass our trigger name. Basically, the string will be used for setting a trigger to transition from one
animation state to another animation state. I'm going to show you how to do this in a moment after
finishing the script. Now we have this class created. Let's just create a
new serialized field, and let's make this
a list of parameter. And I'm going to call this anyms let's just initialize it. Another thing we
need to create is we need to create
a new RS field, and this will be a
list of actions, and let's just call this actions and let's just initialize it. This will be a chaining actions. Whenever we finish all of
the animation playing, if we want to run
a certain actions, we can put another actions
into this actions here. Save this. Now we need to
initialize our animation first. Let's just create a loop. For the loop here, we want
to make sure that if I is lesser than the count
at length that count, then we want to initialize it. Let's just run the names index of I and run the init function. Basically, this
function will populate this hash ID properties
and later when we want to set trigger
some of the animation, we can use this hash ID instead
of a string trigger name, and this is much
more performance. Now we have this. I'm going to delete this
system not implemented action. Let's create a new
routine enumerator, and I'm going to
call this animate. We are going to
create a new integer. It should start from zero, and I'm not going to use a loop here because
I want to halt the thread or the coroutine whenever the animation
is not finished. I want to use a while
and if I is less than our any co then we want to run the loop
inside this while here. For the first line, I'm going to make sure
that we are going to yield return and using the
keyword new weight 4 seconds. I want to pass this
invoked delay here. But since this is
a private field, we cannot access it right
away from our named class. I'm going to create a new
public properties type of flow I'm going to call this
invoke delay with capital. Then I want to set the get value and I want to return, it's Invoke delay field value here and close it
with semicolon. We want to return
whatever value that we are going to feel here
in the inspector. Then once we have this setup, we can just use the NMS list and then xy and then we can get
the invoke delay value here. We want to only get its value and we only want to set this
value in the inspector. Okay. Now, once we delay this
based on the invoke delay, we want to trigger
the animation. In order to trigger
the animation, first, we need to create a new
private animator field. And let's just call
this animator. Okay. On start, I want
to grab this animator. Let's just run the animator and then let's just use this
get component in children. Because the animator might be might be added on
the child object, the mesh object, not
on the parent object. This way, we are going to
make sure that this get component searched
through the child object also for any animator component. Then we also want to make
sure that we take this with a required component
type of animator. Once we have this animator, we can just trigger
the animator by using the set trigger method from
the animator object here. Instead of using a here, as you can see here,
if I close this, let's just try to retype this. Instead instead of
using a string, we are going to use
this integer ID, and we are going to
use this hash ID. Let's just access our index of I and then grab the hash ID. Once we setting the trigger, we are going to
increase the value, and then we want to halt the
cotan up to this point here. We can just type yield return, and then after finishing
the while loop, we want to run any actions that we assign in the inspector. Let's just look through
our actions list. Then for every actions, we want to execute
the ac method. We cannot use I. I'm going to double click on
the I here and set it to J and change all
of the other integer. We are not going to use the I, we are going to use
the index of J, and then we want
to run the method on every action that we
assign on the inspector. Now, once we have this animate, let's just run the routine, let's just start routine and
then pass the rootin name, which is animate and
the set of parent. And then close it
with semicolon. Once I think this should work. Let's go back to unity. Now let's try to set up some NPC that we can interact
with the player here. I think the first one
has some dialogue. Nice. It as something. We can just expand this NPC here and now we can put a character. Let's go to the two tiny people. And go to the precept folder and I'm going to direct the
police game object and set this as a child of this NPC here and go to our scene here and
let's set it up. I'm going to set this to zero. I want to delete the cube
here and also delete the capsule mesh filter
and also the mesh render. I think we can set this to the same value that
we have to our player. Let's just copy our capsule
coder from the player. Since the character
size is the same, we can paste the copy value to our caps paste
component values here. Yeah. So we have the same
alignment for our collider. And also for the I think collider is enough because we don't have
enough mass agent. So let's just set
this two zero or 180, so it face our character. And if we I think we need
to set this two zero, the Z value will
point to the player. The player will stop in front of this police officer and we can just rotate the child
object on the yx20, so it's facing the
same direction as its parent and we can add the animate action into this child game object and now I'm going
to set the two one, and we are going to
set a value here. But first, we need to create some animator for the
NPC, let's just create New animator inside
the animator folder, I'm going to call this
call this police animator, and we can direct
this police animator. Now if we go to our
animator window here, we don't have any state. Let's just create two
different states here, and this one will be idle, and the other one
will be talking. And we can create a
transition from the idle, make transition, right click
and then go to the talking. Then from the
talking, I'm going to create a transition
and go back to idle. For changing a transition, we need to create a trigger. Let's just press the
plus button here, create new parameters on
the parameter windows, make sure it's a parameters, not layer and go to the parameters and then
press the plus button, and then press the
trigger option, let's just call this talk. For the transition from
the idle to talking, which is this one, the one
with the arrow pointing up, we want to set the
condition to talk. Let's just press plus button in the conditions here and choose the talk trigger for a trigger, there are no parameters
that should be set up, choose the trigger and
it should be okay. And we want to disable
the has exit time and for the transition
from talking to idle, we want to make sure
it's to has at time, but the conditions is empty. So we want to once it finished doing the
talking animation, we want to go back to idle. For the idle animation, let's select the state here
and then choose the motion, and I'm going to choose
the male idle A. Okay. And for talking, let's just try using the set male at set up on chair because we don't
have talk animation yet, but we are going to import
some animation later. Let's just use this one and see if the animation
is changing or not. And I think this is we already
set up on the im of site. So let's just select our NPC
here and reposition here. So it sits it rests
on top of our ground. It's not floating like before. And now, whenever
we are interacting, we want to add a new action, and then we can drag TTD
pole object here and it will detect the imit action automatically and save this. For the nate action, select the game object that has the animator component
for the imate action, we need to set the
trigger name to talk. I don't want to give any delay, so I'm going to
set this to zero, but if you want to
delay the animation, you can just set this to some value and this
will be in second, for the trigger
name, make sure that the trigger name is the same
as our parameter name here. It will trigger this transition. Let's save the scene again. Now let's run this
as you can see here, we have a idle animation, and you can see that in
our NPC here, animator, the idle is running with this blue bar here, you
can see it's running. If we try to click on
the police officer, you'll see it plays a
different animation. It starts talking, but
once it's finished, then it goes back to
the if we click again, as you can see here, and
we can just dialogue. Okay. Okay. So yeah, this is basically how we
create animate actions, and of course, we
are going to change the inspector appearance
using a custom editor. Okay.
24. 23 Animate Action Editor: Okay, so now we are
going to create a custom editor for
our mate action, and let's go to our
scripts folder, and under the editor, let's
create a new CSO script. And let's call this
animate action editor. Now let's open the script. As usual, we want
to modify this. It imports the unity
editor name space, and then we want to make
sure that the class derived from our editor class. Make sure this will inspect the script that has
type of and action. Yeah, save this and we are
going to delete this method. Now we are going to create
a new serlize property, and this would be the anyms
and also the actions. Yeah. Okay. So now we are going
to create a enabled method. And for the enable method, we want to search for this
erzed property for the ys. Let's just search it from this erzed object and using
the fine property method and search for the parameter or field that have
this name here. Let's just open our
animate action. I'm going to move this to be side by side with our editor, and I'm going to copy the any field names and
paste this here. For the actions, let's just do the same using
the fine property, and then access it by its
name, which is actions. As you can see here, save this. Now we are going to overwrite
the on inspector GI. Let's just do this
base on inspector GY. First, we need to create a
button to add parameter. Let's just create a
new G layout here. Button then let's just call
this ad animation parameter. And as usual, we need to make
sure that we are updating any serialized object in
start of our uninspected GI, and then at the end of
our uninspected GI, we need to apply any
modified properties using the apply modified
properties method. And then. Basically, whenever
we add new parameter, we want to add a object or
an entry to our s list here. Let's just access our nyms. Then we can using the insert
array element index and we can pass the size
of our nyms array. It will always increase by one. Now we need to draw
the inspector. Now we are going to
draw the an inspector. Let's just create a new method. Draw, for this, we
are going to need a serialized property also let's just call this entry
also the integer ID. Okay. Now, as we can see here, our yams has two post field that we want to draw.
Let's just do that. We are going to create
a new go layout, and we are going to create
a new begin horizontal, I think horizontal so do we want to pass
the box parameter, so it will draw a
nice box for every e n we want to end this
with a n horizontal. And now we want to
create a editor out, and this should be
a property field. For the property here, we want to get the
entry and we want to find the relative property
and the relative property. I think it's called a
trigger name, call this. For the label, we
are going to create a new I content for the label, I think we can call
this trigger name. Close this. Then the next thing we
want to draw is we want to draw the property field also and this would be the fine property relative and this should be the
invoked delay, so let's just copy this name
here and paste this here. L but I content, and we can just call this delay. Like this. And since
this is a float field. I'm going to make sure that
the width of our field. The second field here is small, so I'm going to
make sure that we are using the GI layout. I think I don't know. Let's just try the 60 pixel. If it's enough or
not or if it's not, then we can just change this. And now we are going to
look through our any array. Okay. And then we want to
draw inspector and pass the nyms and then get
the element at index of I, and we want to pass the
number as an ID here. I think we need to
change this, we are going to create a
quick changes here. We want to create a new
begin vertical box, and we want to end this
in vertical fashion. Then inside here, we want
to create a layout option, layout options, but
lay the normal layout. And we want to set a begin horizontal here
without the box parameters, and we want to create
a delete button. Let's just horizontal here, and we want to align
this delete button with our trigger
name field here. Let's just create
a new statement, layout and then button, and I'm going to pass x as a
string for this button here. Let's just set width of this
button to a very small one, which normally we
are using 20 pixel. Okay. And sorry, we are one less
parentheses here. Okay. We can just enter
this and create gap, so it's much easier to read. Okay. Okay. Now whenever we delete
we press the delete, but then we want
to delete the ys, and then we can just delete
element at index of ID. We want to delete
the array index of the ID that we are passing
through the method here. It will make sure that we delete the same entry that currently
we are modifying here. Another thing we can do is
we can create a action. Let's check our editor
extensions here. As you can see, we have
draw actions array, so we can just use
that right away. For the extensions,
we can access the editor extensions and then we can just draw actions array. Then we can pass the
actions array here, which is the actions field here, we can just label this action or actions chaining
something like this. Let's save this now
let's Is that all? I think this is all. Let's go back to unity and let unity compile and see if there's
any error or not. There are no error. Let's go to our NPC and the police game
object, and let's check this. Now we have this drawn here. But I'm going to fix this
here as you can see. I'm going to make sure
that our property field, the second field here, the
invo doesn't have this layout. Delete this layout with
altogether and save this. Okay. Okay. Now, as you can see here, it lines up nicely, and if we delete this
yes, there is an error, as you can see, It tries to draw once we have deleted this.
We need to fix this. Whenever we delete
the array here, we need to return. It will stop this and it
will not draw this anymore. It will ac this method
and it will try to draw the inspector on
the next frame again. So that compile and once it
compile I think it should Okay. Let's try this again. Now we have delete this
If we add eimation, it creates new one and if we delete this, it
delete automatically. So we can create a lot of
animation and we can chain animation using this delay in second here and
just delete this. Okay. So now let's just set
the top trigger back and set this delay in sec two
zero and we can add actions. Let's just once we
run the action. We want to hide the
police officer, so we can just use
the activate action, add an entry and direct this action as a
action chaining here. We want to hide the NPC,
the parent game object. Set this to height to
disable and save this. Now let's play the game and let's go to the police officer and once
it plays this animation. Okay. It's missing. But it's missing right away, so there is an issue here. Okay. I found the
issue, and basically, the problem is when we add a changing action inside
our animate action. It will execute the
actions right away, even if the animation
is not done yet. Because as you can see here, we have a delay in second, which is zero for our top here, when the idle goes
to to right away, our script also execute the
actions right away here. We need to modify here and we need to grab the length
of our current animation. Let's just do that. For the grabbing the imator
current state length, we need to type here, return new and using the
new eight 4 seconds. Inside the parameter here, we can access our imeter then we can get the next state in full. Because whenever we are getting the state
in this frame here, this will still return the
current state, which is idle. It's not talk yet because we are just setting the
trigger just about now, it hasn't transitioned yet. We need to get the
next im state info. And this is a method and
it asks for a layer index. Since we are using
the animator or the mechanism from
our base layer, which is index of zero, then we can just pass
zero here as our index. Then we can grab its ling. As you can see here, the
current duration of the state. Now, but if we try to run this, it will still cause an issue because when we
set this trigger here, this will still
return a wrong value, and when I test it,
it returns zero. We need to add a yield return. New weight for end of frame. Basically, this yield, it will return at the end of frame. After finish rendering, then
it will run this code here. Basically, at the end of frame, we will already initialize
the new state and this will return a correct length value. Let's just save this. And let's go back to unity, and we don't need
to change anything on the inspector side. So let's just run the game
once it's finished compiled. Now if we try to talk
to the police officer, it will try to sit down first
and then after sit down, it will de activate it. So it will finish the
animation and then it will run the chaining actions that we assign in the inspector here. Okay.
25. 24 Level Manager: Now, we are going
to create an action to change scenes in
order to do that. First, we need to create a manager for handling
level transition. Let's just go to our
scripts and manager folder, and then create a new subscript and let's call this
level manager. And let's open this
level manager. Now we have the script opened. First, in order to create a transition or in
order to create a function that enable us
to switch between scene, we need to import the scene
management namespace. Let's just type here using
Unity engine scene Management. The next thing to do we
need to create a new Cerus field game
object variable, and let's just call this panel. And for the next one, it
should be a regular transform, which is transform,
and this is basically the base transform for a object. Instead, using the
normal transform, it is using this transform and this would be the load bar. And we want to create a
new private factor three, and this would be the bar scale. Let's just set this by
default to factor three one. Factor three one is basically a shorthand version for
writing vector three, one on the x and one on
the y and also one on the x. Vector value
on all x will be one. Let's save this. The
next thing we need to do is we want to make this pal manager
has a static reference. I'm going to create
a new weak method. On top here on the field area. I want to declare a new public static properties and the type will be
our level manager. I'm going to call this instance. Then I'm going to set to
have a public getter. But for the setter, it will be a private like this, so we can set it
only on the script, but other script can get reference to this
static reference. Okay. So let's just set
this instance to this. We won't create a Singleton
pattern here because at this level manager will recite as a child of
our data manager. So it will persist
across this scene. So we don't need to
check if there are any other level manager
because it will stay always together
with the data manager. We can safely delete
this update and start method because we
aren't going to use that. I'm going to create a
new show panel method and a panel method. For showing and hiding panel, I'm going to use this panel set active to true for
the hide panel, we want to set the active
status to falls Okay. So whenever we want to
enable the loading panel, we simply run the show panel, and if we want to hide it, we can simply run
the hide panel. And now on start or on awake, we want to hide the
panel right away. Now we need to create a new co routine to create a
synchronous loading screen. Let's just type
this a syn loading. We want to pass a string for the scene name as a parameter. We will define the
In co routine here. But before that, I'm going
to create another method, another local method, and
let's just call this bit bar. Okay. For updating the bar, we want to pass a float
as a value of the bar. Inside this method, we can simply just
access our bar scale, a private factor tree variable, and we want to modify
its x value to be equal to our pass value on this update bar
parameter here. Once we pass this,
we want to apply the new changed value here
to our load bar scale. Let's just access the
load bar local scale, and then equal the bar scale. S like this. Now we have a method
to update our bar. We can start create our
synchronous loading process. Basically, what we want to do is we want to show the panel first whenever we try to
load the new scene here, and then we want to yield
return weight for frame. We want to wait until the end of frame process or
frame rendering. And then we want to create a new asynchronous
sin operation. And this would be the load and for the value for
our asynchronous, we want to run the sin
manager class and we want to use the load
scene asynchronous. Then we want to pass the name, the string name here as
the target loading scene. Now, once we have populate our asynchronous load object
with this scene manager, it will have a
properties such as can check whether this
operation is done or not by using the sync load and it
has this done property. Also, it has a
progress properties and this will return a float. What is the operation
progress and this will return between 021. We can use this to pass that
value to update our bar. Now in order to loop our Anker is weighting loading,
We can type while, then we can type
the object name, which is the assing
load and we can check is it done or not, and this is check has the
operation is finished. If you want to make sure that
while it is not done yet, we can add an exclamation mark in front of this bulon here, and this will check for if
the *** load is not done, then we want to look
through this code here. I order to look this, I
want to put a return, and we want to update our bar using the value
from our progress. Let's just type update
bar and save it first. Let's create a new float
variable called progress. For the float progress, we are going to use a clam zero one and this will
clams the value between 021 and
the returns value. I want to clam our sync load progress
because as load to progress, we return 0-1, but it is
actually the loading progress is 0-0 0.9 because the last 0.1 value is assigned
for preparing the scene. We want to show the
update value 0-0 0.9. Using this clam, it will
return our progress to one. Let's just divide this by 0.9. And what this
actually mean here. Let's just use calculator
and what this means here. If our as load value is 0.9, we can test this zero point
9/0 0.9, and we will get one. Basically, we are setting
the maximum value to one. When it's reached at 0.1, we want to reap
the value to one, and if it's zero and
it will stay zero, and this clam we'll make sure
that this value won't go below the zero or it
won't also go over one. I will just keep the
value safe between 021. Once we create this line here, we can pass the progress
value to our update bar. Basically, once it's done, or if the sing load
progress reaches one, it means that it is done
and it will leave this loop here and we can then disabled. We can then hide our
panel like this. Now we have this coroutine
declared or defined. Now we need to create
a new public method. For it. This would be, let's just call this scene load. Then we need to pass the name as the target
scene that we want to load. Then we can use the
start co routine and then run the loading
routine that we created below. Then we can pass
the s name here. Whenever we need
to switch level, we can just run the scene
load from any other script, since this level manager has a static reference
and we save this. Now we have this
level manager setup. Let's go back to unity, and let's create
a new UI Canvas, and this will act for our
level manager Canvas. For this canvas here, let's just copy our scalar properties, copy the component
and just paste this on its canvas scaler, it has the same pixels values
or resolutions settings. We want to make sure
that the sort order is greater than our
default canvas. Since this is zero on our level manager canvas,
we want to set this two one. This canvas will get
rendered in front of our UI whenever it's being used, and we want to
create a new image. Let's just call this panel. And let's just
maximize the panel, so it fits the screen, and I'm going to change
the color to bland. Another thing we
want to do is we want to create a new tech mesh pro and I'm going to increase
its size to 300 by 75. Then I'm going to
set the alignment to the center and also the middle and change
the text to loading, and I'm going to use the ose. It will fit our transform width and height setup.
Now we have this loading. I'm going to push this slightly on the y position
around 35 pixels. Safe and then I'm
going to create a new loading bar
using the UI image. I'm going to set this to round 205-20-4205 and I'm going to set the value of the y posin
to run negative 25 pixel. It moves to below this text here to the
bottom of the text, and we want to set its
width to run 200 or 300. Now we have this
nice loading bar. Later, we are going to modify its scale here on the x axis. But at the current setting
at the current anchor point, this won't be feasible because it has the anchor
point in the middle. We need to fix that.
In order to fix that, we want to go to the
anchor free sets, and then we want to hold
shift so we can move the anchor to the left side of the bar here, we
can choose this. But this will also move our pivots and move
our anchor point also. Once we move our pivot here, we want to click the
middle anchor again, so the anchor stays
on the middle, but the object pivot will be on the left
side of our bar here. Now if we change the
value on the x axis, it will keep the
pivot on the left, we have a nice bar
progress like this. Set this back to one
and save the scene. Now if we go back to our
level manager canvas, we can wreck the level
script to the canvas object. Then we want to drag the panel
as the panel field here. If the panel gets
disabled via script, it will hide all of the left
switching graphic or UI, and if it's enabled, it will show this window here. Okay. And we want to direct also the image
as the load bar. So whenever we load the scene, this will get change, depends on the load
progress value. Let's just save this scrip. On the next video, we
are going to create an action to change scene. Okay.
26. 25 Change Scene Action: Okay. Now we are going to create actions for changing scene and this
actions will access the level manager to change scene and show this
loading progress. Now let's go to
the scripts folder and under the actions folder. Let's create a new CH script, and let's call this
change scene action. Okay. Now let's open
this CHR script. Now first, we need
to make sure that this change in action
class derived from the action based
class and we need to implement the
abstract method member. Let's just fix this by creating this public vero
I'm going to delete the void update and move the egg method should be
below this start method. We want to create a new
serialized field type of string, and this will be
the s target. Okay. And whenever we
run the x method, we want to access the
left manager instance, and we want to run the scene load method
and we also want to pass the scene target name
as the scene name parameter. It will change to that scene. I think we don't need to start method too. Let's
just delete this. And the next thing we
want to do is we want to store our
previous scene name, and this will be useful
to reposition or determine the player position relative to the previous scene. So the player can start from
let's say from a taxi or can start from a
door position if the previous scene is the
interior of the club, for example, we need
to create some sort of dynamic reposition at start and this will depend
on the previous name. So let's just hold that data
inside our data manager. Let's open our data manager. And then we want to create a new public properties and
this will be type of string. And let's just call
this previous scene or prep scene name. And here, we want to make this a public get
but a private set. Okay. We won't be able to set this previous
name from another script, but we can create a
method to do that. Let's just create a new public
void, set previous scene. Let's just pass a string
name, call this name. Then using this method, we can access our pre name here, use the eqsine and
pass the name. When we run this method, it will change the
previous name. Later we are going to use this for the players
paint position. Let's go back to change
scene position and now we want to access the
data manager instance, and we want to run
the set trap scene. Now we want to get
our current name. In order to do that, we need to first import the scene management
class or name space. Let's just type using Unity
dot engine scene management. Then once we import
this name space, we can access the
scene manager class and we can get the active
scene, and this is a method. And after we get
the active scene, we want to get its
name like this. This will return string, and the scene name
will be set to our current previous
scene in our deminer. Let's save this, I
think they should do. In order to test this
change scene action, we need to create a new scene. Let's just hide our panel here. And I'm going to make this
data manager as a prefab. Let's just create
a prefabs folder. I click here under our assets and create a new folder and this will be a prefer this is wrong. It should be pre open this prefs now we want
to save the base canvas here. I'm going to rename this
as our base canvas. This is where it has this dialog system object
and we want to set this as a prefs we want to also set the data
manager as a prefabs. And also we want to set
the player as a prefab. Whenever we create a new scene, we can wreck this three object right away. Let's
just save this. Now, let's just go to
our environment here. I'm going to set this
cube here or this one. I'm going to make
this interactable. First, I want to create a new material so we
can see the difference, create a new green material, change this color to green. And directly to this cube here. Now for this cube here, we want to move this
out of our environment. We want to make sure
it's interactable. Let's just use the
interactable script. For the action, we want to
run the change scene action, and let's just save
this scene here. Under the scene, we want
to create a new scene. Let's just create
a new scene here. Let's call this city. For this cube here, Let's just set the scene
target two C. Whenever we click this green cube here, it will switch our
scene to scene here, save the scene and let's open our C we have previously
imported our scene model. Let's just access the point click folder, and
if we open this, it has a prefab and
I want to direct the city scene here and
once it's imported, as you can see, it has
this nice city model. Now, let's just put our player
somewhere inside the city. I'm going to put the
player to be here. Sorry. I want to put the
player to be around here. I want to put the data manager
and also the base canvas. This is basically baking, but it takes too long because
it has lights and stuff. We want to go to the
lighting properties and we want to disable
the auto generate. Now we want to change
the indirect samples, if you don't have the
lighting windows open, just go to the window
and under the rendering, we have this lighting setting, and this will open this
lighting settings. We want to set this to not
shadow mass, but big indirect. Then we want to set substrative. And then we want to set the indirect examples
to a quite low. I will set mine to 50 and the direct examples
to around 24. For the light map size, we can decrease this to 512, five 12, save the scene and make sure that the atto generate is disabled and less
generate lighting. Of course, we are going to
tweak this lighting later. But right now, we want to set
this to a quite low value, so we can run this scene without baking every time there is a changes on the
lighting and stuff. If you can see here, bakes the light nicely
with the settings. Once it's finished
baking, we can continue. Let's just reposition
our player. I want to rotate
the player so it will face to this building here. Let's just rotate to
180 on the y axis. Okay. Now if we go to our city game object, it has a static
properties check, and this is also applied to
all of the child object. So now we need to make sure
that we go to the navigation. We need to make sure
that we bake the scene, so it has naviigation mass. So let's just run
this and press bake. Okay. Okay. Now we have
this navigation mesh, and the lighting
baking is also done. Here, it's saving the light
map to the EXR files. Now we have this set up scene. Let's just save our scene. Let's test this.
If we plus play, can we run or can. We need to set up our camera. Let's select our camera here because it's
in the center here. I want to set the few something like something like this. Let's just select
our main camera. Once we have this on
our perspective view, we can press control shift
and F to set up the camera, the same position as
our preview here. If we go to the game view, you can see here, we
have set up nicely, but we want to more because
we can see a hole here. Let's just zoom once again, and then realign our camera. Now we have this nice
scene here and save this again. If we press play, Okay, there is an error. Let's check what kind of error? There was an error previously. And the first thing we need
to do is we need to make sure that the city has a
default layer first. So let's just set this and
apply to all of the children. And There is an error
on our extensions. This method when
this method is for checking if we are clicking on the UI or on a game object, if we click on the UI, it will move our character just like in the previous video, the error arise because we don't have the event
system on our scene. Let's create a new under the UI and let's create
a new event system. Save this. This is created automatically when
we create a new canvas, but since we drag this
canvas from our prefabs, it doesn't create
this automatically. We have to create
this by ourselves. Now if we press play, you'll see that we will
move on the scene here. Now we have the setup. Let's test our change
scenes action. Let's go back to our
sample scene here. Now let's press play. Then I'm going to click on
this green box. Once we arrive, it
will switch our scene. Okay. It doesn't switch, let's check our cube here. We already set the
change scene action, but we haven't assigned to the intertable so let's just direct this script
to the action, and it will create a new
entry and will access this change scene action
and state the scene again and let's test this again. Now, it open, but
there is an error. Let's just check. I also forgot to put the scene
in the build setting. Now we need to go to our build settings
and all of the scene that needs to be accessible by the scene manager needs to put inside the scene in build here. Let's just go to our project, our scenes folder and drag
the scene to this sins build. Once we have put the c here
in the build settings, it should work. Let's
test this again. Okay. Now it loads and change
scene as you can see here. For example, if let's
say on our scene here, let's say we want to create
a new empty game object, and this empty, we'll
have a box collider. Then we want to move this
box collider to be around this do For example, we can just edit the collider
and change its size. And then we can easily set this to intable then we can use
the change scene action, and we can change this back to our sample scene and
assign the actions. Let's go back to our
sample scene here and we can go back and forth
of these two scenes here. If we go to our city scene here and if we go
to the door here, it will go back to our sample
scene. There was an error. Whenever we switch scene
on the next scene, we cannot find the level manager instance, the static reference. We need to change
how this works. Basically, if we go to
our level manager script, we don't need the static
reference right now. We can just cut this first. And we remove this
static setup here. Set hide panel on
awake and go to our data manager and we want to paste this static reference, but we want to make sure that this is not
a static anymore. Let's just delete delete
the static keyword. We can call this instance
because instance is already reserved for
the static reference. Let's just call
this left manager. And then upon k, we want to fill this with
the level manager component. Since the level manager is the
child of our data manager, we can just type
level manager equal, get component in
children and this will search the component in the
children of this game object, which is suitable for our case here and search for the
level manager class. Okay. Okay. Now we have access to level manager
via our data manager, and we need to change
a couple of things for level manager is already working, it
should be working. We need to change the
change scenes action and right now instead of
accessing the level manager, we need to access the
data manager instance. But the scene load is under
the level manager object, so we need to access the
level manager properties and then run the senad
method like this. Now once we modify
this, this should work, I've tested before and now go back to unity,
save our scene. And we need to apply this
prefs one more time. Our prefs on the assets
folder gets updated. Now if we test this once again, it will load to another scene, and if we go to this door here, it will load back to our
previous scene here. Now our change scene
action is working and the next thing we need to create is the player
spawn position.
27. 26 Player Spawn Position: Okay, so now we are
going to create the player position manager. In order to do
that, let's create new CS script under our
scripts manager folder. Let's create a new CS script. Let's call this
player Spaw position. Once we created this new script, let's open this on
aficio Studio or Monell. Now let's just
create a new private and this will be a
type of transform, and this will be our player. We are going to create a
custom class to hold the data. Here below the players
pon position class. Let's create a new class and let's mark it
as serializable. Let's call this poner. For this punter, let's
create three field. The first one would be the
previous scene or prep scene. The second one would be
a type of vector tree, and this will be the position, or we can just call
this saw position. The third one would
be also a factor, but this will be
a saw direction. We will create a public getter for each of this field here. First, let's just create a
new public string properties, and let's call this the same
name with capital letters. And we're going to
only set together, and this will return
our prep name. The next one will
be a vector tree, and this will be
the spawn position with capital S. Then we want to return the saw field value. The third one would be
also a vector three, and this will be the
spawn direction. We want to also return the
spawn direction field. Now save the script. Now we can create a
list of our spawner. Let's just create a
new series field in our player spawn
position and then create a new list and the types should be our spor that we
have just created here. And this would be
the spawn and trees, and let's initialize it by
typing the new less spawner. And we are going to
delete the update method. Basically, on start, we
need to find our player. Let's just type the
player field here. Then we can use the
game object class and then find object with tag, and we can search
the player tag. But since find game object
returns of game object, we can access it
transform by typing the transform because player
is type of transform, so we need to search for
the transform component of the found game object here,
the search game object. Then we want to reposition
once we found it. This is a method and we are going to create
the method now. Let's just create a
new method and call this reposition and we want to look through
our spawn entries. Let's just create a four loop. Then for the length, we
want to type entries. We want to search through
the spawn entries and If the data manager instance
previous name is equal to the pw entries index of and we want to get
this previous name. But since this variable has a type of private and
serialized field, that's why we are using
this public gather. Let's just type the name. Then if it's equal, then we want to set our
player position to be the same as our spawn
position and the direction to be looking at the
factor direction here. For the position,
we can transform, but player position equals to this entries index of I saw
position for the direction, I'm going to set the rotation to be equal to quaternion class, and then we want to
use the rotation, and then we want to insert the saw entries and we want
to get the spawn direction. Let's save this script. Now we've created our
players spawn position, we can save this and
let's go back to unity. Here, we are going to create a new spawn position manager. Let's just create a
new anti game object. Reset its value, Let's just
call this spawn manager. Then let's drag the
script spawn position and we can create a new entry. Okay. And now we can
just for example, we can just put it if we
are from the CD scene here, we can type the
previous name and we can set its
position, for example, to be maybe around
let's check this here. We can set the x23 and the z two negative five for the
direction, for example, if we want to make the player looking at the
negative x direction, we can just insert
negative one on the x axis and it will make the character
looking to the right here based on our camera few. We are going to save this in
order to make this works, we need to tag our player game object
to have a tag of player. Let's just do that and I'm
going to apply this prefabs Save the scene and now I'm going to reset
the transform value. But we have this value already set up in our players
pon position. Right now, if we press play, the player will start
from this position, as you can see here. But if I go to the city
here, After we load this. If we go back to
our sample scene, you can see the player, we'll start from this position here. This will be useful if we
are entering a building, and then we go back out
from that building. We want to start from
the door position of that building in the city. With this spawn manager, we can create as many
entry as you want. If we are arriving
from different scene, we can set a different
spawn position. Of course, we are
going to set up a custom inspector to
make this setup easier.
28. 27 Player Spawn Editor: Okay. Now we have position
script working nicely. Let's create the custom
editor for the script. Let's go to our
script editor folder and create a CH script. Let's call this
player Saw editor, and let's open the script. Okay. Now we have this
players editor open. I'm going to set up a couple of things before we
continue the script. As usual, we need to import
the Unity editor namespace, and then we need to
also make sure that inherits from the
editor based class. We also need to mark or this custom editor and insert the type class that
we are going to inspect, which is the player
spawn position. And for this script,
we need to only create a serialized property. This will be the spent the
array of our sponor entries. Let's just delete the
start and update. First thing we want to
do is we want to create an enabled method
for enable method, we are going to search
for our sponor entries and we can just access
the serialized object, and this is the
inspected object. Using defined property,
we want to search for the serialized field here. Let's copy this name here, and then I'm going to paste
this to our string argument. And this will search
for this field here. Now let's just create an override for our
inspector GI method. Let's delete this
based on inspector GI. As usual, we need to make
sure that we are going to update before we are
modifying the inspector. Then at the end,
we need to apply any modified properties using the apply modified
properties method. Now we need to draw the re. Let's just create a
new method to do that. Let's create a new method
called drop entries. Then we are going to pay pass the serialized
property here. Let's just call this spawners. We want first to create a new Q layout and this
will be a begin vertical, and we want to pass
box as parameters. It will create a custom
box inside our inspector, and then we want
to end vertical. After creating the
begin vertical, I'm going to create
a new. Editor. Sorry, it's wrong
editor I layout, this would be a
field Label field is a fixed label that
are not editable. Let's just call
this per entries. We want to check first, if the per array size or the count member
is equal to zero, then we want to
create a new button. Let's layout button. Let's name the entries. And we want to add a new
entry for our sponor entry. Let's just use the insert
array element at index of and we want to pass the
sponor entries array size. It will always add new member
at the end of our array. We want to loop
through our entry so let's just create
a new for loop. We want to look through
our sponor let's just use this keyword here for the
poner entries and also for this one because
we're already passing our son are going to pass the poner entries
to our son argument here. And we can access the
poner array size. Now we need to draw
each of the entries. Let's just create
a new GI layout and this will be
a big horizontal, and we want to make sure
close it by horizontal. We will need to draw
the sponer settings. We have three settings,
which is the previous name, the position and the direction. Let's just draw edit g layout, and this will be a type
of property field. Property field, and
we need to pass the seriz property
for the property, we're going to use this
poner and we are going to get the array element
at index of I. Then we want to find
the relative property, and for the first one, it
should be our previous name. Let's just copy this name
here and put it here inside a and then For the next one, we can just copy this line
here and paste it a couple of times for the saw position, we want to grab
the saw position, and for this saw direction, we want to grab the
spawn direction. This is going to draw a basic property field
for this entries. Then we want to draw a set a begin vertical and we want to close this
with a vertical. And this is for
creating a button. We want to create a
button that will lay out from top to bottom
and not horizontally. This button will
resides on the side of this beside the
three properties. Basically, we want to create a button to delete to
be able to delete. Let's just create a new button and we can put x character. For the size, I'm going to set this Q layout to be 20 pixel. For this, whenever we
press this button, we want to make sure
that we are going to delete the array
element of index of i. Here, we want to make
sure that if i is equal to the array
size minus one, which is the last
member of our entries, then we want to
create an ad button. The ad button will be only available to the last
entries of our spons. Let's just create a new button. And at a plus sine. Let's make the width the same as our button above
around 20 pixel. Then when we add this, we want to add new array, just like this at entries. Now we've created this sponor
entries. Let's draw that. Let's just run the
method entries, and let's just pass our sponor entries
serialized property here. Okay. And save this and
let's go back to unity, and it's still compiling,
but once finished, we can check our spawn manager, and now we have this here. There is an issue here. I forgot to put a
vertical layout. For this saw or settings, let's just create a
new vertical settings, and we want to close this
with A and vertical. Okay. Okay. Let's just go back to unity
and see if this fix the issue. There you go. We have this entries. As you can see here, the second entries
has the plus pattern, so we can add new entries, and we can also delete
each of the entries, and I'm going to copy
the value first, but if we delete
this, as you can see, we have this add entries button. Let's just paste the component
value and save the scene. Let's create a better
layout for this. For this horizontal.
Begin horizontal, we want to create
a box parameter past the box parameter
with lower case B. For each of the entries, we want to create a
space in between, let's just use I lay out, space, and maybe put a
space of five pixels. It doesn't stand so close to
each other to each entries. Now let's save this script
and once it's compile, we should see an update. As you can see here now, we can see the
separation very clear. And the remove button
occupy a lot of space here. In order to fix that, we can just here inside our begin vertical where we have
this button implemented. We can add a I layout. That space and make sure that the width is the
same as our button, 20 pixel. Let's save this. Now, if we go back to unity, you will see that our layout is very tight to the edge here, and we will have the rest
for our settings entries. Let's just do the second one, and let's just save this. Now we are going to create a transform gizmo on the scene, so it's easier to set
up our spawn position. Let's go back to our player
spawn editor script. Here, we want to
create a new method and this will be the onsen I. Basically, OnsenI is a
build in method and if we want to create something like transform on the scene editor, we need to put it
inside this onsen UI. So now we're going
to look through our, but our spawner entries. We want to look through
that and we can just use the field serious property
here and get the array size. Then we want to set its value, each value, each
factor three value. We want to get the array
element at index of and then we want to find
the property relative, and this would be the position. Since we know that son
position is a factor three, we can access the factor
three value here. Then we can set this two equal I'm going to enter,
put a line break here, and then I'm going to
access our handles class, and this is a custom
treeGI control for drawing in the scene view, and we are going to
draw a position handle. For position handle method, it asks for a rotation and
a factor three position. We can just set
the position here. Let's just get its factor
value again and put it here. For the quaternion,
we are going to put this quatern identity at this moment here.
And save this. Then we want to create a label. There is also a label
in the handles class. There's a label
method and it asks for position and a GI content. We can just paste
the position here, which is our vector tree value. But I'm going to move this
slide up from our position. I'm going to put a addition with a vector tree up and
multiply this by 0.5. For the label, we can put a string or AGI
content if you want to. We are going to put a string and let's just create text here, spawn from with colon
full colon space and then I'm going to
put a line break here, and then I'm going to pare
fine property relative. But we are going to get
the previous scene. Let's go back to
our player position and copy this
previous scene name. And paste this into the fine
property relative argument, and then we want to
get its string value and then end this
with semicolon. This is basically two
different lines here. If you go to the end
and then press enter. As you can see, this
is the first line. This is one line
because I didn't put any semicolon
at the end here. This is actually a
continuous line, and this is also a
continuous line here. As you can see, we
are concatenating a string with a
string spawn form, and then we are adding with
the previous name value. Here, We need to also update this realized
object on top of it, and then we need to also apply any modified
properties at the end. If we save this script, the player spawn editor
and go back to unity. Now once it compile, it will show the position of. Our spawning position as you can see here, spawn from city. Now we can move this and if
we move this position here, it will update the
spawn position entry. We can also modify this gizmo, the z direction we're always facing our saw
direction value here. In order to do that,
let's go back to our saw editors script and we are going to delete
this quaternion identity. I'm going to put a line break here so we
can create a new line, so we can see the whole
code and then just type quaternion class
and then rotation. Here, we are going to put
the factor three value, but instead from
our spawn position, we are going to grab
our spawn direction. It will take this factor value
from our spawn direction, and then it will convert
it to a quaternion. It will be applied to our
handle gizmo on our seam. Let's go back to unity
and Now it's compiling. As you can see here, because
the z is negative one, so it's facing backward
against the forward set. If we set this two zero, this will be the default value. If we want to make the player face to
the right direction, relative to screen
after spawning from CD, we can just insert negative
one as the spawn direction, and it shows that the direction of the player will be
facing to the right. This z vector here shows us in which direction will
our player be facing.
29. 28 NPC Move Action: Okay. Now, let's create an
actions for moving the PC. So basically, this action
will allow the NPC to move whenever it gets
triggered by the interactables. So let's go to our scripts
and actions folder, and let's create C subscript and let's call this NPC move. I'm going to add action MPC
move action to its name. Let's open this in so
Studio. Now it's open. The first thing
first, we need to set the inheritance to actions. It will inherit from
this base class action, and we need to implement
the abstract class act. Let's just cut this and
put this below update, and we are going to need
this update method. Since we are going to
use the FMS agent, we need to use the
Unity engine namespace. Now let's create a
couple of fields, and the field will be a
private type of names agent. Let's just call this agent. We will also need a cerliz field and this will be a vector tree, and this will be the, the direction, the
target position. Okay. And then under the start method. We can just initialize
the agent by typing the variable name agent and then using the
G component method, and let's search for the Name
agent class or component. We have set up the agent using
the get component method. Now let's modify our method. Let's create another
serious field, and this would be a type of float and let's call this delay, we can delay the action,
and for delaying, we are going to
create a coroutine. Let's just define one. Let's call this move NPC, and we can pass the
float here, the delay. Then we can just delay
by using the id return, new wait 4 seconds and pass the delay value or
the delay parameters. And then we can
just start moving the agent using the agent
keyword and then set destination to the target
position variable that we've declared on top
here and save this. Inside our act method, we can just run the
co routine and run the move PC and pass this
delay variable as the delay. We are going to use the update method for
defining the animation. In order to create
the animation, we can reuse the
player animation class that we created way
before in previous video. Using this class here,
this generate class, we can incentiate
the class and run the method inside
our NPC move action. Let's just create a new
private and for the class, it's player animation, and
let's just call this PC. On start, we can just initiate create new
player animation. Then we can run I think it's
in it and ask for animator. Let's just get component in
children because for the NPC, probably we are going to
put the game object or the visual
representation that use animator component inside
as a child object. Let's just use to
get component in children and let's
search for animator. And then close it with
parentheses and then semicolon. This is for initializing. Because with the play animation, we don't have we are not
inheriting from mono behavior, so the animator needs
to be initialized because we cannot use the get component method inside
this class here. Save the NPC move action
script and inside the update, we need to make sure that we
are passing dispute value. Let's just access
our NPC and then we can type update animation. Then we can pass the
c the agent velocity. Since this is a factor, we
can pass the magnitude, but I'm going to use the
square magnitude value because it's more performance. Since there are no
square root calculation inside this method
inside this properties. We don't want to calculate
this every frame. Let's just create a, a bullion. Let's call this is
moving or we don't need to create a
bulon we can just check if the agent is
currently stopped or not. But this is for
stop and resuming. I don't think we
can use this so. Let's just create a new bon. Let's call this is moving,
and this is false. Whenever we start moving,
we want to set this. We want to set the is moving. Okay. But whenever we
arrive at a destination, we want to stop this
inside our uplate we can check if it's moving and the remaining distance is less or equal than the
agent the stopping distance. This means that the agent already arrived at
the destination. Then we want to set the
moving back to falls. Okay. Now we have this bulon for checking whether we are currently
moving or not, we can put it inside
the statement here and make sure that we want to only update the animation
whenever it's moving. Whenever we stop moving here, we want to also
update the animation. But instead of passing
the velocity value, we can just pass
a value of zero. We'll make sure that
whenever it stop, our speed parameter here will be also zero. Okay. Okay. Save this and let's head back to unity and try this action. Now let's drag our
newly created script, which is called NPC move actions
to one of the other NPC. For example, this one, yeah. Let's just drag the NPC move
actions to that object. There is an error, I think
the error is related to the name class because
we rename this to NPC move action and the
original name is NPC Move. Let's just add actions
to it and save this. And a unity. This showed work now. Choose this or instead
of modifying this, let's create a new
one third NPC, and let's hide
this to other NPC. And let's drag this NPC move
action to the third one. Now it should work.
As you can see, we can add this let's just
delete the message action. Since we are copying this
from the previous NPC, it has this item action
and message action. Now let's drag our move action as actions for our interactable. Now we want to add
also a message action. Let's just add a message action. Basically, we can type message. Basically, we are asking for this NPC to move
out of our way, and we can direct
this message action into our interactable action, so it will run two
different actions. Let's set a delay
maybe around 1.5 second before the
NPC starts to move. Let's move him to the
zero and y also zero, but on negative
four on the x axis. It will move to run this
position or positive for, so it will run this position, if we look at the
world transform, the positive sign is to the right side of
the screen here. Let's just save this now run the game and click and it will. Now it's working, but
since we don't have the Names agent, it
throws an error. We need to add a Names agent. And we can just use
the default value. For the child object, we can we can delete the cube object and also delete the capsule mesh
fielder on the parent object, and also the mesh render. Now we can probably add a model, the tiny people model and go to the prefers folder
and we are going to use the male A as a child
of this NPC here. Okay. And let's zero out
the y position. Okay. And we can copy our settings, the names agent settings. Let's just copy coonent
then paste this to the PC, paste the values, and also for the
capsule collider, we can copy from
the player capsule collider and paste
it to the third PC. It will have the same settings. Now we can make sure that the PC is rotated
zero on the y axis, it's facing the
same z direction as its parents and I also
have a controller. For now for testing out, we can use the player animator. Okay. Since we need to access the speed parameter inside the player animation
object here, Okay. I'm going to save the
scene and try this again. Let's go to player B and. As you can see here, it works
very nice, this NPC object. We can also not P object, the NPC move action, and we can also chain the action after giving some item
or receiving some item. In the next video, we
are going to create a custom editor for
this NPC move action. Okay.
30. 29 NPC Move Action Editor: Now, let's create
a custom inspector for our NPC move action. First, go to the
scripts folder and to the editor folder
and I'm going to create a new sub script, and I'm going to call
this NPC move editor, and let's open up the script. Have you opens. Let's
try to open it again. Now we need to import the Unity editor name space and also change this model behavior
inheritance to editor, and also define
the target object to be inspected using
the custom editor tags, and type off and
then another set of parentheses and this will
be our PC move action. Now let's just delete
this two method. For the first one,
we are going to overwrite the inspector GI. And then we are going to
also create a void on C GUI. This is for drawing the
target position factor. Now, let's just create
serial as property and this would be the delay
or S underscored delay. So it matched the
other convention that we use for cerilized
property with this as underscore and then another one is underscore target position. If we go back to the
NPC move action, here we can see that we only
have two curized field. So, we only need to access this and we want to
only draw these two actions. Okay. So now on Inspector, let's create a UI layout, and this would be be vertical, and we want to add
a box argument, so it will draw a
nice box to it. Then let's end this layout
with the vertical method. Now I'm going to
draw another layout. But this time is begin horizontal without
the box argument, and then another one for
ending the horizontal layout. For the begin horizontal. We want to draw the
delay custom inspector. Let's just use the editor
I and property field, and we can pass the
underscore delay. For the label, we can just type new I content and
pass the text here, and this would be the Let's
just call this action delay. Okay. And we can also I'm
going to add a another label. And I'm going to type this sc or we can just say
this in second in sec Okay. But we haven't initialized
this, I forgot. We need to create a
void on enable method. Let's just search for this as delay under the serialized
object, find property, and this would be the delay because we name this parameter or variable delay in
the move action here. Okay. And for the
serialized target position, we need to search
for the property that are called target position. Like this, we initialize this. Now we can use it right away. For drawing the target position, we don't want to
draw the editable, but we want to draw
a read only field. Let's just type
editor layout and then use a factor three field and ask for
a factor three value. We are going to use
the target position. But we want to grab its
object reference value. We also need to insert a label. Let's just type target
position like this. Shouldn't be object
reference value, but the vector three value. K. Yes, we won't be able to
edit this factor three value. But we want to edit
the factor three value inside the s GI just like
the players pawn editor. Here inside this sin, we want to access our
S target position and it's factor three value. Then we want to
use the position, and we want to pass the vector three value
inside this handle, so it will modify Then
for the rotation, we want to set this
to tern and identity. Then we can draw a label. Let's just draw a label, and we can pass this factor three value, and for the label. Let's just use a string text, and this will be the
target position. Or we can add NPC
target position, and end this with the semicolon. As usual, since we are dealing
with civilized property, we need to update the civilized object on
start on the beginning of inspector GI and also apply any modified
properties at the end, the saving goes with our ons I. We need to update this and then apply any
modified properties at the end of the method. Let's save this and let's
head back to unity. Since we are not using
this name space, we can safely delete this
and save the editor, the editor class again. Let's go back to unity. I'm going to select
the third NPC here, and one it's finished compile, I have this custom
or for the NPC. As you can see, we cannot
modify this target position. But if we move this gizmo here, it will update our
target position. For example, if the character will move to this position here, we can test this by playing and then if we try to talk
to this character, he will go to this
position here. Okay. Okay. So this should do with the custom editor for our NPC move action. Okay.
31. 30 Interactable Custom Editor: Now, let's create a custom
editor for our interactables, because it's still using the default inspector
and we already defined the action how
to draw actions array. Let's just use that and create a custom editor
for interactables. Let's go to our scripts and editor folder and then
inside this folder, let's create a new
CS script and let's call this editor Okay. Then let's open this
up in fico Studio and let's import the
Unity editor as usual. Also, change the mono behavior,
inheritance to editor, and also add a
custom editor tech to access the inspected object. And this would be
the and say this. And we are going to create a
serialized property, first. The first one would
be the actions as underscored actions, and the second one would
be the distance position. Since if we open our
interactable class here, we can see that we have
two serials field, which is the distance
position and the actions. So we need to create those reference in
the editor script. And then we need to create
a un enabled method. Inside this, we can
just search for the serialized proper field by accessing the
serious object and then find property actions. And let's make sure that
it has the correct names. Yeah. Okay. And for
the distance position, let's just search for the
distance position property. Now, we are going to create an override for
the uninspector GI. Basically, what we need to do is we need
to make sure that we update this realized object on the start of the method. And then at the
end of the method, we apply any modified
properties. Okay. Let's create a layout,
begin vertical, and this will be we put a box argument
inside the parenthesis, so it will create a nice
box layout and then close it with a G
layout vertical. Now we need to draw two
different property. The first one would be
the distance position. Let's just create
an editor I layout, and then the property
field method. Then we can pass the
as distance position. For the label, we can just type I content and pass the string inside the argument here
inside the parentheses. Let's call this
distance position. And then we are going to use the editor extensions
that we've created, and we are going to use
this row actions array, and this as for a serials
property actions array, which is our as
underscored actions. Let's just type that.
Then for the label, we can just call this actions. And then close it
with a semicolon. Now if we save if we save this editor script
and go back to unity, this will update
our interactable. Let's just select
our third NPC here. Now we can see that
our interactable has a very nice custom
inspector working, and we don't need to
create complex coding with the actions array anymore
because we already defined this in the
editor extension. So it will draw it
the same way like our previous actions array
on other classes. Okay.
32. 31 Entity State Concept: Okay. Now, I'm going to
explain first how we create some sort of a state for every object in this
point and click games. So basically, for any entities such as
NPCs or players or anything, we are going to represent
this with an EmpS one. For example, if we have an PC and we are going to build that PC with a
couple of CL in it. For example, the first one will be an empty game object and this antame object will have a as items component
and also intractables, and also the needed components such as collider for
clicking detection, and whenever we click it, it will ask for items. Then another antigam object will have a different actions, which is a message actions, and this will only be enabled
whenever we give the items, then the PC will say tanks. And another example,
if for example, if we deny the items, then the NPC will curse the player or we'll
say some bad things. All of this object here will be an empty game object and it will be a child
of this NPC one. Each of this object will have its own collider and
its own intractable. But the first ig object, we have a item actions. The second one will
have a message action, and the third one also
a message action. Of course, as any other object, NPC will have positions and rotation recite in the
transform component. We are going to save all
of this information, the position, the child
object into an entity data. This is a custom class
that will hold all of this data for the
position on a rotation, it will hold the vector
values and the coon values. For the object, it
will only hold the active or enabled status value, and this will be a
type of bullion. If it's enabled, then the activate status will be true and if it's
disabled, it will be false. Okay. And why do we hold the bullion data
or the enabled data, the game object enabled or not? Because when we
give, for example, if we go to the NPC and then the NPC as for the items and
then we give that items, the as items, then we are going to disable
the first game object, the first child game object, and then the second sorry, the third child game object. And we will leave the second
game object to be enabled. The next time we try
to talk with this NPC, the NPC will repeat
this tanks message. It will not reset. For example, if we leave
the scene and then we go back and then we
meet this NPC again, then the PC will still
say thanks because we are going to record the state
of the child game object. For another example, if
we go to the NPC and then the NPC asked for the items and then we refuse to
give the items, then the first game object and the second game object
will get disabled. Then the third one
will stay enabled. When we talk to the NPC, then the NPC will curse or
say bad things to the player. The same goes, if we leave the scene and then we
go back to that scene. Since we save this condition
into an tide data, then when we start the scene, we can load the data to the NPC and the NPC will
still say the same thing. There is basically a
safe entity state works. In the next video, we will try
to implement this feature.
33. 32 Entity System: Okay. So now we are going to create
the entity safe system or entity data system. And let's create a new folder
inside the script folder, and this will be the entita we are going to create
a custom class, and let's call this entity data. Okay. And for the folder, I'm going to rename
this to entity system. So it explains or describe the class inside
this folder better. So let's just rename
this to entity system. The next thing we need to
create a new interface. Interface is basically
like a class, we can inherit from
this interface without sacrificing the
mono behavior inheritance. It's a different inheritance, but it's not a class,
it's an interface, and you'll see later,
I'll explain it. Let's call this I savable
because by standard convention, worldwide convention in C sharp, whenever we declare
an interface, we use the letter in front
of the interface name. We will know that
this is an interface, and let's just open
this entity data first. We need to create a new
couple of public variable. The first one would
be the float, and this is the position
for holding position data. Let's just create
three different folds. The first one is x, y, and z. The next one will be
for the rotation. This will be the Q, Q, Q, Q x and Q, and then Q z. The other thing we need
to create is we need to create a dictionary. This will be a dictionary, and for the key, we are going to use an
integer and the integer will be the index of our
child ant gave object, and then the second one
would be a bullion, and this will be
the active status, whether it's enabled or not. Let's just call this child active status, and
let's declare this. For this class, we are going
to make sure that this is a public class and we want to mark this serilizable
so we can save this data and since we cannot serilize factor
tree and quaternion, we need to break those
value into this float here. That's why we create a
couple of float variable. Save this and now we need
to create a couple method. The first one is to
save the position. Let's just create a new public
fit and then we can call this set position and we can pass a factor
tree as a parameter. Since we want to
save the vector re values that we pass
here as a parameter. We can just type
the x flow here, and we want to store the
position x and the y, we want to store the
position for the z, we want to store the
position like this. The same goes with
our quaternion, create a new public fit and
this would be a rotation, and it will ask
for a quaternion. Let's just call this rotation. And now we can just pass the rotation y rotation
to the Q float. Then for the Q float, we can store the rotation dot x and the same goes with the Q, we can pass the rotation. And for the Q z, we
can pass the rotation. It works the same like the
position, but with position, we only have three components with rotation we have
four components, we want to also get position
and get rotation method. Let's just create a new public
for getting the position, we want to return a vector. Let's just type vector for the return type instead of void. Then we can call this position and we don't need any
parameter for this. We can just type
return, and then Set a new vector three
value and we can pass our x and our y, and z vector three
component values like this. For the get rotation, we need to create a
new public method, and let's just return the type quaternion here and
let's just type rotation. And we can return
a new quaternion. Here we can pass the x y z and the W value. Let's
just pass that. First would be Q x and the second one would be Q
and the third one would be, and the fourth one would
be QW Now if we save this, we have this custom class, and now we can just delete
the unused name space here. We have defined this. Now,
let's define the interface. We cannot use this yet because this is custom test and
we are going to use this object in the other
script. Go back to unit. I'm going to open the interface. For this interface, we want
to delete using namespace, and then we want to
delete the meno behavior also and also the method inside. We want to mark this
save as interface. For this interface, we
are going to create two method and the first
one would be safe, and the second one
would be load. We cannot we can only
declare the method name, but we cannot declare
what's inside of it. But other class
that derives from safe must implement
this two method. Let's rename this safe state, so it describe
better the function, the method, save this. Now go back to unity, we are going to create
a new C subscript. And let's call this
safe entity. Okay. And let's open this class here, and we want to
implement the savable. In order to implement
the interface, we can just type a comma
after the inheritance, keyword, the mono behavior, and then type the savable. Now it throws an error because we don't implement
interface members, so we can just try to implement interface and it will create
new implementation here. Save this. We want to create a new cerlize field and this
will be type of string, and this will be
the instance ID. And let's create a new
private entity data. Let's just call this entity
data also with case, and then we can just initiate
the entity data like this. We want to create some sort of a unique ID for
this instance ID. So what I'm going to do is I'm going to create a reset method. In this reset method, we want to fill our incense
ID with the game object name, and we want to join it with the game object, get incense ID. Why do we put this
inside the reset method? Because if we put it
on the reset method, when we add this
script to an object, it will generate
this automatically, and we don't want to
change this actually. Currently, I'm tag this
as serialized field, so we can see it
on the inspector, but later we're going to hide
this from the inspector. Okay. Let's save
this safe entity here and let's go back to unity. Now let's add this safe entity
component to the player. As you can see here,
once we add it, it generates automatic instance
ID to the safe entity. In order to make this
safe entity works, we need to modify our
data manager script. Let's just open our
data manager here and we are going to
create a safe data, first, a public class
for saving data. Let's just create a new
public class here below, and let's just call
this safe data. Then let's mark this
safe data serializable. We need to access
the system namespace and then type that serializable. Safe Inside the safe data, let's create a new
public dictionary, and for the key and value type, the key would be a string, and the string will
be the instance ID of the safe entity
component here. It will hold reference for each of the object with
this unique instance ID. Then for the value, it would be an entity data. With this entity data, we are going to
hold the position, the rotation, and also the child active or
inactive status. Let's just call
this entities data, and let's initialize this. Now that we have this safe data, we need to create a reference
under the ta manager. Let's just create a new private and this will be the safe data. Let's call this also safe
data with lower case as. Let's initialize this. Now we have this safe data. We are going to create
a couple method for saving entities data and also
for loading entities data. Let's create a public method. Let's call this safe entities. And the other method would
be void load entities. Now we have this two method. For the load entities, we should return an entity data. Let's just change the
void here to entity data. For the safe entities, we need to pass a ID and the entity data itself.
Let's just call this data. For the load entities, we need to find it
by accessing the ID. Let's just pass a string
ID for searching. K. We are going to check if our save data entity data
contains key of our ID. If it contains, then we
want you to overwrite that. Let's just access the save
data again, entity data, and then we can pass the string key inside
this square brackets, just pass the ID, and then one to overwrite this with the data that we are
passing through this method. Let's just set equal to
data and then run a return. And if it's not
contains then else, we want to save or we want to create a new
entry into this entity data. Let's just type and pass the ID and also the
data for the value. For returning the
entities or loading the entities, we can return. We can just check if the
save data and entity data contains key of the ID
that we are passing, then we want to
return that value. Let's just return We
can just copy the save that data and then pass the ID to the
square brackets here, so it will return
whatever data that we have with this key, and then else, we
can just return, no. Save this. Now we
can save and load entities from our safe
entity component. Okay. Now, let's just
do that for saving, we are going to modify the
save state part first. For saving basically,
we want to save the child status and then also the position
and the rotation of our game object that
holds this component. Let's save this
entity data and we can run the method set position, and for the set position, we can pass a vector tree. Let's just pass our
transform that position. And for the rotation, we can set the rotation. And then we can also pass
the transform rotation. Okay. And before saving and setting the
position and rotation. We want to clear
the entity data. So let's just type the
t data keyword and then let's initialize
a new entity data. And then we are going to look
through our child object. So let's just access the
transform the child count, and we want to save
the enabled status. So we can just access
the entity data. Okay. The child active status and
we can add a new entry, and then we can add
the as the key because the child count or the child
member will start from zero to it depends how many child
this game object have, and we can pass those
value via the i integer. Then for the Boulan status, we can access
transform and then get child and then pass
the index here, and then we need to
access the game object and get the active
hierarchy properties and this will return a bullion, and this defines whether a game object is active
in the scene or not. Let's just pass this value, and this will record
the active status. Once we've created all of this, we want to push our entity
data to our safe data. Let's just access the
taminger instance and we can use the safe
entities method, and we can pass our instance ID, which is this unique
ID that we have when we are adding this component
to the game object. Then we can pass the entity data that we have just
previously built here. This will save this data to the safe data object
on our data manager. For the load state here, First, we need to
load our entity data. We are going to get
the entity data by typing the entity
data variable itself, and then we want
to get this from our data manager instance. Then we can use
the load entities and this will ask
for a string ID. We can pass the instance
ID that we have here, and then we can check if it data that is Then we want
to return this method. Don't go further
and if it's not no, then we are going to access
the code below here. So if the does
contain some data, then we want to get its position by feeding to our
transform position and we can just access the
transform position and then we set to the D get position. For the rotation, we can do the same transform the rotation, and then we can get from
the entity, rotation. For the child object,
we are going to look through our child count. Let's just transform
the child count, we are going to access the child index of
the game object, we want to access the
game object because child will return the transform. Then we want to set act
based on our entity data. And the child active status, and we want to get the
integer as the key, and this will return the bullion status that
we saved before. Let's cose this with semicolon. We can just remove
the update method. We don't need this update
method on our entity save. To test this out, we
need to create a event a public event on our data
manager and public event, we are going to use this
in the final game also. Let's just create that
the event would be a public event and the type would be a
system that action. And we can call this on save, and we can create
a empty delegates. So It would be safe to run this event in
this data manager. Even though if there are no other component or other classes that are
subscribed to this event, we can still run it
for the other event, we need to create system action, and this will be the unload
and let's just also create an empty delegate for
it and save this. Now we have this two event. We can create some
sort of di method on how to save load, for now, I'm just going to create
a FoI update method and this method update will
have a input key action, and we want to use for example, for loading, we are going
to use the keyboard, L. For saving, we are going to use the keyboard character as on
the keyboards for saving. Now inside the input, get keyed on key code L, we can run the load. And for saving, we
can run on save. We have this created. Now, because we already
created the event, we need to go back
to the safe entity, and we need to subscribe both the safe state and
load state method in the safe entity class onto the unsafe and unload event that we defined in
the data manager. In order to do that, first
on the start method, I'm going to subscribe by
typing the data manager, that instance, and we can access the unsafe and whenever
unsafe is happening, we want to run the safe
state method like this. To subscribe a
method to an event, for example, the safe state, we need to search for the
event first and then we add a plus and then equal
sign and the method name. This will subscribe this
method name to this event. Whenever the data manager
run this event here, this method here
will get triggered. Let's just copy this line and
paste it as a second line. Then for the second line, we
are going to change this to unload and this will
be the load state. But we need to unsbscribe whenever this object
is destroyed. This is for initialization when this object
gets created in the. When it's destroyed, we can
access the built in method, which is called destroy. Once I close this,
it turns to blue. This is a built in method
that unity provides, and we can just copy
this line here. Then instead of adding
with a plus sign, we change the plus sign
to the negative sign, we unsubscribe this event. This method will unsbscribe
itself from this event. This will prevent a memorial
leaks or an error if this object gets
destroyed and somehow the data manager running unsafe. I will cause issues if
we don't unsubscribe. Now we have set things up. Let's go to unity. Okay. And let's add
the safe entity to the player game object and
to the PC game object here. So, let's just add
this safe entity, and save this now
let's test this out by running then we can
move to this area here. Now I want to save the state
of this two object here. I'm going to press S, and this
will run the unsafe event, even though we cannot
see anything, it's okay. Let's just test this
then now let's talk to this person and the
person should move. Once the person move,
let's press L and C, you can see that our
object gets loaded. But since the agent has a destination a
previous destination, it tries to move to that point. Now in order to fix that, we can access our
NPC move actions, and also the player which is on the script folder
and the player script, and we want to set a stopping status for the agent object or
the agent variable. Whenever it's not moving again here for the
NPC move animation, we can set the agent is
stop This will stop, even though we load a different position
using the entity eta. It will stay stop
and we can copy this and whenever we are
moving the NPC, then we want to set
this two false. We want to move the agent again. For the player script,
we want to also do that. For the player script, we want to set the agent is
stop when we arrive, so we can use this method
that check if arrived Here we can check if the
agent is stop is false. We can add an exclamation
mark in front. It's not stop,
currently is moving, and then we want to
check if arrived, then we want to stop the agent. Let's set the stop value to true we want to set this false again whenever
we start moving. We can just put it here. Before we are setting
the destination, we can set this theory
set this back to false. And this should do the trick. Let's go back to unity
and test this again. I'm going to save
this state now, and then I'm going to
talk to this NPC here, and I will move and I'm going
to move to this position. Then let's try to load this. Now when we load this, in
its position and we can again and it will move again
if we load the state again, it will load again. Okay.
34. 33 Trigger Interactable: Okay. Next, we are going to create
a trigger in component. And because so far
for interaction, we only have this interactable. So let's just create a new CH script on
our scripts folder. Let's call this trigger
in. And let's open this. Basically, the trigger
interact will be a component and it
will trigger an action whenever a certain object with a certain tag enters the trigger of that object
or exiting the trigger. We are going to delete the
start and the update method, and let's create a serial field, and this would be a
type of string let's call this selected tag. For the next series field, we are going to create
an array actions. Okay. And this would be
the enter action. Okay. The next one would
be the exit action. Let's just copy this line
here and paste this here, and this would be
the exit actions. Basically, what we are going
to do is we are going to create the fit on
trigger method. For this method, we are going to look through the interactions. Let's just create a four loops through the interactions length, since this is an array,
we are using length. And I've decided using array because we are going to set
the actions on the inspector. The difference between
array and list. With ray, have a fixed
member on run time. But with list, we can add and
delete member on run time. Array should be faster
if we don't need the ad and delete
member feature. Let's just look through
the interactions based on the next of i
and then run the method. And we can copy all of
this method here and paste this below and change
this to trigger exit. For the exit action,
for the untrigger exit, we can just copy
the exit actions, and then paste this as the array that we
are looking through. Another thing we need to make
sure is we need to check if the tag is the selected tag. So we can just check the other objects that are being passed by
the untrigger enter method and search for it tags and we can get
from the string here, select the tag, and if it's not this tag then we want
to return the method, so it will not run this. Let's just copy this and let's paste this and the same
goes with the trigger it. If the other game object
doesn't have the selected tech, then we won't run the action. Let's save this
trigger interact, and now create an object or we can just modify
any object that we have. Let's just modify
this cube here, and we can set the
trigger B enable, and we can add the
trigger interact method. And we can type the tag
that we want to detect. In this case, which is player, and we can add a
message action for testing and add message
and we can just type player hit or player triggers the que direct
this to the inter action, so it will gets added to the
action and save the scene. Okay. Now if we walk through
this point here, the player will avoid because it creates a hole in the F mesh. Let's just duplicate
this cube game object. And then direct
this to be outside of the environment and put it somewhere where we doesn't have hole in the scene here. For the cube one, we can
just we can set this back to not trigger and remove the trigger action and
remove the message action. Let's try this again. If we pass through here, it so Let's just check what
is actually happening. Apparently, I forgot to put
a rigid body on the player because the player
has a collider and this cube also
have a collider. But in order the void on
trigger enter or void trigger exit method to be recognized or to be
executed by unity, one of the objects that are cliding needs to have a
rigid body component. I'm going to add the rigid co body component to our player, and we are going to disable the use gravity and enable
the askinematic option. It will not act like
a physics object, but it still have a
rigid body component attached to it to
detect cohesion. Now if we try this,
it should work. Let's go to this point here. When the player passes
through this box, it will trigger this message. So, this is for the
trigger action, and we are going to
create a custom editor to make setting up things with
the trigger interrat easier.
35. 34 Trigger Interact Editor: Okay. Now, let's create a custom inspector for the
trigger interact component. So let's go to the editor folder and let's create a
new CHAR script, and let's call this
trigger interact editor. Okay. Let's open this script, and we are going to import
the using Unity editor. Also change the mono
behavior to editor, always when we are
creating an editor script and forget the semicolon here, and add the attribute
custom editor and target the script or the class inside the type of statement here and just type the class that
we want you to inspect, which is trigger interact. And then we want to create a couple of
serialized property. Let's just type the class
serialized property, this would be the inter actions, and I'm going to name this as underscore inter axions
and for the exit action, it should be as underscore
exit actions for the tag we want to name this
underscore selected tag. And now we can just delete
the start and update method and just create
the unenable first to initialize the
serialized property. Let's just type the
serials property, and then using the
serialized object, we are going to use the
fine property method and we need to pass
the field names, which is enter actions with
lowercase and uppercase A. Then I'm going to
copy this line here, paste this twice, and
for the second one, I'm going to change
this to exit actions. Also I'm going to search
for the exit actions. Okay. And for the third one, it
should be selected tag, and let's search for
the selected tag field. Okay. Now let's create an override
for the un inspector GY, and we can just delete
the base method here. On the beginning of this method, we want to update the realized object if there are any changes, and at the end, we want to apply any
modified properties if there are any changes also. We want to draw actions array. Let's just use the
editor extensions and use the draw actions array. Then we can pass
the actions array, which is the enter actions. Okay. And for the label, we can just type this trigger enter actions. And we can copy this method, this line and change the second
one to the exit actions, and let's just rename this
to trigger it actions. Next, we are going to
create a custom tag field, and this will be a
drop down and it will list all of the tag
that we have on our project. Instead of typing
like previous videos, we can just use this editor I layout and use the
tech field method. A for a string, We can
pass this is property, but this is not a string,
it is ers property, so we can just pass this inside here and then get
the string value. We can add a label before. I'm going to add coma here
and as the first parameter, I'm going to create a label. Let's call this trigger tag. But we need to put this
inside a string value. Any changes that we
change in the tag field, we'll get assigned to
this string variable. Let's just copy this as underscored selected string
value and put in front of the editor L and
then add an equal sign. We will assign any
changes in the tag field to this selected
tag string value. Let's this semicolon. Then now let's save this
trigger interact editor. If we go back to
unity and we select the cube object that has the
trigger intra component, we will see that it has a
custom actions array editor, and then it has a trigger tag. We can browse the tag that
we have in our project, and this is quite
neat trick instead of typing a tag because
with typing text, it can cause an error
and this will list, so it's easier. Okay.
36. 35 Camera Manager: Now we are going to create a camera manager and this
camera manager will handle camera changes or camera
switches whenever we approach a certain area
and we want to switch camera, this camera manager
will handle that. Let's go to the
script folder and the manager folder and
create sharp script, and let's call this
camera manager. Now, once it's created, let's open this into So Studio. First, we want to create a static reference
since this is a manager. I'm going to create
a new public static and the type would
be our own class. I'm going to call
this instance with capital because
this is a property. I'm going to set this to a
public but a private set. The next thing we need to
declare is we need to create a event a public event, and this would be
a type of action. But we want to pass a
argument to this event. We need to define a class or a type of here inside
the angle bracket. I'm going to pass the camera. I'm going to call this
on camera switch. I'm going to assign
a empty delegate for now. To this event. So we don't have to
check if this event has a member or not or has
any subscriber or not. We can just simply run it
whenever we need to run this event or
trigger this event. We also need to create a list
public but a private list, and this would be
a type of camera. We can call this all
cameras on scene, and let's initialize this and also a public properties
for holding the arn camera. A is called this arnmera
this will be a type of type, we'll have a public getter
and a private setter. Now we are going to
create a k method. In this method, we are
going to initialize the instance properties
to this script instance. This type instance equals this. On start, We are going to set our on camera to the
currently active camera. In order to get
the active camera, we can just access
the camera class and then type dot main. This will return the first enabled camera tag
with main camera. The camera object has to
have the tag of main camera. We will also need to grab
all the camera on scene. But the problem is if we try to use the fine
objects of type, this will return a list
of all loaded objects. The object needs to be
activated and if we use the fine objects of
type, this is deprecated. Let's create our
custom extensions for finding object
of certain type, whether it's active or not. Let's go to unity and open
our extensions class. Now we have this
extension class open. We are going to create
a new public static and this will return a
list type of generic T, so we can use any other type or classes to use with this
method and we can create fine objects of
type and pass the t degenerates again and
close this with parent to Let's create a new type of t, and this will be the result
and initialize this. Now we need to import using Unity engine scene management because we are going to
access our current scene. Also, we need to
import the system link because we are going to use a two list method and
this is provided by link. Now we want to
search in our scene, all of the object that has camera component attached to it. We can access the scene manager. And then get active scene. Then we can get
root game object, and we'll return all the root
game objects in our scene, and this will also return an
array type of game object. Then we can convert this to list Once we convert
this to list, we can look through each
of the member by using the four H method and then pass the action
inside this four method. Here, we are going to
use a Lambda expression. Basically, we can use
any name, for example, g or x or H, and this will be the
representation of our game object. I'm going to use a G letter, and then I'm going to add an equal sign and then
write arrow sign. Then here, We are going to
access our result list. Then we want to add
range because there are many multiple objects that have camera components
attached in the scene, we want to make sure that for each root game
object, which is G, we want to search, get component in components
in children with plural here. And search the T
degenerate type, and we can close this line with Smc and then once we
already search and we return every object that has a type T component
attached to it. Later, we can change the t with certain classes
that we need to find. Now, let's just return
our list result. Return result. Okay. If we go back to
the camera manager, now we can use the
editor extensions to search for all of the camera
game object in the scene, and we want to store it
into the all cameras on. Let's just type all cameras
on scene and then equal editor soil editor extension, extensions class and then we want to access the
fine object types of all. Then we want to pass
the camera class and this will be the class that we get searched by defined object type
of all and safe this. For example, we
can test this out. Let's create a new empty
game object in unity. I'm going to put this below
the spawn manager here, so we can see it together
all of the manager and change this to
camera manager. Okay. And then we can add the camera manager class
to this game object here. Let's just direct
this and let's open the debug and we can see
whenever we run this, we can see that we fill
this cameras scene list. So for example, if we
duplicate our camera, two times, and then we activated the second
and the third camera. If we go to the camera manager,
and let's save the scene, If we press play, Okay you'll see that we will populate the all
cameras on scene. Even though this
object is deactivated. We can easily search object, whether it's
activated or not with this method and it
will grab this camera. We need to activate only one camera because
we want to make sure that on the
start of that scene, we are viewing the scene through or that
first active camera. Now we see that the
extension method is working. We can continue to create
our camera manager. Now we are going to create
a new public method. And this will return void and we can call
this switch camera, and it will ask for a
camera to be switched, and we can just call this
camera parameter cam Let's look through our
cameras on scene list here, and we are going to disable
all of the camera Let's just type the camera on
scene and index of, and then we can just set act or access the game object and
then set active to false. This will disable
all of the camera. Once we disable the camera, we are going to activate the camera that we pass
through the parameter. This way, we will make
sure that we will switch to this selected
camera or this pass camera to be
the only camera that are going to be
activated at that moment. Let's just access
the game object, and then run the set active
method and set this to true. Once we change the camera, we want to also change
the current camera To the past camera
here, the came here. We will change the
current camera to the new camera that we
passed as a parameter. Then we want to run the event that we've
created on camera switch, and we want to pass the new
camera to this event here. You may ask why do we have
to create this event? If you remember, we can open our player script class here. Let's open the player script. Okay. Let's go back to
the camera manager. Here, whenever we
switch the camera, we are triggering the
on camera switch event, and if there are any
subscriber to this event, it will receive this event and it will run a
certain method. In player script, we have
this main camera variable, and it's store the main camera
that we have in our scene. We need this camera variable to detect the click position from the screen to
the world position. When we change the camera, we need to change this also. That's why we are
creating this event. The player script can
subscribe to that event. Okay. Let's create a new method, and let's call this
camera switch. This will know when
camera is switched, and we need to pass the camera here whenever these
functions get executed, we want to change
our main camera to the cam that we pass here. Basically, we are not
passing any camera, actually, but on start, When we subscribe this
event here, not camera, camera manager, instance, and then we access
the on camera switch event. When we subscribe the method, which is the camera switch
to this event here, as we can see here, the event is passing a
camera as a parameter. So when we trigger the event, we are passing this camera,
this new camera here, and the listener, which
is the player script, we receive that camera and it will get passed
to the argument here. So we will know when the
camera is switched and we will also know the
new camera and we can assign this new camera
to the main camera. So when the main
camera is changed, then it will also affect our
click on click method here. So click will register
from the correct camera. Okay. So now, I think
that should do, but we need to make
sure that on destroy, we remove the subscription. So there is no any error,
any unwanted error. So we can just change
the plus sign to a negative sign and save this. Okay, so. Yeah, that will be
all for the camera manager. As of this moment, we
cannot test this yet. Because we need to create
a camera switch action. We are going to do that
in the next video.
37. 36 Camera Switch Action: Okay. So we already created the camera manager in
the previous video, and now let's create the
camera switch action. Let's head to the scripts
folder and then actions folder and then create
a new CSA script, and we are going to call
this switch camera action. Let's open the class here. First, we are going to change the inheritance from
mono behavior to action, and it will ask to implement this override abstract classes. Let's just implement that we can just remove the
start and update method. Let's create a serialized field, this would be a type of camera. Then we can just call
this camera to switch. And we can just delete this system not implemented
action exception. Now we can access the
camera manager instance, and then we can run the switch camera method
and we are going to pass this camera to switch as an argument to the
switch camera method and save this. Okay. So yeah, This is the script. It's very short and save this. Let's go back to unity
and test this out. In order to do this, we are going to use
this box collider, and let's delete or just hide the mess
render for this moment. And then we are going to
delete the message action. Let's change the collider
size to be t bigger. And let's move to
around here position. We are going to delete
the third camera here. But for the second camera, we want to set an angle,
something like this. Let's just press
the second camera and then hold control Shift F, and it will position that
came to this position. And if we select the
third cube here, the cube with the
trigger in component, let's add the switch
camera action, and we need two action because we want to switch between the first and
the second camera. Let's just add a trigger axons and a trigger exit actions. Make sure the trigger tech is clear because we want
to make sure that the trigger interact only correspond when a player
enters the collidor. For the switch camera action, direct the first main camera, and we want to direct
the second main camera to the second action. When entering this
box of trigger, we want to set the second camera to be the current active camera. Direct this switch camera
action to the first slot, the trigger ter actions, and dirt switch camera
action to the trigger exit. Whenever we're
exceeding this trigger, we want to switch back to
the default, this one here. Now let's save this scene
and now test this out. We can move here and if
we go to this area here, it will switch the camera. As you can see, our mouse
click is working because we are switching the
camera and also we switch the camera reference
inside the player script. Now we have a very nice
switching camera action.
38. 37 Inventory UI: Okay. Now we are going to create an
inventory UI system to show the item that we
have in our inventory. Let's go to the D here, not the game, the D.
Let's enable this. And we are going to start
to create the UI system for the inventory UI. Let's just focus to
the base canvas. Let's create a new empty and
this will be the inventory. And I'm going to stretch this
to the size of the canvas, so I'm going to hold L on the anchor preset
and then choose the bottom right preset and
it will fill out our canvas. Then I'm going to create
a new child image. This would be the
inventory UI panel. Let's make this around
900 by 600 Okay. And I'm going to
change the color to black and make this also transparent around 0.5
on the alpha value. Okay. And then we want to
create a new empty child, and this will be
the item layout. Let's stretch this to
the size of its parent, which is the inventor UI panel. Then I'm going to add a margin
on each of the side here. Let's set this to
round 40 on left top, right, and the tom margin, it will have margin. Let's put a grid layout group. Save the scene. Let's
create a child object, which is a button, and let's duplicate this button. We can see how the button
is going to be lay out under this item lay object. Now let's set up
the item layout. We are going to set spacing, both to be set to
25 on x and y x. If we add so many button, it will look through this item lay out and we can
select the item lay out, and let's set this
to start corner left axis horizontal,
the child alignment. Let's set this to middle center. It looks better. It's centers just delete
all of the button here and we can keep item. But I'm going to change the item ladle center to upper center, so it will stay on the top
side here, but centered. Okay. Now we are going to set up this button as a item UI. Let's just delete
the text object. For the image, let's
just set this to none, so it's wide let's rename
the button to item UI. I'm going to create a
text mesh pro object as a child and let's just set this here on the buttom anchor. And change its size
to a very small one, perhaps a 150 pixels by 30. Let's just change the text
to something like x01, so we can see for
multiple object, we can show how many
items that we have with this text and change its
color to black at the moment, and just set this
size to be enabled. We will see it like
this. I think I'm going to change the item
you cell size to round 150. We have a bigger item UI. Okay. Now we have set things
up here in the scene. We are going to
create a new script for handle this inventory UI. Let's create a new folder under the scripts folder and I'm going to call this inventory UI. The first thing we are
going to create is the inventory item UI. Okay. Let's open this and we are going to declare
a couple of variables. I'm going to paste the variable that I've already declared. Basically, we need to import
using the unity engine UI. Here, I'm going to
table this line first. Basically, we are
creating type of item to hold the item
data for this button. Then also, we are creating
a variable for holding the button component because each of the item UI have
a button component, and this will allow us to press on that item and to
see the description. This is for showing
the amount text and the type would
be the text match pro G Later, we are
going to enable this. Basically, this is
the parent system UI that will insaniate all of
this item UI layout and handle the connection
between updating or redrawing the item
whenever there are changes in our item inventory.
This will handle that. This will the item image
and the item image is the image component that
lies on this button also. This will change according which item do we have currently
in this item variable. Let's delete the It start
and update method and we are going to create a
new public void and this will be called in it. And for initialization, we are
going to passing the item, and let's just call this item. Later, we are going to pass
the inventory system UI, but we haven't or maybe we
can just create the script. Let's just go back
to unity and let's create new CHR script, and let's call this
inventory system I Let's open this also. Now if we remove the comment, this should be
available because we already created a class
with this name here, and we are going
to pass that also. To this argument and pass this. We can just call this
also inventory system. Now, basically, we want
to set up the button or the UI appearance from the information that
we have on our item. First, let's just fill this item variable with the
item that is being passed. We want to also fill the inventory system
that we have here. Let's just type, this
inventory system. This will revert to the inventory system
that we declared in our class because we use the same name as an
argument or as a parameter. We can just fill this with the inventory system
from our parameter here. We will also need to
initialize our button. Let's just type button, and then fill it with get
component and the type will be button and it will search the button on the game
object that hold the script. Also for the amount tax, we want to search
for the text object. But since the text
object is a child, then we are going to use the
get component in children, and then just type
class of the text mesh, which is the GI and then now we are
going to set things up. We are going to set
the item image sprite to be our item sprite. In this item class
here or variable, we already passed the item
that we are initializing, so we can get the
items right away. We want to enable or disable this text object
depend on this item, allow multiple bullion setup. For example, we can just
type game object set, and then sat active
for a bully and value. We can just pass this
item, allow multiple. If we don't allow
multiple on that item, then this value will be false, then this object will
get the activated. But if this item that
allow multiple is true, and this will be enabled. Then we can also set
the amount tax value to be x space out
space will be fine. Then we can grab
this item amount, and this will show the text how many items
that we have currently. This will be all for the inventory item
UI at this moment, and we are going to
modify this later. Now let's go to the
inventory system UI. Now let's create a variable. The first one will be
a type of transform, and this is for the item layout, and let's call this items parent because all of the item UI will be parented to the subject. The second one would
be the prefabs of our inventory item UI. Let's just type the class
here, inventory item UI. Let's just call this item
UI prefs The third one will be our inventory object itself and it is a
class of inventory. Let's just call this
player inventory, and we will direct the scriptable object
to the slot here. The last one is a private, and this will be a type
of inventory item UI. All of the item UI that we
instantiated during runtime. We are going to put it inside this list here to
keep track, things. Let's call this item UI
collection and initialize this. Let's delete the update method. On start, we want to
disable this object right away because we will put the script on the
inventory panel. Let's just type game
object seductive to false. And let's create a new
initialized method in it. And in the init method, we want you to pass
the list item, and let's just call these items. And we want to look through
the items that we pass here. Let's just type items.com. For every iteration, we want to create the item UI prefabs. We want to instantiate it. To make that easier,
let's create a new method and let's
call this at item UI. We want to pass the
item data item class and let's just call this item. Basically, we want to create a temporary object
inventory item UI, and let's just call this item. Let's instantiate a
new item UI prep here. Let's just type item UI
for the transform parent, we are going to pass the
item sparent This way, we will incentiate
the item UI prefs, and we will keep the inventory item UI component
reference to make it easier. Once we create this, we
can access the script, the inventory item UI, and then we can run
the init method, the public it method that
we've declared here. Let's pass the first
parameter, which is an item. For the second one, we want
to pass this class here, this script here, the
inventory system UI. Let's just type this Okay. And then we want to add created object to our
item UI collection. Let's just access the item,
UI collection and then type add method and we can insert the inventor UI item
that we have just created, which is the ten item. Now that we created this, we can run the method
and then pass the items. And since this is
a list of items, we can just type items index of. It will look through our item. Okay. Now, we already created
this init function method. We can run this by typing the
it and then we need to pass the list of items inside
our player inventory. But we haven't exposed that yet. Let's just open our inventory
script, go to unity. Then under the
descriptable object, let's open the
inventory class here. Now, as you can see, we have
this list item is inventory, but we don't have
a public getter. Let's just create a new one. Public geter this will
be a type list of item. Let's call this inventory
with capital I, and then we can just
set together and let's just return the inventory
inside this property. We cannot use this keyword because it's the
same with our class. Let's call this inventory or we can just call this get
inventory and save this. Let's go back to the system UI. Now we can initialize
this by getting the list of items that we have inside
our player inventory. Now it will look
through the list of items that we have in
the player inventory, and it will create new item
UI based on those items. Now we need to create an event inside our inventory
scriptable object and this will be useful
if there are any changes inside the inventory and script can subscribe
to that event. Let's just create a new public Event, and this will
be type action. Action is for
encapsulating a method. And list of item would be the parameter of this
delegate encapsulate. With this, we can subscribe
a method to this event. Whenever we invoke this event, we will need to pass a list
of item as the parameter, and all of the event
subscriber will get triggered and receive the list of item passed by this delegate. Let's call this on item change. And we are going
to initialize this with an empty delegate. So let's just type delegate. Now whenever we add item, let's just run this on item
change on item change and pass the inventory here that we have pass
this and close this. I'm going to copy this
line after we copy this, we want to also put it inside
the modified item amount. Let's just put it here
before we are returning from the four loop and then
put it also here. Whenever we add the item,
after adding the item, we want to on item change also. After we made changes
to the inventory class, let's go back to the
inventory system UI, and we are going to
create a redraw method, and let's just call
this void redraw. We need to pass a list
of items as an argument, and let's just call these items. We want to subscribe the
redraw method to the on item change event that we
have on the inventory class. On start, let's just do that. Let's just access the
player inventory, and we will have this on
item change and we can add a plus and then equal sign and then type the method
here, redraw. But without the parenthes we need to add a
void on destroy. Whenever we destroy this object, we want to remove
that subscription. Let's just copy this line and change the plus to
a negative sign. Okay. Now we need to update our items
whenever there are changes. In order to do that,
we want to look through the items UI collection, and this will be all of the
game object that we have inside the inventory UI
panel, the child button. And we want to look
through its count, and we want to delete
all of those objects. Let's just run destroy
item UI collection index of I and we want to
access the game object. We want to destroy all
of the game object of each inventory
entries in the UI. After that, we want to clear
the item UI collection. Let's just clear the list
if there are any traces. Then we want to
initialize this again. We can just run in it here, and then pass the items. It will I will incentiate a new list of items
and save this. We need to test this out, but before we are going
to test this, let's just create a
new public method and this will be
called show inventory. Let's just enable this
game object here. Let's just copy this
line, paste this here. Let's just invert
the active status. Just type the game object act and hierarchy and then
add a exclamation mark. If it's true, then we are going to set this to false
and if it's fall, then we are going to
set this back to true. It will create a togal effect with this method and save this. Let's go to unity and for
the item UI object here. Let's under the
inventory UI folder, the inventory item UI script. I'm going to direct this and dg the image component to
the item image slot here. Let's set this as a pet's
go to the pref folder and let's create folder, which is the inventory UI. And then drag this
item UI. Okay. We can just delete this
button here and this one. For the inventor UI panel, not the inventory UI
anti game object, but the inventory UI panel. We want to drag the
inventory system UI script. We need to set up a
couple of things. Let's just set the
item lay out first. For the item UI pre we are
going to grab from the prefabs that we just saved before as the prefabs for
the player inventory, let's just browse and choose the inventory
that we created. The scriptal object is
residing in the data folder. So let's save the scene and
let's just create a button, a temporary button
to show inventory. I'm going to set the size
to be around 1005075. I'm going to align this
button to the top right. And we can add a margin here, negative 25 on the x and the y. Let's just change the ex to inventory and enable
the best fit option, so we can see it and save this. So now we have set this. We need to create an
item in the scene here. Let's just go to the scene and let's just copy
this cube that one, the green one, copy
this cube here, and let's set this as an item. Okay. Instead of using
the change scene action, just remove this and
let's just item action, and browse the item database, and this will give
us key, for example. This will give us key. Save this and let's go to the data folder
and check our inventory. We don't have any items,
so we can test this now. So if we press play, sorry, I haven't set up the
inventory button yet. So let's go back to
the base canvas. And for this button,
let's just create a new click event and direct the inventory UI
panel as the object. And we can run the script the show inventory method under the inventory system
UI and show inventory. Save this now if we press play, and we press the inventory
button, it will show nothing. It will show the
inventory panel, but we don't have a think, and if we press this
again, it will hide it. Now if we press this,
it gives an error. Let check. Yeah, I
forgot to insert action. Let's just direct
the item action and save this and
test this again. So now we don't
have any inventory, and if we press this,
It will add key. Currently, we are using check mark for the
icon of the key. Now, if we expand this item
layout, as you can see, it has a child object, and this child object
is actually a key. If we stop and play this again, you will see that we
don't have any child on the item layout. Oh, sorry. It does have because the item is get safe to the inventory. If we clear out if we delete
the item from our inventory. If we clear out the inventory, if we press play, you'll see that it doesn't
have a child object. For setting up item, we can just set it like this
whenever we pick this up, we can show also
a message. Okay. We show a message.
You pick a key, and we can direct this message action to
the second action. And for the third action, we are going to
use the activate. Activate action and basically, we want to disable ourselves. Let's just direct the
game object and make sure this is disabled
for the act action, let's direct this as an action
here as the third action, the item action message
action and activate action. If we save this now
and we press play, Now we can see our item layout doesn't have child and
if we pick this up, we'll show the
message and it will create item child object, and if we show the inventory, we will have this
item picked up. This will be for our inventory UI system
and we are going to create a description UI to show a description of the item that
we have in our inventory.
39. 38 Inventory UI Description: Now we are going to create the show description
in the UI inventory. Let's just re enable
the two D total here and go to our inventory UI, focus on that region. Let's create a new child
object, a child object, but much quicker if we
create an image and set the color also with
alpha value of 0.5. And we want to change its
anchor or not anchor. It's pivot by holding the ship button inside the anchor resets and
choose the first one here, and it will set our anchor
to this position here, and we want to set the
anchor back to center area. For this image,
let's just change this let's just call
this item description. Let's set its size to
be around 400 by 200. Then let's create a
new child object, which is a text mesh pro and set the test mesh pro
to fit this window here. I'm going to set the auto size, but I'm going to set the
maximum size to be 32. It will have sizing options 18-32 depending on
the resolution. And add margin to the
text object here. 2025 on each of the margin. 25 is too big, I think, 15 would be better. Now we have this setup. Let's go to the scripts folder, the inventor UI folder
and then create a new invent description UI. And let's open this. We only need one
variables in this class. Let's just define that and this will be a
serialized field, and this is a text
me PG this will be the text description text. We are going to delete
the update method. Let's create a new
height method, which is called height panel, and this is for hiding
the description. Let's just set this game object, set active to false on start, we want to hide the panel. Let's just run that method. We want to create
a public method for showing description. Let's just call this
show item description. We need to pass two
argument two parameters. Let's just type string, and
this will be the description, and I'm going to make
it shorter disc. For the second parameter, it will be type factor
and this is the position. Okay. And now inside this
show item description, we want to make sure that we
are activating the panel. So let's just set
the acts through. We want to set the
description text to the description that we are passing through this parameter. And then we want to
set the position by getting the
component transformed. Transform and then grab the anchored position and then pass the position
that we are passing here. Now we have created this script. Let's modify the previous
inventory UI script. I'm going to open the item UI and also the
inventory system UI. Now we need to
create a reference to the inventory description UI. Let's create a new
serial IS field. And this would be the
inventory description UI. Let's just call this
inventory desk. Make it shorter. Let's create a public getter for
this field because we are going to access this
from another script. Type inventory
description UI and with capital desk,
the same name. I'm going to set only a
getter and it will return the inventory desk
with lower case. Now on the item class here. We are going to
create a new method. We are going to call
this method tap whenever we tap this
inventory item UI, we want to access the
inventory system, this parent, and we get this
value from initializing. Let's just call this
inventory system. And then we can access the
inventory description UI, which is the inventory disc that we've created a
public error for it. Then we can run the show
item description method, this ask for a description
and a position. For the description,
we can just pass this item description
item description. Then for the second argument, we will need to get our
rec transform component of this item UI here, and then grab the
anchor position. And save this. Now we
have add this tap method. We can just pass this to our
button. On click Listener. Let's just type the button.
On click Add Listener, and this would be
the tab method. Save this and let's go back to unity and let's set up
this item description object. Let's add the inventory
descriptions UI and direct the text
mesh pro child object to the description
text field here. Under the inventory UI panel, we are going to direct this
item description object, who will be the inventory
description slot here, and save this. Okay. Now let's check our data and let's empty if there are any,
it's already empty. I'm going to switch
back to three view, and I'm going to focus
to one of the character. Now save this and let's run. Okay. If we press inventory,
we don't have item, and if I try to
pick the key here, it will show us that
we pick the key. And if we open the inventory, we have this checkmark and
if we press this button, it will show a description, but the layout is all wrong. So let's just fix this Okay. As you can see that
we have our pivot. It's on the top right here, but our anchor is on the middle. We need to change the
anchor to top left. For the pivot, we
want to add spaces. Let's move our y value
here to a bigger than one, about 1.2 or 1.1. 1.2 will be okay. And we can set a negative
value to the x value negative 0.2 or 0.20 0.050 0.1. I think 0.1 should
do and save this, and let's try this again. Since we already have the
key in the inventory because a descriptle object
does not reset its value after we
stop the run time. Let's just open the
inventory right away and see if we
click this key here, I show a description, and when we close this, it'll still showing
the description, so we want to fix that. Let's go to our
inventory system UI. Here, whenever we
showing the inventory, we want to make
sure if currently our game object is active
is active in hierarchy, is active in hierarchy. This means that we
want to disable this because if
it's still active, then this will deactivate this. Whenever it's active, we want to set the inventory description, game object, set active
to also falls. Like this. Now, whenever we run this and then we try to show the
description of an item. And then if we hide this again and then we
show the inventory gin, it will automatically hide
the previous description that we showing before. Okay.
40. 39 Save System Extension: Now we are going to
create a static class and the static class we're functioning for
saving the system. Let's create a new subfolder
inside the scripts folder, and let's just call
this safe system. Then let's go inside
to this folder, and let's create
a new CS script, and let's call this
safe system also. Now, let's open the safe system here in the fisico studio, we are going to modify
a couple of things. First, we are you need to
use the system run time, the serialization and
then the formatters and then the less one binary. And we want to also using
the system that IO, and this will allow us to
create a binary format, class object, and this will also allow us to create
a file string class. Now we need to change this safe system class type
to a static class. Let's just add a static keyword in front of the class keyword, and we can safely delete
the mono behavior because we won't attach
this script anywhere, and then we can just delete
this fit update and start. Now we are going to create a new public static
method static void and this will be the safe data. Let's just call this safe and we want to pass a generic class, so we can save different classes according to our safe
data classes later, let's just pass the safe
data into the parameter. Let's just type the type
first which is t because it's a generic and then let's just call the
variable safe data. And save this. Now we want you to create a
new binary formor. Let's create a new
temporary field and type this binary for meter, and we can just call this binary formor and then we can initialize this by typing new binary formatter with
sets of parentheses. Then we want to also
create a new file stream. For the file stream, we
can just call this file, would be sufficient
to initialize this, we can just type new file stream and then it ask for a parameter, which is the first one is
the path that we want to save the data and we can
just use the application. A persistent data pass. Basically, this
persistent data pass is the pass to a persistent
data directory. This is the data directory that is persistent
on the device. So for example, if
it's on the PC, usually it's on the
safe the install folder and also the same goes
with Android and IOs then we can concatinate with
a string and we want to add a forward slash
or forward slash, and then type the safe data that we want to call
or the file name. We can just call the safe and the format can be
anything or anything. Then for the second parameter, we want to type the
file mode to create. So we want to create a new file. And we want to create
this save data file in the precisent data path. Now we have created
this two object. We can just use the
binary formatter, to serialize and it has a
serialized method function, and I asked for a stream and
we can pass the file here, since this is file stream, pass the file and it will
save any object that we pass in the second parameter to the file stream in
the first parameter. For the second object, we need to save the
safe data here. Basically, what this function
is doing is it's create a new binary for met to serialize any data
that we pass here, and we can pass
any type of object because we are using
generic type here. When we pass this data here, and we create a new file here, the binary form will serialize after we
created this file, we serialize any data that we pass here as a
safe data and then it will be written as a binary to this file
that we have created, which is the safe data that we have in our
persistent data path. And since it's a binary, it's quite hard to modify because when a user
try to open this data, it's not really readable. But of course, for
any dedicated person that want to hack this
data, it's possible. It's just not as easy as
the player preference, the one that Unity
provides for saving data. Whenever we successfully
save the data here, we want to type the file and
we want to run the can read, but the close method. This is basically we'll close
the file from the memory. And then we want to type a debug log and call
this safe success. We can also make sure
that this debug log only runs on editor by typing
a unity underscore editor, and then we can type
with a sharp sign. We will make sure
that this sign will only run in the
editor and once it builds on the device or on the final build,
it will not run. Because debugging a string
or logging a string can also cause hit
in performances, it's better to disable any kind of logging
in the final bill. Save this. The next method we are going to create
is the load method. Let's just type
another public static. But this time we are going
to return the type of t, and let's just call this load. T here. Let's just close
this with a set of parent C. We want to check if we
have the safe data or not. Let's just create a new method
also and this will return a bull and check for safe data. Let's just return and then we can use the file
class and it has a method. It has a exist method, this it's wrong, I think. Let's just retype this again, is this will ask
for a string path, so we can just copy
this string path here. And it will return the file is existed and it will return false if the file
is not existed. So we can use this
method and later, we are going to also use this
method on another script. Now we can check check
for safe return true, then we want to load the data. We will need to create
a new binary formatter also temporary binary formatter, and then initialize this. Okay. And for the file stream,
we want to load this. Let's just create
a new file stream, and we can also call this file. Then we can initialize
the file stream, and then we can get the
path that we want to load. Okay. Also, we want
to load the file, so we can just use
the file mode here. We can just type file mode
and then choose open. It will open the file. We'll open this file to
the file stream here, and once it opens, we can make sure that we
create a new generic type, let's just call this
loaded data or load data. And we can use the
binary formatter and derze the file stream
that we have here. Let's just type file, this will rilize this file
into the object that we want. But since method returns
type and object, we can just typecasting this object that has been returned by the erz
method to a generic type. Let's just add a set soparnsis in front of the
binary formatter. Word and then insert
the t keyword. It will cast this object to a generic type and
then it will get assigned to this temporary
lowa variable fields. We want to also close the file. Let's just type file
and then close. After that, we want to, we want to return the load data. Let's return load data before
returning the load data, I want to also do the bug. Let's just copy this line
here and paste this here. And then change the
safe to load success. We will know in the editor if it's successfully
loaded our data. But here, as you can
see it has an error because we only return
the load data inside the statement and
we will need to also return something
outside the statement. Let's just return and we cannot return no since the
type is generic, so it will cause an error. The right Q word that we need
to use is the default and then we can put the T type inside a
sets of parent C here. And we will need to add a
semicolon over here. I forgot. That's why it throws an error at the bracket here and save this. One thing we can do is we can
create an L statement here. And then we can also
copy this line here and make sure that L statement only gets executed
in the editor, and we can just safe
file not found. We will know if there
are no safe file and save this script. Don't forget at a
semicolon at the end of this check for safe code
here because I forget. Let's just save this again. Yeah, I think that will be
all. We can just delete this unused namespace
here, library or API. Then just save this and right now we can't test
this script here, but in the next video,
we are going to create a method to test the
script out. Okay.
41. 40 Save Features Setup: Now we have created the
save system static class to help us save data. Let's create a
working safe system. Let's go to the scripts
folder and under the manager. We are going to
open the datanaga open the data manager class. Here, we already have a safe data that we
mark as serialzable, and we need to add a new fields entry and the first one would be a string and this will be
the current scene. The next one would be a
public and it will be a list, but it will be a top of integer. This will be our items
ID or inventory ID, or we can just call
this inventory items ID because we are going to save the ID of the
inventory that we are currently own instead
of the item data, and we cannot save the item a, because inside the item data, we have a field and sprite field or sprite data is
not serialzable. We can just grab the ID and
save those ID two A list, and later when we want
to load our inventory, we can just load it right
through the list of integer and then grab a new item based on the ID or the
integer ID that we save in our save data using the get items method
from our item database. Because integer is serialzable, we can save this two A file. Let's just create a new integer. Let's create a new
private integer and let's just type private
integer and this will be the safe data ID, and we want to set
this default to zero. Basically, we are going to save the safe data into a list, and that list is going to
be sterilized into a file. Basically, whenever
we load a safe data, we need to access
based on its index. Currently, for testing purpose, we are going to use
the first index. Now we need to create a
new list of safe data, but we already have
created the safe data. Let's just convert this
safe data to a type of list by adding a list keyer
in front of the class. Then when initializing,
we want to also inize as a list.
Let's just do that. Then I'm going to cut
this and put this below our safe data here.
And save this. Now we have this safe data or we can just add a as to
mark this as plural. It's clearer for us
later down in the road. And now we have this error. Be safe data, we don't
have safe data anymore. We have safe data safe
data is actually a list, so we need to paste this, but it will still throws an
error because it's a list, so we need to access its index and we can just
put as the index, our safe data ID here. Since this is zero
and it will try to save and load
on the first re. But later we are
going to create a way to save into a different slots, and when loading, we can
load from a different slots. Okay. I'm going to fix all of this error
by changing all of the safe data reference to safe data index of safe data ID. Let's just add a
letter and then paste this and add later and
paste this safe data ID. Now we fix those issues. Now we need to create
a new method to return whether we have a safe data at least one safe
data or we have none. Let's just create a new public
and it will return type of bolet's just call
this safe data. We want to make sure that
our safe data is not. Let's just access the safe and then we can make
sure that it's not new. If it's not null, then we
want to return I count, it's greater than one,
is greater than zero. Basically, if it's
greater than zero, it will return true,
but if it's not, then it will return false. If safe is, then we need to create another
condition here, and we need to return false. This method will always
return a bulon now we need to create a safe and load function
inside our data manager. Let's just create a new
public void safe for safe, we want to access
our safe system. Then we want to run
the safe method and we want to save the data to our safe data like this. We are saving the data
from our safe data, list of safe data here
to a file for loading, can just create a new
public void load. It should be load
only on level loaded. For loading, we want to pass
the data to our safe data, and then we can just assign the value from our safe system. Then we can type load
and then for the type, we can just grab the
list of safe data. And then we need to
add a parent to. It's like get component. This is basically a method
that as for a type, and it's also a
generic because we can grab any types of data to this get component
method and for the load method inside
our safe system, we can also load any type of
data by passing the data. Since we are saving the
safe data, so when loading, we know that we have a list of safe data inside our safe data, we need to load using that
type, so it's compatible. And we'll be able to load any previous data, if there any. Now we are going
to create a method for saving an entry
loading an entry data. Here below our load method, let's just create a new
public void safe data entry. For saving the data entry, we want to save based
on our safe data ID. Currently, this will be
stays zero, the value, but later we will have
different value depending on the safe slots that we are previously loading at
the start of the game. In order to save the data entry, we want to first run
the unsafe event. This will trigger
the save entities from any object that have the
safe entity component here. We want to also save
the scene name. Let's just access the save data and then index of safe data ID, which is currently still zero. The ID. Then we want
to insert we want to set the current string field here to our current
loaded scene. So let's just use the
Unit engine scene manage it has a
scene manager class. Inside the scene manager, we can run the active scene method, and then we can
return the name of the scene by typing names. This will return the name and it will get saved
to our current scene. When we finish
passing this here, we want to also save inventory. This We haven't defined it yet, so we need to create this now. For saving inventory, basically, we need to record all of
the items in our inventory. It's ID, and those
ID that we record, we need to save this to our list of integer
inside the save data. Let's just create a
new method to do that. Let's call this public
void save inventory. But we will need to create a helper method in our
extension static class. Let's just go to the
extensions script. I'm going to search here, and this is our
extension static class here and create a
new method with a public static keyword
void this would be the safe items to ID. We know this is for
saving items to ID, and we will need to add a
parameter list integer, this would be the items ID. The second one would
be the list of item, and this should be the
inventory that we are going to iterate from our
current inventory. But here, we want to extend
the list integer types, and we can add this
keyword in front of it. Later, when we have the list of integer data
or object in other script, we can use this method to pass through the
list of item and then get the items ID and then add those values
to our items ID. Save this extensions
and we are going to look through the
inventory count. Let's just this
inventory dot count. Whenever we are saving data, we want to check first
if our items ID contains a specific integer because
it as for an integer and It contains our
inventory index of item ID. This means that we already
have this specific items, then we want to skip that. We don't want to
save this because we already have this in
our list of integers. Otherwise, it will create
duplicates and we will have copies of items and
that will be an error. So let's just type
return. I will skip. Here, we want to
add the items ID, add the inventory
in x of item ID. So if if the items ID does not have the intended item or the current item that
we are loping here, then we want to add this
to the list of integer. The values of the item ID
from our inventory inx of. Now we have this method. We can use this in
the data manager. Let's go back to
the data manager. When saving our inventory
to the data manager, we can just type where
is the safe data here. Let's just access our
safe data and index from the save data ID, save that ID. Then for the item
inventory items ID, we want to we don't
want to pass, but we want to run the
method that we just created. We can just type here, and then save items. As you can see here, we have a extensions method
for a list of integer because we use this
keyword for this parameter. This will extend the
list of integer here. Whenever we have a list of integer objects, we
can use that method. Safe items to ID, and we will need to pass
a list of inventory. List of inventory is
basically our inventory here, we can just access
the inventory fields and get its inventory. This will return
the list of items. And this is a properties. Let's just close
this with semicolon. Now we can save the inventory
and it will convert our list of items to
a list of integers. Now in the save data entry, we want to save the inventory. Once we save the inventory, we want to save the data here. Let's just run the save method. Okay. Now we want to load an
entry from our safety data. Let's just create
a new public void, and let's just call
this load data entry. For data entry, we
can pass an integer. Let's just call this ID. Then for load data entry, we want to pass the ID to our safe data ID
and an equal ID. Here, we want to load inventory, but we haven't defined this yet, and we also want to
run the unload event. It will trigger
all of the safe it object that we have
on our scene and it will load the previous
safe entity data. Before we can load this data, we need to run the load method. Let's just type load here. Now we need to define the
load inventory method. Here below the safe inventory, let's just create
a new public void, and let's just call
this load inventory. Basically, we need to convert
back our list of integer to a new list of items based on the item ID that we saved
into that list of integer. To load inventory, let's go
to the extensions class here, and we are going to create
a new public method. Let's just type
public static void. This would be load ID two items, and this will extend
the list of items. Item and let's just
call this inventory. Then we need to also
access the item database, and let's just call
this item database. The last one we need to pass is the list of integer. Okay. We will look through the
items ID that we pass here, and this items ID
will be pass from our safeta should have a
couple of items in it, and we need to look
through that items ID. Let's just create a loop, then we can just type
items ID co It will whatever members or how many items that we have
inside the items ID. Then let's create a new item, temporary item called item. We are going to copy item and we have
this copy item here. And we have range of copy the item from
our item database. But remember, inside
our item database, we have the get item
method and it asks for ID, so we can pass the items ID. Value here. Let's. Let's
just type items ID, and then grab the index of I. It will look through
the items ID, and when it has a value in it, it will have an integer value. This integer value will be the items ID that we previously saved when we get
item based on its ID, it will return an item and
it will copy the item. To a new instance of item here. Once it creates the new item, we can add this item
to our inventory. Let's just type inventory, and then we want to
add the item that we just created here. Pass the item, and it will add this item to this list of items, and this is an extension. We can use this in our
inventory descriptable object. Let's open the inventory
scriptable object. Here we are going to create a new method called
update inventory. Let's just call public
void update inventory. There is too many brackets
that were created, so I'm going to fix that. Save this. For this
update inventory, we are going to pass
a list of integer, and this will be the items ID. On start of this method, we want to clear our item. Let's just grab the
items, the inventory. I think this list of items
here. We want to clear that. Let's just run the clear method, and clear method is actually a method from
the list of items, and this will act for clearing any entries inside
this inventory here. Okay. And then we want to run the extension
that we just created, which is called
load ID two items. This as for a item database, so we can just grab the item database because
the inventory in scriptable object
have a reference to the item database
here as you can see. Okay. And then we want
to pass the items ID, which is a list of
integer, safety. Basically, this method we'll access this extensions method, then we'll create a new item, and those items will gets
added to the inventory. Once it finished updating, it will update our inventory. Now we already have this
update inventory method. Now we need to go to the data manager and then set up the load inventory method. Here on top, as you
can see in the fields, we have a reference to
the scriptable objects. Now we can just access
the scriptable object and then run the update
inventory method, and it will ask for
a list of integer. For this list of integer, we can just type safe data
index of the safe data ID. And then grab the
inventory items ID here. It will grab any value that we have here from our save data. Then it will update
the inventory based on the items ID
that we saved previously. Save this. Now we have this load
inventory method created. We need to load here in
the load data entry. Let's just type load inventory. Let's save this. Now let's go back to unity and see
if we have any errors, hopefully not still
compiling right now. So far no errors. In
order to test this, we are going to create a debut for loading and temporary
loading and saving. So let's just create a new
button here. Create an empty. I'm going to put this empty on the top left of our screen
and going to expand this, and then I'm going
to add a button. I'm going to set up
this button here. Move this to the side here. Set it tax increase its size by enable the best fit
and then type safe, and then create a new button for loading, push this aside. And then open this and then
change the text to load. Save this. Now for
testing this out. We can just grab the data
manager here for saving, we want to run the save data
entry method for loading, we want to enable the onclick method and then
drag the data manager, and we want to snager want
to run the load data entry. For the ID, we can just set this now at this moment to zero. Save this. I think
this should work. Let's just go to the data folder here and
for the item database, not the item sorry. For the inventory
scriptable object. Let's just delete the key
object here, and save this. Now let's run this
pick this key here. Then let's talk to
this person here. I will move out and let's go to this position and
then save this. Now, there is an
error on saving. Let's check that first. I found the error,
and basically, whenever we save
this, safe data is actually doesn't
have any member yet. We need to make sure if we
don't have any member yet, then we need to create
a new safe data. Here in the safe data entry, let's just create
a temporary code and check for safe data, and if it's equal to zero, Then we want to
create new safe data. Just run the add method and then initialize a new safe data by typing new safe data like this. This will create
a new safe data. And now the save
system will work. One thing we need to fix or
we need to add is also on the invcript object class here under the update inventory. After we clear and then we
load the items from the ID. We need to run the on items change event that
we already defined here, and we need to pass the
inventory list of items. Let's just type the inventory
object here and save this. Now with the changes, let's test this in unity. Okay. So first, I'm going to clear the inventory and once it's finished compile,
let's run this. Now, let's try to pick
this object here, the key and then
talk to this person, so this person will move and then go to
this position here. Now if we open the inventory, we will have this key
object and now save this. It should print out save
success and then stop this. Basically we are already
stopped the session. A in the previous session will be clear from the memory and
we can reset the inventory, so we don't have
the key anymore. Now if we press plea and
then If we try to load, it will load the
inventory position, the last position
for this person here and also our last position, our less player position. If we open the inventory, it will also draw the
inventory item because we run this event and this will alert the inventory system
UI to redraw the UI. That is for the
basic safe system and we will also create
a screenshot to be safe whenever we save an
entry and we will create a UI to load different
slots. Okay.
42. 41 Transparent FX: Okay. Now we are going to
create a transparent effect. Basically, what we are
going to do is we want to create some effect and this will change an object to
a transparent state whenever a player is behind those objects, so
we can see the player. And this will be useful
for a rooms or wall, whenever player enters
that rooms or wall, we can change the wall that occludes the player to
a transparent material. In order to do that, I'm
going to first create a new folder and I'm going
to call this FX Here, I'm going to create
a new C script, and I'm going to
call this first then effects and create
another cup script and call this
transparent trigger. Trigger. Now let's open the transparent effects and
also the transparent trigger. For the transparent trigger, we are going to create
a couple of fields. We will need a reference
to our player, and let's just create a
new private transform and this will be
the player. Okay. The next one should be a
private ray and just call this ray.other one is the cast
hit, and let's call this hit. And then the last one is
the transparent class, and let's just call
these effects. This will refer to the wall that are occluding the site from
our camera to the player, and we will get
that script and we will trigger method inside
of this class here. On start, we are going to
get reference to our player. Let's just type player, and then we can use the game
object class and then it has a fine objects with method
and search for player. Since we are referencing the
player to a transform field, we need to get it
transform by accessing the transform properties because fine game object
with tag method, we will return a game object. Let's say the script, and then let's create the trigger
inside the update method. Basically, we want to
feel the ray field here. We want to create a ray from
the camera to the player. In order to do that, we
need to declare a new ray, and here, we need to insert the origin, and
then the direction. For the origin would
be the position of the camera and this
will be the transform the positions because
we are going to put the transparent trigger class
to the camera on the scene. Then for the direction, it
will be the player position, substract with the camera
transform position. Okay. This would be the ray and this would mean that we are shooting a ray from
the camera to the player, and we want to check if
this ray hits any object, so we can just use physics cast and then we can pass the ray, we are going to
use and we want to output the result to our field, so we can extract any
info from this hit. Then for the distance, set two math float class and it
has a infinite properties. First, we want to check if
the hit is null or not. If the collider is, the hit collider is not, then it means that we are
hitting something. Then we will check if
hit else if hit anything else but player and the other
else if hitting player. Basically, we want
to make sure that if the f x object is
currently null, then we want to grab it. We want to grab the f x from the object that we
are hitting currently, so we can just access the
re has hit and access its collider and
from the collider, we can get the neighbor
component by using the get component method and search for the
transparent effect. This we'll try to search
for this component, and then we can check
if the X is not null, then we want to run the
transparent effects. But we haven't created this yet, so we'll just leave
a comment here, and then we want to set else hit collider and we can
compare it tag is player. Then it means that we are
currently hitting the player, and we need to check if
the effect is not null, then we want to switch back
then the transparent effects. Save this after switching back the transparent effects,
we want to set the effects, so we can just type effects
equal new Okay. So save this. And now, basically, the the transparent trigger
script is already done. The next thing we need
to create is We need to define the transparent
effect script. Let's just do that. I'm going to tidy
up the script a bit safe Let's go to
the transparent effect. For transparent effects, we need to create a new
serialized field, and this will be a type of material let's just call
this transparent material. This will hold the
transparent material that we are going to create
in the projects. The next field that we need
are an array of materials. Let's create a material array. Let's just call this original. And then we need also
a mesh render array, and let's just call
this mess renderers. We want to initialize
this on start. On start, we are going to
fill the mesh renderers with any component on the object or on the child object by using the get components in children and search for the mesh render. And get components in children will return
a mes render array. It will populate our array here, and then we want to set the original material array to have the same amount
member of our mesnder. We can just
initialize this using the new material
keyword and then insert the mesh render length
as a array numbers here. It will create the
same length with the mesnder then we can look
through the mess render. Let's just create
a four lo keyword and get the measure
render length here, and then we want to assign
original index of I. Okay. Equal the mesh
render material. Basically, we are going to save the material of an object
to the originals here. We will keep reference to
the original materials and we can safely change the materials to the
transparent one when we need to change it. Save the script. Now we want to create a
new public method. We can just delete
this update method. Let's create a public
void and switch material. We can call this switch material we want to as a parameter. Just put a bullion, and let's just call
this transparent. And we want to make sure
if transparent equal true, then we want to change all of the material to a
transparent material, we want to revert back to
the original material. Now we can look through
the mesh render length, and then we can set
the mesh index of material to the
transparent material, and if the transparent is false, then we want to set this back
to the original material, so we can just get
the original x of because the order of the measured will stay
the same when we are initializing the
original material array, we are using the
same order here, as you can see, it should work. Now that we have finished creating the transparent script, let's go back to the
transparent trigger and then complete the script here. Basically, this
statement here is to check whether if the
effect currently is new. If it's currently is new, then we try to grab on the object that
are hitting our has, and if the f x is not null, then we want to switch
the transparent effect to transparent material. Let's just run the
switch material method that we just created before and then set the bulon
to true and this will make sure that we are
running this part of the code. Switch the material,
all of the measurement are found on that object
to a transparent material. And here, the LS statement
is to check whether if the object that
getting hit by our RCA has type of player, then we want to switch the material back to
the original one. So let's just set this bully
in to false basically, if the ya hit the player, then there are any object that are standing in front of the player or
clding the player. Save this and Another thing we can do is let's go
to the player script, the script where handles
the player movement. We want to create
a new layer mask. Let's just create
a serialized field and type should be layer mask, and then we can just rename
this to interact layer. For the physics recast, we can add the layer mass to
the fourth parameter here. Let's just type int layer and this will make
sure that we only want to cast against
the object that has the layer that we set
up in the inspector, a specific layer,
a certain layer, and it will ignore any other
layer that aren't selected. Let's just save this
and go back to it. Now here, if we
select the player, we will have a new in
slot or fields here. We can set this to a certain layer, but we
haven't created that. Let's just select
our ground object, and then we want to create
a new layer called ground. Then the other one should
be the interact table. Now if we select
the ground object, change the layer to ground
and save this and go back to player and we can set the interact layer to the
ground and to the interactable. Basically, we can only click on the ground or on the object that have a interactable script, and we will set all of
the object that has interactable script has
the layer of interactable. Now let's just select the
cube here and let's duplicate this and maybe put it here.
And we want to scale this. So it looks like a wall, and then push it upwards. And for this script here, let's add the
transparent effects, and then add the
transparent material. For the transparent material
is basically a new material, just create a new materials
in the materials folder, and this transparent material just set the rendering
mode to transparent, and for my settings, I've set the color to a slight gray and set the
value of the alpha to 0.1. This will be transparent. And if we select the cube
that we just created here, we can wreck the
transparent material to the transparent
material slot. Don't forget to add the transparent trigger
to the main camera, and this should get added also
to the other camera here. Let's just add that
transparent trigger. Let's save the scene and
let's test this out. So now we have set the
ground to the ground layer. So whenever we click
behind the wall, the player will go to
that point of the ground. But right now, there is
an issue because when we go back to behind the wall here, the wall doesn't get
changed to a transparent. In order to make the
transparent wall, to work, we need to disable this cube
tree here because you have this empty clider that triggers
our change camera before, right now, the wall here
lies behind this cube here. Whenever we run, our camera
will hit this clier here. It will do nothing
because it cannot found the transfering effects when the camera should hit
this other cube here. Let's just disable
this cube tree for a while for
testing this out. Save the scene, and
let's try this again. Now let's try to go
back to this wall here. Once we go back, as
you can see here, we have a transparent wall. If we go back to front here, it will back to the
solid transparent. And this will be applicable
to any other object that has different material
because whenever we are changing to the
transparent state, basically on start, we already save reference to the
original material. Whenever we comes back
in front of the camera, then we can switch the material back to the original
material. Okay.
43. 42 Audio Manager: Okay, so now we are going to create
the audio system and we are going to start
with the audio manager. Let's create a new
CSP script under the script folder and then
under the manager folder. Create a new CSP script, and let's call this
audio Manager. Then let's open this
in Fissio Studio. First, we want to import
the audio namespace. Let's just type
using Unity engine, the audio, and we will have
access to the Mixer class. Let's create a new S field, and this would be
the Mixer group. The audio mixer group. This would be the Soffe group, and then music group. We want to separate the channel for sound
effects and music, so we can create a
volume controller later. With this audio menager the
audio menager will create a empty game object with audio source
components attached to it, and we can define the
characteristic on initialized. Let's create another
cerlize field, and this would be
a type of integer and this would be the
audio source instances. And we can set this
default to five. We want to also create a Q.
Q is basically like a list, but it doesn't have
add or remove feature. Instead, it has a
N Q and Q feature. I will show you how this
works later in this video. But basically, we want to create a Q type of audio source. This would be the SFX library, lips Let's initialize
this Q audio source. Then we want to create a single audio source
component or fields, and this would be
the music source. Or we can just call
this music player. This. We are going to make sure that this audio manager
act as a singleton, so it persists through scenes. Let's just create a
new public properties, and this will be
a type of static and should be the audio manager, and let's just call
this instance. Then we want to set a public
get but a private set. Okay. Now let's create the void awake method and
inside this void awake. We want to sorry
want to check if instance is equal then we
want to reserve the instance. We are going to assign
instance to this script here. And after we assign the instance
to this component here, we want to set this
object that holds this audio manager to have a don't destroy unload behavior. We will set the
scheme object unload, so it will persist
through the scene. And else if some
other object already claim the instance or if
instance is not null anymore, then we want to destroy
this game object. The next thing we
need to do is we need to create a init method. Let's just create that. Basically, for initialization,
we want to create instance of audio source
attached to a game object. We want to create those object as many as our audio
source instance value. We want to look through
this amount here. Let's just create a four loop. Then for the length,
we want to use the audio audiosurce instances. For initializing, we need to create a method that
will return audios. Let's create a new method that returns audio
soource this will be the audio source stand, we want to pass a
couple of parameter. The first one should be
the audio mixer group. Let's just call this group. We want to assign which group that we are passing as
the audio mixer group. We want to pass also 1 billion, then this will be
the S. If it's true, then this audio source we incentiate audio source
component for sound effects, and if it's false, then
it's for the music channel. T one should be a string, and this is name and we can
set this to a default value. Let's just set this to
audio source by default. Here, we want to create
a new game object. Let's create a new
temporary audio source, and let's just call this audio, and then we want to
create a new game object, and this is for
creating a game object. But we can pass the name
to this parentheses here. Let's just copy the name
and pass this here, and this will create
a new game object and it will also
return a game object. In order to return
a audio source, we want to add a component, and then we want to
add an audio source. Once we set up like this, this new object component audio source that are added
to the game object, we will be referred to
this audio object here. The next line, we can modify any properties from
this audio source. The first thing we
want to set is we want to set the audio source, the group here, the
output audio mixer group to be the group that we pass
in our method parameter. Okay. And then for the spatial
bland and spatial band is a three D specialization
calculations, and if it's zero, then it makes a sound fully two dimensional
and if it's one, then it will be three
D sound and it will react depending on
the audio listener that we have on our scene. Usually it's on camera. The further away the
object from the camera, then the volume of the
sound will be smaller. We want to set this
depending on our SFX lean. If it's true, Then we
want to set this to one, and if it's false, then we
want to set this two zero. This is basically a
ternary operator and the format or the syntax for this ternary operator,
this is the condition. And this will be the result. If it's true then this will be the value of
our special bland, and if it's false,
then this will be the value of
our special bland. If we set the S effect to false, then the special brand
will be zero and it will be suitable for our
music audio source. We want to also loop the
audio based on our Sf. Let's just set the
loop mode here to negative or
inverted as f x value. For example, if we are
setting this false, then the audio loop
will be true for music, we want to set the
loop to true and for sound effx we want you to
set the loop to false. Then we want to also set the parent to the
audio transform here. Let's just access the audio and then the transform and then we can use the set
parent method. Then we can pass the transform
of our audiomager here. And after that,
we want to return the audio because this method returns an object
type of audiosur. We need to return
something here. Now we created the
audiosurce insaniate method. We can use this to insentiate
a new audio source upon it. So we can use this audio instantiate and then we
want to pass the group. For the audio source instances, we want to set the
sound effect group, for the so effect bullion,
we want to set this to true. For the name, we can just pass
this to the audio source, and then we can add the
index the increment, it will have difference
value, different names. And we can set this
two string with a two digits format.
It's readable. After we create this
audio source instantiate, we want to instate another one, and this would be this is for the music group and set the sound effects to false let's just call this music source. And save this. Now if we go back to our
fields declaration, we have this Q SFX lips and also audio
source music player. We want to set this
to the music player. Let's just type music player. And we want to keep the reference for our
music layer here. For the sound effects, we want to put this
to our Q here. Let's just type SFX lip and then Q and then put this
inside the parenthese. We will shuffle this
audio source inside the Q and then later we can
use this from our Q here. Okay. And now we need to
run the init on start. So let's just run
the init method. Next, we need to create a
couple of public method. The first one would be a public
void play sound effects. And we want to pass
an audio clip, and this is call this clip
and also a transform, and this will be
the source position for our audio source. It will react to a
three D specialization, and we want to set this
source to know by default. We don't have to always put transform whenever we
execute this method. We want to create a
temporary audio source, and that's just call this audio. Okay. And then we want to set if sound effects lip
count equals to zero, then it means that we don't have any free audio source left, then we want to
instantiate anyone. Let's just run this. Instantiate number
one, and we want to put this inside our
audio variable. Here, we want to set the naming by the maximum count
of our sound effect slip. We can use the audio
source instances. Let's just typed so instances. And we can increase this.
Okay. So save this and then, if there are still free
audio source available, then we want to deque
from the SFX lips. Let's just set the audio
to SFX lips and run the Q this will any audio source inside the
q here, and we can use that. Now once we set this up here, we want to set the audio clip to the clip that we pass
inside our parameter. Then we want to run
the play method. We want to also set the
position of our audio source. Let's just access the audio,
transform that position, and then we can use the ternary operator
like this one here, and we can check if
the source is not new, then we want to
grab the position. But if it's new, then let's
just use a vector three zero. It will be incentiated
or it will be positioned in the
center of our role. And at the end, we want to put our audio
x to the sound effects lit by running the
N Q method and pass the audio that we are just
previously using here. Here, I'm going to
add a method to move from its hierarchy using
the set as last sibling. This is basically
for illustrating for illustrate the Q and Q process. Later, we can see this in the hierarchy in the editor whenever we play
a sound effects. Another method is for
playing the music. Let's just create a public
void and play music, we only need to pass
the audio clip here. Let's just call this music. We want to if the music player
and music player is our audio reference for
the music player here. It's clip the same as our pass music from
the parameter here. Then we wouldn't want
to change the music. Return the method. But if it's different, then
we want to change it. Let's just access the music clip to the new clip that we are passing and then we
want you to play that. Let's just run the play
method from our music player. Okay. This is basically the audio manager here and
safe D let's go back to unity. Now let's create a new empty
game object for setting up the audio manager and
reset its value here. I want to reset the transform. Reset the transform, and then rename this to audio Manager, and we want to add the audio
manager component to it. I ask for the sound effects
group and music group. In order to do
that, we can go to the audio folder here and
create a new mixer object. Let's create and here we have this audio mixer and let's
just call this master mixer. If we double click it, it will open the mixer window here under the groups, we
can create a new group. The first one, we want
to create sound effects, and then select
the master again, and then we want to create
another group for the music. We will have two different mixer for outputting
different audio types. The first one for sound effects. The second one for music. If we go back to the
audio manager here, now we can pick those group
for the sound effects group. Let's just pick the sound
effects for the music, let's just pick the music. Save this. Right now we
cannot test this yet because we need to
create an action to trigger or to play a sound, and we will do that
in the next video.
44. 43 Audio Action: Let's create the audio actions to play audio using
this audio manager. In order to do that, let's go to the scripts folder under
the actions folder. Let's create a new
C sharp script, and let's call
this audio action. Once it's created,
let's open the script. Since this is a action class, we want you to derive from
the actions based class. And we want you to
delete the epic method, and let's implement
the abstract right. Let's just implement this
abstract class here, and I want you to cut this
and put this below the start. First, we are going to create
a new serialized field, and this would be the
audio clip array. Let's just call
this audioclipse. The next one would be the
bullion and this would be for toggling if this
audio action is for playing music arena we can
create is music bullion. Then we want to create a new private audio manager reference. Let's just call this manager. For the manager, we want to
grab this from our singleton, so we can just type manager
equal the audio instance. The next time we need to use
this audio that instance, we can access the manager. Now let's modify the a method. Basically, we want to check
if it's music is false. Then we want to play
a random audioclipse. Let's just access the
manager, play sound effects. Then it as for a clip. We can just type
this audioclipse. Then for the index, we can use a random
value, random range, and then we can
insert the range from zero to the length of our audioclip This will automatically choose
a random eclipse from our audioclipse array. For the transform, we can
just pass the transforms of this audio actions
holder or owner. Let's just type transform else if we are going
to play a music, then we want to use
the play music method. For the clip, we can just
grab the audio clips. The first one, and safety. This is the script for
the audio actions. Now let's go back to
unity and test this on. Right now we want to
create a footstep sound. In order to do
that, we can go to the player and to the
graphic objects here, the one that has the animator
component attached to it. Let's just direct the audio
actions to that game object. For the audioclipse,
I'm going to set two entries and inside the audio folder, we
have a footsteps. Let's just direct the
first foot step A, and then footstep B. And I want to press apply
to update the prefs. After that, we are going to
duplicate the animation. Let's just go to tiny people. If we go to the object
here and the animator, we can check which animation
are we using in the blender. Basically, we are using this. If we go to the project,
this is the animation file. I want to duplicate
the walk here. And it will be a separate file, and I want to rename
this M M walk footsteps. I'm duplicating this by selecting the clip and
then press control these, it creates a new clip file. Now if we go back to animator, we can change this
to the MO footsteps. This is basically
the same animation. If we press play
here, you'll see if I go to speed of one, it we play the
walking animation. And why do we have to
duplicate the clip because now if we open
the animation window, we can create a custom
event with this footsteps. So if we select the
graphic objects again, the idle is read only. We cannot change it.
But for the footstep, we can create a new
event here. Let's just Select the game object, rotate this and I'm
going to focus here. Let's just crop the timeline. We're able to see the animation, I want to synchronize every time it puts steps on the ground. For example, if I
move this here, I want to play sound
in this frame here. I'm going to create a
new event over here. Then for the other feet, I want to put the event here. Whenever the foot
contexts the ground, I want to create
this event here. Now, if you select the event, we can select the function
because we already add the audio action and
egg method is a public. For any function
that wants to be triggered by the animation
event has to be the public, for the second one, I'm going to also select the agg function. Let's save the scene and
now let's test this. Okay. As you can see,
when we are moving, it plays stepping sound. Now let's create a sound whenever we are
talking to a person. Let's go to the
audio mixer first. I'm going to set the forum
lower for the sound effects, and let's test this again. Okay. Okay. The next thing, I'm going to select this police officer here, the NPC, and it has a message. And let's just create
audio action here. And whenever we interact, we want to play
the audio action. Let's just create new
actions on our intractable, and then let's play a dialogue. We have this male a dialogue and drag these
actions to the NPC, to the second sot
of these actions interactable and save this. Now, if we press play, Okay. And let's try to. Sorry. I need to change the
NPC layer to intractables. So let's just do that
change the children also. Now let's try to click on
the police officers again. Okay. So, we have a really nice working
audio system and later, we are going to setting
up how to play music. And right now for the audio
systems is basically done.
45. 44 Interact Cursor: Now we are going to create a cursor script and
this cursor script, we create a behavior or a hint. Whenever we are hovering
and interactable, whether it's a person, it's a sign, then it will show an icon to indicate that we
can interact to that object. In order to do that,
first I'm going to create a new subfolder inside our scripts folder and I'm
going to call this UI, then let's create a
new sharp script, and let's call this
cursor script. And let's open cursor script. This cursor script is
basically a controller. We are going to attach
this to a canvas and we are going to create a
couple of serialized field. First, let's create
a serialized field and this will be a
type of transform, and let's call this cursor. Then the next one is
also a serialized field, and this will be
a type of image. But since we have import or using the UI name
space, we need to do that. Let's just add a
using unity engine UI on here, on top here. And then let's type image and this would
be the cursor image. Then we want to also
create a serials field and this would be
the layer mask, so we can filter out let's
just call the layer mask. Then let's create a couple
of private variable. The first one would
be the camera. And we are going to store the active camera
to this variable. The next one should be our cast we can just call
this and the other one is and call this ray
the last one is the interactable Let's just
call this current interact. On start, we want to grab the main camera that
are currently active. Let's just grab the camera, camera class and then
call the main properties. This will return the first or first enabled camera
tag main camera. Then we want to also event on the camera manager instance. We have this on camera switch, but we haven't created a method subscribe to this
event. Let's just do that. Here we can call
this camera switch, and we need to pass a
camera to this method here, and then we can just assign
this camera here to the cam that we are passing here to differentiate which variable is this, and which one is this? We can add this and this will
return to this class here. If I wrote it, then it will revert to this
private variable. If I choose this cam here, it will highlight and it will
show that this is actually the argument parameters that we are passing by
this method here. Now we have created this method. We can just ascribe this to
the on camera switch event. Let's just type the method name camera
switch, and save this. First on update, we want to set our cursor position to be the same with our
input mouse position. Then we want to
create a new ray. Let's just type the
variable and then use the variable here, and then use the screen 0.2 ray let's grab the
input mouse position. Now we can check if
this ray is hitting some object in the
scene and we can filter out the ya using
this layer mass. Let's just create statement, and we want to access
the physics class and access the a method. Then as for ray we can
just put the ray here, and then we can
put the result to the variable here
with this skewed out, it will send out result to this hit instead of
getting the value, and then we can use the
layers, type layer mask. And then we can
open the bracket. Then we want to
activate the cursor. Let's just access the
cursor game object and then run is active method
and then set this to true. We need to check if
the collider is not. It means that we are hitting something, and at the same time, our current interact is still, then we want to grab the current interact
if there are any. Let's just type
current interact and then access the
collider and then get component and we can get the interactable class
from this object. If we filter these
layer mess for only interact with interctables, then most of the
interactable object should have this component. And we want to change
the cursor sprite. But we haven't
implemented this yet, so we are going to
modify this later, but let's just create
the Ls event here. Basically, if this physics
races doesn't written true, it means that we don't hit anything that are inside
this layer mass here. Then we want to hide our
cursors L statement here. Then for the cursor object, we can access the game object
and then set active to false and we want to also set the current
interact back to null. We want to clear
interactable class reference inside this current interact. Now let's modify
the interactable and add a new serialized field. Inside this interactable, we are going to add
a new cerlized field and this will be a
type of sprite this should be the sprite cursor. Since it's a serialized
field, we cannot access this. Let's just create
a public getter below here with the same type, which is sprite and
then let's call this sprite cursor with
capital S. Then we want to only get return cursor value, whatever they are,
and save this. Now if we go back to
the cursor script, we can change the cursor sprite. We have this image. Let's just access
the cursor sprite this should be the
current in sprite cursor. For any interactables,
we need to assign a sprite cursor on the
inspector, so it will interact. But we can create
some fail safe. If the current sprite
cursor is null, is not assigned, then we want
to hide the cursor object. Set two falls. Okay. Let's test this out. Let's go back to unity, and let's create a new empty for the base canvas and I'm going to stretch this to
fit our canvas. Then I'm going to set
to call this cursor. I'm going to also create a image and this would
be the cursor image. Let's put the cursor script
to this cursor game object, this empty UI game object. Then we want to change the
layer mess to injectable. For the cursor, we can just
drag this object here. It will grab its transform. Cursor image, we can
just put the image here. Now we save this and switch
back to three D view. Let's go to the NPC that
are currently active. Here in the intractable, we cannot see the
sprite because we haven't created
that in the editor. Let's just go to
the editor folder, and let's open the
intractable editor. We need to create a new
serialized property, and let's just call
this sprite cursor as underscore sprite
cursor and we want to grab the
value inside enable. Let's just type sprite cursor
equal serialized object, fine property and I
believe this this is called I believe the
serialized field is called cursor. Let's check this. Sprite cursor, and then
we want to draw that. We can just draw
this on top here. For the sprite here, we want
to use an object field. We can just grab the object reference value here and it has a
object reference value, and then we can assign this to the editor GI layout and use the object field method and we can pass the
serialized property here. Let's type Sprite cursor. I think we can just grab the object
reference value again here, and then we can set the
type of a sprite here. We can add a false flag here. This is basically to make
sure that we can only pick an object from the
assets, not from the scene. It won't allow to
picking a sprite from the scene, and save this. Let's try this and
go back to unity. And let's see if we have, we have the slot here
and we can pick Sprite, for example, let's try to
pick this UI sprite here. But it doesn't show the UI here. Let's change a bit of the code
here for the sprite here. We can I believe we
can add a name before. Let's just call
this sprite cursor. And then after the
false statement here, let's just add a layout and use the hide and sets
the height to around 75 and save this and
see if this increase the size of the sprite
slots in the inspector. Yeah. We have the
sprite cursor and we can see the cursor that
we are currently using. Let's save the scene first. Now let's try this, see if it's working or not. Okay. Okay. Yeah. As you can see, it shows something here. So basically, probably
there is a collider here, but if we go to the
police officer, it should change the. There you go. If I go
to the police officer, it change but there
is an issue here. Let's check the ground. Okay. Sorry, there
was an issue before. And here, if I hover on
the cast, as you can see, currently, it gets the value of layer mass as a
float mass distance. The value of the layer mess
is actually an integer, and it treats as a max
distance, not as a layer mess, so we need to add a parameter for define the max distance, and right now we can just
set this to 100 unit, and then the last one
should be the layer mass. If I hover again,
and as you can see, we are filling the argument
correctly right now. And if I save this, this should fix the issue. Now if I press play, it won't show a cursor
on any object that are assigned that we haven't
assigned the sprite cursor. But if I go to the
police officer, it will show this circle cursor. But right now we
have another issue. If we press this,
we cannot go to this person because
the cursor image is blocking our cast target. So let's just top
this and then select the cursor image
and then disable the Cast target and
save the scene. And since we added a new
child to this base canvas, let's just update the
prefs by pressing apply. So it will be included to the pre and save
the scene again. Now if I try to click on the police officer,
we can interact again. And then now we can go to
the NPC here and we can change this to the top cursor
and for the other NPC, we can also change the
cursor to the top here. And for the cube here, this is for picking
item, I believe. So we can just use a hand
cursor, so save the scene. Now if we press play, We can talk to this officer or we have an issue here because we haven't
changed the layer. Let's just select
the other PC and then the other green cube here. Let's change its
layer intractable. Yes, and change children
and save the scene again. Now if we try to hover the
player or the other NPC, as you can see it change our cursor we can
pick this item, we can talk to
this person, Okay. And we can talk to
this police officer. And yeah, that is pretty
much all about the cursor, and it's a nice visual feedback to let the player know if we can interact
to an object or not. Okay.
46. 45 Look Only Interaction: Now we are going to expand the
intractable script because currently we can interact
to an object or NPC. But whenever whenever we try to interact with
the intractables, the player will move
to that position. We want to add an
option where we can just create a on the option, and this will be suitable
for a sign or anything that doesn't require the player to come over to the
interractable position. Let's open the
interactable script. Once it is open, we are going to create
a new serzed field. Let's just create a
serialized field here. Then type and let's
just call this only. Let's create a public accessor and this should be the type of bone's call this also
only with capital L. Then we can set together
and return the only value. And safety. Now that we
created this options here. Let's go to the player
script and now we can check inside this physics
cast if statement. Now for the move player, we
can add an if statement. Basically we want to check if the interactable
only is not true, it's false, then we want
to move the player. Otherwise, we want
to ignore this code here and just do the
interaction and save this. With this changes, we
can create some things. For example, let's just try
to modify this object here. Okay. And let's just add
an intable script. Okay. And then let's add
a message action. But here, as you can see, we don't have the Boulan option because our editor script for the intractable
doesn't render that. We need to go to the editor folder and open
the intractable editor, and we also need to create
a serialized property, and let's grab it inside un enabled by accessing
the serialized object and then front property and access the only with lower se L. And say this. Now we can draw the inspector
using the editor UI, property field and insert
the Suds only property. Let's create a label
using the new UI content, and then just type
a string inside the parameter only this with a semicolon
and then save this. Let's go back to unity. Now if we click
on the cube here, we should have this only option, and Let's try this only
option and Let's give the box cursor for this
sprite cursor under the interactable inspector
and add new message, let's just type a message. We can just add action and direct the messes
action, save the scene. We will also need to change
this layer to interactable. But we can create
that inside script. Whenever we are adding new
interctables to an object, it will set the layer to
interactables by def. What we want to do
is we want to create a new build in
method and it reset. And then we can set the game
object layer layer mass, name two layer, and we grab the layer name and just to make sure that the
layer name is interactable. So let's just type that
interactable and save this. Now if I let's delete this again, this
interactable script. And the interruptable
script again. It will change the layer automatically to intertable
as you can see here. So we don't have to set every time we add an
intertable component, you don't need to change
the layer manually. Then we can just direct
the message action again to the action
slot and enable the only option and
also at the cursor. And save the scene. If we now try to test this
feature, as you can see, we have a really nice cursor
and if we click this, it will only show a message, but our player will
stay in its position. Yeah, that is the features
that we need to add to create a looking at
something behavior. Okay.
47. 46 Preparing City Scene: Now we created most of the components that we
needed to create this game. Now let's start to create
the lighting and set up the global illuminations for each of our levels or our scene. Before we bake the scene, let's create a pref for
each of the manager, so we can easily add this
onto the other scene, direct the audio manager, and also this pw manager
and the camera manager. And we have created
the C before, so I'm going to go to the scenes folder and open the city scene. Now we have the scenes opened. Let's just revert the
city that we have here. First, we want to delete
this directional light, the second directional light and we want to only
use one light. We need to make
sure that the scene is during the nighttime. Let's go to the lighting and if we go to the point click
here under the material, we will have We will have the sky the night
sky box over here, and we are going to use
this for our skybox. Let's go to the lighting panel, and if you cannot find
the lighting window, just go to the window
and then under the rendering, open
the lighting settings, and it will open this panel
and wreck the skybox, or we can just browse in the
skybox material and choose this black skybox Let's
clear the big a first, so we will see the real time result of our lighting of
our current lights. Now I'm going to
filter out our lights here so we can modify all
of the lights in our scene. Just type T for types full colon and then type
the component name, which is light, and
we will able to see all of the light that
we have in our scene. I'm going to select
all of the spot light. I'm going to change
the mode to only. Then I want to set the
indirect multiplier to 1.5. For the point light here. I'm going to also
change this to bake. The light will only get speak and it will reduce
performance overhead. We have two directional
light, as you can see here. I'm going to select
the second one, the one with the yellow
to whitish color, and just use this one here. Let's just clear
out the search bar here so we can see all
of the other object now. Now we have this Let's
save the scene again. Now let's try to
bake the light here. I'm going to change
this to enlighten. We will have a softer
lighting and change the light resolution to 20 and also the light
map size to 259. I'm going to enable
the ambient occlusion and I'm going to increase
the indirect contribution to two and change the
light map parameters to default low resolution. Now, let's try to generate the lighting and I'm
going to cut the video and go back when
the baking is done. Now, as you can see here, we finish baking
the lights and we have a very nice time lighting, and we also have a very
nice ambient occlusion, as you can see here on the air conditioning unit and the line between the
pipe and the wall here. We have a nice smooth shadows
on the edges here. Case. Now let's save the scene again. Now let's try to play this game. If we move the player,
as you can see here, the player doesn't get affected with the light from the
light pole over here. Because our spot light
it sets to bake only, and we want to create an effect, so it will affect any
moving object such as character and BC but still at a very low performance
overhead and without activating a real
time light because we already have one real
time light in our scene, which is the direct
directional light here, and it casts the
shadow for our player. But if we make all of the Spot light or the light pole the lights on the light pole
here, or to be real time, then it will be to perform or
too costly for performance. We will need to
create a light props. Basically, light props
is a way to sample the lightning information
in certain areas and our player mesh can sample from that lighting
information without having to do a expensive real
time lighting calculations. Now let's open the City
game object and let's create a new child object
and under effects, under light, we have
this light probe group. Let's create that light probe. And I'm going to go
to the front view and disable the perspective, so we will have an
ortographic view. I'm going to move
this light probes by holding the control
to snap by one unit. If we go to the inspector here, we can edit
the light probes. I'm going to edit
the light probes, and I'm going to switch
to the top view here. Basically we can by
editing the light probes, we can select each of the light prop we can move the
light probes individually. I want to select this light
prop in this area here, and then I want to press
a control D. I can duplicate this light
prop press Control D and then hold the control to move it to this position
here and I'm going to move this one one
unit on the z axis. I'm going to select
this one again, and then press control to duplicate again and
then hold control to snap and move the light probes and put it in this position. Also, this one,
I'm going to press control duplicate and then move it by holding the control. And let's select all
of this light probes. Hold the control and move
it to this position. We will have a light probes or distance three
units from each other, and we can duplicate
again and we can start filling our scene with
this light prob Press another control
and then duplicate and then move it by
holding the control, snapping, So I'm just pressing control the and hold control when moving repeatedly, and we will fill this scene in no time with this light probes. Let's just set this one and then expand this
to the side here. Okay. Now let's select this light probe here
and duplicate this. We don't need to
select this here because the player
cannot go to this area. Let's just select this light and then press control D again and then
start positioning. Basically, we are done
with our light probes, and if we go to the
front view here, we don't need to duplicate or we don't need to move the
upper light probes to higher position
because our player or the moving objects in the
scene is not very tall. As long the light probes cover the moving objects
area, then it's safe. Now we have the light probe, we need to rebuild the light. Let's go to the lighting tab here and then press the
generate lights again. And I'm going to
stop this video and continue when baking is done. Now baking is done and
we can just unselect lightbe it doesn't occlude our few here and save the scene. Now if we press play again, try to move into the area where there is
a light and we will see that our character affected
by the light of that area. And this is the light
probes effect and it's quite performance
because it doesn't add any
expensive calculation, such as a real time lighting if we go to this position here, you will see that our player will get a reddish
light because we have a lighting or red light on
that area as you can see here. Although the light
mode is set to bit. But with this light probes, it will still affect the
moving objects in the scene. Whenever we change the lighting settings
or lighting position, we need to update
the lighting data to update the light probe. And we are going to set also for the other scenes to set the lighting for
the other scenes.
48. 47 Preparing Club Hallway: Now let's continue to
prepare the other scene. And in order to do that,
let's create a new scene. And now we have the new scene. Let's go to the
point click folder. Inside the point click folder, let's go to the prefabs
subfolder and let's select the club hallway and
drag this to the scene. Now we have the club hallway. Okay. And let's delete
the directional light. And first, we need
to save the scene. So let's just save
the scene under scenes and let's just
call this club Hallway. And now let's enable the lights. I think is already enabled. Let's see. Yeah. Now we need to add lights
to the scene here. Now, let's create a lights
for the scene here. First under the club hallway. Let's create a new
empty game object and this will be the holder
of the lights group. Let's just call this lights. The next thing I want to
do is go to the lighting here and let's disable the autogenerate we won't have legs because sometimes with autogenerate we can have legs. Then I want to add a
light and this would be the point light.
Let's position this. I'm going to go to the
orthographic view, and I'm going to
align the first light to our light object here, let's move this here. If we go to the
perspective view, let's set up our lights
so we can see it better. First, I want to
increase the range, let's just test this 15
or ten should be fine. I want to increase the intensity and let's set this too big, and we can add shadows. Okay. And then we can duplicate
this light here and let's go to the top of you again and move it to this
position here, and then duplicate both of the light here
by pressing control and then move to the other
area of the room here. Okay. So now, as you can see here, we have the lights. And another thing I want to make sure is that it's
a bit dark here. Or we can just
disable the shadow. We have a very soft light. Let's decrease the range here. Maybe set this two eight. I want to change the
color to be slight yellow has yellowish orange tin. Another lights that
I want to add is a area light area if you have seen area lights
from the photo studios, and this is exactly like that. But it's only for big lighting and it can have a very
nice bleed lights. I want to change its
size here by selecting the point on the edges of the area light and
then move this to the top here and
increase the size. I want to change the
color to a reddish. So we have some redhled
on this area here. Now we have set our light Let's save the scene again
and go to the lighting. And here, make sure that we
have selected and lighten and set the indirect resolution to light map resolution to 20, light map padding,
and we can just use a lower size light map because the scene is
not that detailed, 256 would be fine. Make sure enable the
MB and occlusion and we can increase the
indirect contribution to. The default value should be one. Also for the big mixed lighting, make sure it sets to substrative
instead of shadow mas, because the default stings was shadow mas and set
this to subtractive. And we can disable the real time global elimination and
disable the sky balk also. We don't need that.
Let's just use a grace color or we can
change to a certain color. Okay. The next thing we need to do is we need
to add a light probes, and let's just add
the light props under the lights group here. Under the light, we
have light probes. Let's go to the front
autographic view and move this one unit up. On this. Now if we go to the inspector,
go to the top view. Let's duplicate or let's press the edit
light props button, and now we can select the
light prop press control D and then hold
control to move it. Press control D and hold control to move it and press control D and control to move
it, we have a snap. I want to disable the light
probes and move this it fits our hallway and edit
light props again, and then select the
side, the top area here, press control, and then move
it by using control to snap. Until we fill out
all of the hallway. But this part here, I want to make sure that it's inside the club hallway
inside the room, and this area, press
control and then move it and then press control
and then move it again. So, let's disable this and
move it so it's more centered. Yeah. Once we already
set up the light probes, we can go to the lighting
tab and save the scene, of course, save often, and then press
generate light here. And this will calculate
the baked lighting. Okay. Now we have this light, as you can see here. But we cannot see the
directional light at the area light
that we have here. I'm going to move this
to the wall here and make this bigger and
also make this taller. I'm going to generate
the light again. But before that, just
increase the intensity to three and then press
generate light again. And let's wait
until let's finish. Okay. Now we have a
reddish area here, but I think we need to move
this slide to the back because we have this color bleed and we have this dark area here. Let's just generate
lighting again. Okay. Now it is much better. And this is basically the scene here and make sure
we already set up aesthetic, but we don't want to
set the door esthetic, so I disabled esthetic
on this door here. If we go to the navigation menu here and we can access this inside the I believe
it's under the general. No, it's the Okay. AI under the AI navigation. Just press beak
and it will create a path for our
character to move. Now save this and
we can test this. Let's go to the prefabs folder, and in order to test this, we need to put the player first. But we also need to put a base canvas all of the prefep
that we use for the game. Let's just direct
the audio manager, the camera manager, and
also the pawn manager. For the Canvas to work
or for the UI to work, we need to add a event system. Let's just add one
and save this. Now if we play this, as you can see, we can
move our character. The sound is working right
away because we have the sound the audio
manager set up. We can go inside
to this room here. But later, we are
going to create a mechanism on how to open the door automatically
when the player is near.
49. 48 Preparing Office: Okay. So Hello, we are going
to continue our lesson. And on the last video, we are lining the club hallway. So now we are going to set the light for the detective
office, Wes Office. So let's create a new scene. I'm going to call
this we office. Or we can just call this office, and let's open the scene. If we go to the point lick folder inside
the prefabs folder, we will have this off pre just direct this
to the new scene, and this is the light has
been set up basically. All we need to do is
to bake the light. Let's disable the to generate. Let's just clear first. Let's set this to lighten and we can decrease the
light map resolution, two ten, make it faster, and we don't need a very
big light map size here. Let's just decrease this I
think 256 would be enough. We can just disable the real
time global elimination. The other thing we need to set is we need to set the camera. We want to have this angle. Once I've set up the angle
on the scene editor, select the camera and then
press Control Shift F, and we will have the
same view in our scene. Let's just this a bit and
reposition the camera by holding the control Shift
F and select the camera, we want to set the background to a solid color and
change this to black. Now, let's create
a light probes. I'm going to create a
new empty gave object, and I'm going to call
this light probes. And create new light
props under light. We have this light prop
group. Let's create that. If we go to the side view on
the x axis here and press the circle the middle
circle object here, we will switch few from the
perspective to orthographic. I'm going to move this
by holding control, so it will snap by one unit, and let's edit the light props. Let's select this
light props and press Control D to duplicate and then hold control and we
want to move on the x axis. And let's select this one here. Press control the again and then hold control, choose snap, and let's just duplicate again and then move
it only one unit. If we disable the
dit light props, let's just move the
whole light prop group. I fits with our s here. Then from the top view, Let's move this to the site
and press edit light probes again and select the
one in the middle, the whole row, and
then hold control D and then hold control to move the light probes
to the side here. Now we set up the light probes. Let's just pick the lighting, go to the lighting type again. If it's not there, just go to the window, I think
it's under the rendering. We have lighting
settings. This will open this panel here. Now I'm going to enable
the ambient occlusion, I'm going to set the
direct contribution to 0.5 and leave the indirect
contribution to one. And save the scene. Now
let's generate the light. I'm going to cut the video and skip it to the part
when it's done. Now, the lighting is done, but I forgot to delete
the directional light. Let's just select this
directional light here and delete this we shod have
something like this, go to the game view and Let's just regenerate
the light again. Yeah. This is our scene
for the introduction, and we will set up some sort of a cut scene in
the scene here. Now let's just save the
scene, and I next video, we are going to continue
to light our last scene, which is the part that will
be off for this video. Okay.
50. 49 Preparing Park: It Now we are going to set up the last scene of our
game, which is the park. In order to do
that, let's create a new scene and go to
the point click folder. Under Prefps we
have a park prefs, and this is the whole scene, let's just direct
this to our scene. And now we have this here. First thing, we need to
adjust the camera position. I'm going to set
the camera to be around this position
and we want to disable the autogenerate it doesn't keep on generating the
GI or the lighting. And let's just set the
camera to disposition here. Once we are happy with our view, the set our main
camera and then hold the control shift F and
it will reposition. But as you can see here, if we go to the
game view, we have this empty area shown here. We need to make sure that
our camera is closer. Let's just change this to local, and then we can move it on the z axis and move it to
the side here, rotate. I'm going to also rotate the camera and then move it again. Another thing we can
change is we can go to the inspector and we can change the field of
view to a lower value. So the camera is not as
wide as it should be, and we can lower
again to around 45. Now if we go to our scene view, we will have a how
view of our park and it doesn't show the
empty area over there. Okay. Another thing we need to do is we can just delete our default directional
light from our scene here, and then select our camera and we want to change
the background color. Let's just set the clear flax
on the camera component to a solid color and then change
the color to a black color. And let's set up the lighting. Open the park prefabs here and under the lights, we
have three lights, first one is the
directional light, and we want to make this
slightly darker, darker bluish. Then we want to change the
bake for the point light here, we want to also change the mo and we can just
enable soft shadow. Also for the directional light. Let's just it's
already been enabled, and for the light
on the fire here, set this also to bait and set the shadow
type to soft shadow. Okay. Or we can just
disable the shadow because it doesn't look very
good here, as you can see. Now we have set up all
of the lighting here. Press apply for our part, and to save this
back to the prefabs. Now I'm going to create
a new light probes, and this is for shading the
moving parts of the scene, which is the character, and let's reset the value. If we go to the
ortographic view here, I'm going to move this
by holding control, one units up. Okay. And let's just press edit
light probes and select the bottom light probes and move it to line with the ground here. Let's duplicate the
side light probes here. I'm going to first move this
so it covers the area here, and this one, move
it to this position, then select this one
and I'm going to to press control D to duplicate
and hold control to move it and press another control D and select this one and press control to duplicate and then
move it by holding control. Okay. And then
from the top view. We are going to move this
p to the side here and this one to round this
position and then press control to duplicate
and select this one, press control D to duplicate. Yeah, I think this should be enough and we want to save our
scene so we can bake this. Let's save this and go to scene, and then I'm going to
save this as part. Now that we have created
this light probe. Let's go back to
the lighting panel, and I'm going to change the
light map to enlighten. I'm going to decrease the light
resolution to around ten, and the other should be okay. The other setting
should be okay. For the light map size, I'm going to decrease
this to five 12. Then we want to enable the ambient occlusion and set the direct contribution
to around 0.5. Safe again, and let's
generate the lighting. Now the lighting is done, and let's check this
from our game camera. But as you can see, it's b bright we want to readjust this, open the park game object
and under the light. We want to lower the intensity
of our directional light. Let's change this to around 0.5 and then generate
the lighting again. I think it should be enough
and let's save this. Now if we drag the player, go to the prefex folder on the asset one and drag the player, as you can see here, we don't
have any real time lights. If I select all of
the lights here, you can see that all of
the light is baked but the player will get lighting information from this
light probe group. If I move this, it's
getting more reddish, and if I move this further away, it's getting more yellow
because we have a fire here. Here, it's a more
darker lighting. This will eliminates
the moving parts or moving objects on
the scenes. Very nice. One thing we need to add is the taxi and I
forgot to put that, so let's just go back
to the point click folder under the
models. We have a taxi. I'm going to direct
this taxi to be the child of our
park game object. I want to enable the
static on our taxi. And let's move this on
this position here, and if we check it
on our game scene, we want to see more of the taxi. Let's just move it here. Now we have this.
We want to make sure that later the player will arrive from
this taxi position. Now let's go back to
the lighting panel, save the scene and
regenerate the lights. Okay. Now it's done. We have a slide
shading on our taxi, and let's just delete the player and we're going
to set up the scene later. Now, save this and we have set up all of the scene
for our game, and in the next video,
we can start designing our gameplay based on the
story of our game. Okay.
51. 50 Office Cutscene: Now we are going to
start to develop our games and let's start
with the cut scene. I'm going to save this
park scene here and go to the scenes folder and I'm going
to open the office scene. First, we already have
adjusted the ama, let's just start creating the asset needed for
this cut scenes. The first thing we need to
do is we need to create an empty and let's
reset this empty. I'm going to rename this
two opening timeline. And let's open the
timeline window under sequencing,
choose timeline. Let's direct the
timeline window to the bottom panel area here. Let's create a couple
of anti game object, and this will be
the first dialogue. Let's just call
this dialogue one, then we are going
to copy this later, but right now we are
going to set this and we want to create
another anti game object, and this is for changing
change scene Okay. And we need to put
the UI prefabs that we already prepared. Let's go to the prefabs folder, the one that under
assets folder, and let's drag the data
manager. Put it on top here. I'm going to drag
the base canvas. And we need to create
an event system. Let's go to the UI here and then create
a new event system. We are going to also need
to put the character. I'm going to go
to the pref under the assets, tiny people, T and then under the pre direct the male B to run this position here and we're going to
set up this later, let's direct female
and put it here. Okay. Now we want to adjust the
position of the character. I'm going to go to the
orthographic view and then put the female character over
there and for the male, I'm going to put it here. Let's say this. Let's put the character below
here below the timeline, so it's easier to access. We want to scale the scene, so let's just select the
office object and scale this to be 1.2 on all access. So we have a bigger scene. And we are going to
set up the timeline. Let's just select the
opening timeline here, and then under the
timeline, let's create a new timeline file. Let's save this under the
assets and we are going to create a new folder
called timeline. Let's just call this
opening and say, now we have this timeline here. We can create a couple
of groups first, so let's just create
a new track group and this would be the character. The next one would be
a script or event. And I'm going to also create a new environment track group for grouping the animation here. For the character, we want
to add an animation track, and let's direct
theme character here. And we can put an
animation over here. So let's just add from
animation clip and we can pick here and let's
search for the mail set. We want to use this one here. Now if we play this, you'll
see that it has animation. And we want to set this to
start from the first frame, but as you can see here,
the position is not, if we try to move the character, as you can see that when we go back to the
opening timeline again, it will snap the position. So to fix this, we can move the
offset here under the t under the mail animation
clip in our timeline, and we can adjust the position. I'm going to adjust
the z position. So it sits on the chair and move the y position, something
around like this. So if we see in the game view, we will see it sits
nicely on the chair. Okay. And now we want
to animate the female. Let's just select the female,
sorry the female one. And under the opening time line, let's create a new
animation track. And this would be the
female character. And we can add animation
from clip and let's select the female walk. Now we have this walking
animation, as you can see, and I'm going to put it here and I'm going to scale
the walking animation to around 150 frame. We have a longer clip playing. Now we want to set the position, the starting position
around this position here. Here, as you can see
it holds a knife, and we want to hide that knife. Let's just select
the female object and select the knife game
object here and hide it. Okay. And now we have a very
nice walking character. And now we want to also
rotate the character. So let's rotate this 180
degrees on the y axis. So it face the detectives. And we want to create
another animation track, and let's drag the female here. And we want to create
a new animation. So let's just hold the record button and move
to this position here. And we can move the female object to around
this position. Okay. Yeah. So we will have a we need to also key the first frame, so
let's just move this back. Now we have two
key frames as you can see here, and
if we scrub this, you will see that it's moving and we want to
move the first key frame. It we can just this recording and we can double
click on the track and it will open the animation
track set to finish quicker. Let's just move the keyframe to this position
here to 2 seconds. And then go back to
the timeline here. And for the other track that
have the female work clip, we want to add another
animation clip, and this would be
the female talk set. Now we want to move the
female toxic animation clip to overwrite or to transition
with the walk clip here. I'll just move this and
overwrite slightly. Now if you see
here, when it stop, it will start
sitting and talking. We want to set the
correct position. Let's just select this
clip here and enable the record button on
the animation and then let's move the
female character. Sorry. Let's select the timeline
again at this position here. Let's just enable
the record button on this timeline here. Now let's reposition
the character, not the opening timeline, but select the female character, and then move this to the
site here on the x axis. And also move this
on the y axis. It sits on the chair. Now if you play this, it will stop and it will transition to
the sating animation. And also, we are
correcting the position. The female character sits
on top of the chair here, and we can just stop
this recording, and as you can see, we have
a very nice animation. If we go to the
game view here and disable the base
canvas for a while, let's save the scene
and then select the opening timeline
and we can play this. And you'll see that we have
a very nice animation. Next, we are going to create
the dialog for the scene. Now we are going to
create the dialogues for the opening sequence
and also an event to change scenes once the
sequence is finished. In order to do that, we need
to create a new shop script. Let's go to the scripts folder, and let's create a new
interruptable script, and we are going to call
this a trigger Sorry, start interrat now we can
just open this script. Now we open the script.
Let's start creating the variable that we need
to make this component. Basically, we need to create
a new serialized field, and this would be
a type of actions, and it will be an array and we can just
call this actions. Another option we
need to have here is a bulon This is
for hiding message. We want to hide
message on disable, and then we can just
delete the start and the update method since we
are not going to use that. Basically, we need to
create a new enable method, and also on disabled method. For disabled method, we are going to check
if the hide message. If the hide message
on disabled is true, then we want to access
the dialogue system, instance, and then we want
to hide the dialogue. For the nenable we
want to run an action. We can use, not the
editor extension, but the extensions that we've
created, the static class, and we can use the
run actions method and we only need to pass
the list of actions, the array of actions
that we have here. I'm going to pass the
actions and then close this. Now we are done with
this component here. We can start using
it in the editor. Let's go back to
the Unity editor and access the
timeline by selecting the opening timeline gain
object and we can see all of the previous timeline that we already set up
with our character. Okay. And now we can create
a new activation. So basically, activation
track is for activating object and we can drag
the first dialom here. Okay. And we can move this
dialogue to around the position where
the women sits, and then we can
start the dialogue. Here, for the first dialogue, we want to add the start in. Then we want to also use the message actions
and add message, and we can type a message here. And for the first message, I'm going to type. I've already prepared
an opening dialogue, so I'm going to copy this line
here. And paste this here. Then the next thing we can do is we can duplicate
this dialogue game object, and this would be
the second dialogue. Go back to the opening
timeline here. We can create a new
activation track, and then we can move this around maybe this position and drag the second dialogue. For the second dialogue. We want to change the message
to the second message. I'm going to grab from
this document here, and then delete the first
message and then paste the second message and
basically we did four dialogue. I'm going to move this, so it's more tidy and change the name to
the third dialogue and this will be the
fourth dialogue. For the third one, I'm
going to just copy the fourth dialogue here. Okay. Okay. And paste this and for
the fourth dialogue. Let's just copy the last
dialogue that we have over here, and then go back to unity, delete this and past
the last dialogue. For the change scene, we are going to also use a
start in and add action, one, size, and we can use the change scene
action and direct this change scene action
to this position here. For all of this dialogue here, we can just select all of them. Then we want to make sure that we are hiding the
message on disable. For the change scene,
we are going to go to the I believe
will be the C scene. Let's check this.
Yeah, C. We want to start the game from
that scene, type the C. And then on the timeline, we can access the
opening time line again and we are going to add those game object as activation track and going to add three different
activation track. Now we are going to direct
the third dialogue to this here and the fourth
one to this slot here, and the change scene
should be on the last one. And we can trim the dialog
timeline here and move this and we need to make sure that our timeline can cover all
of this dialogue here, and the player can read it so
it should not be too fast, but not too slow also. Once it's finished, we
want to change scene, so we can just use this for
changing scene in here. I'm going to save
the scene. Then in order to test this,
we need to play this. But another thing I
want to do is I want to create an
animation track Okay. And this will be the fan that we have here on
top of the office. So let's just expand the office, and I'm going to direct the
ceiling fan game object and direct this
to the slot here. And I'm going to eimate fan, and then I want to
rotate the I believe it should be the ceiling
fan game object and should be the rotation. Let's just set
this back to zero. Then at the end of the timeline, set this maybe around
three 60 by ten. Yeah. So it rotates very fast, and we can just
Now we have this, but in the first frame, we need to chase this
pac to zero, I believe. So we have rotation. Let's just top this
and let's just access the clip here by double clicking it and we can
just adjust the value here. So the first rotation value. Let's check this Okay. Okay. The y should be zero. And then we can change this to maybe around 3,599. Okay. As you can see, we have issues, so we need to delete the key
and then set a new rotation. Let's create a new key here at key and set
the rotation value. It's not y, and
it should be the. We are going to
rotate the z axis to 30005991 angle less than 3,600. And it's currently not rotating. Let's just enable the
record button again. If we zoom this, we
have two keys here. We are going to
delete the first key and it should rotate. As you can see, it rotates. Then we want to make sure
that the animation is linear. If we zoom out again and
then go to the curve, we can select all of the
curves here and click. And set the left tangent or both tangents
should be linear. It should be linear,
and go back to the sheet to the dope sheet. If we play this animation, it will rotate linearly. We have that working
and now save the scene. Let's test this by
playing the scene. Okay. So it will sit and
when it's activated, we forgot to activate
base canvas so let's just reactivate the canvas and
save the scene again. And now let's play this again. Okay, there seems to
be an issue here. Okay. We don't have
theta manager. Let's just drag from
our prefs Okay. We do have data manager, but I found the issue. Basically, all of
the child object that gets activated by
the time line here are enabled on start and we
need to disable that so it doesn't trigger any
script actions, and let's just leave the enling of this game
object by the timeline. Also, we need to
set the dialogue. First dialogue, I'm
going to direct the message action
to the actions here. Then I'm going to add a line here and let's just call
this grieving wife. Okay. And I'm going to copy this here for
the third dialogue. I'm going to also add
that information. For the second dialogue, we want to change this to will, for the fourth one, we also
want to change this to will. This will inform the player which character is
currently speaking. For the first dialogue, we already set this as an actions. For the second one,
we need to direct the message action as the actions that we want
to trigger on start. Same goes with the third one
and also the fourth one. So now we can save
the scene here and let's run this again. Now we have this
dialogue, as you can see. Okay, there is an issue
with the second one, and let's check for a timeline. Okay. So basically this here, so show the first dialogue, and this is the second dialog
If we play this again, somehow This is the first one. Maybe it's overlapping,
it's best to at a slight distance between
dialogue, like this. Now let's save the scene and
then run the game again. Okay. This is first dialogue. As you can see, when it's it loads the scene and then it goes to the first scene that
we supposed to play to. That is basically how to create the cut scenes in
this adventure game. Of course, we can always adjust the
timing of the dialogue, so the player can
read this better. I'm going to adjust this later. And, that's all in
the next video, we are going to start building our gameplay on other scenes.
52. 51 City Gameplay: Now we are going to
work on the C scene. Let's just open the C scene
here under the scenes folder. Let's go to the CN view here. Now we are going to create a bouncer that will
guard this door. Basically won't be able to enter the club
via the front door, so we have to find a
way how to get in. Another thing we need to do is we need to add the
audio manager. Let's go to the prefabs and
direct the audio manager. And let's test this and see if there are any other prefep
that we need to put. Okay. So basically,
we have this error. It asks for a cursor script. So under the base canvas here, let's just check this error. Okay, I asked for
a camera manager. Let's just go to our prefabs folder and
direct the camera manager. Let's just put all
of the manager on top here so we
can see it better. I'm going to also direct
the data manager, put it on top here
and save the scene. And let's press play again. Okay. Now, we don't
have any error. First, we need to create
a new empty group, and let's just zero
out this position, and let's just call this
incles we are going to put all of the interactable
objects under this antigua, the bouncer on the scene
here and let's create another anti game object
and reset its value, and let's just call
this characters. And I'm going to go to
the two tiny people and let's go to the prefix
folder and drag the male A, and
let's put it here. Under characters, sorry. Let's create a new game object
under this male object. Let's put this out here
and drag the male object, and let's just call this
PC underscore bouncer. And let's rotate this, set the y rotation two zero, so it's facing forward and then let's wreck the bouncer to be the child of this
character game object. For the bouncer, we want to add a intertle In
order to do that, we need to add a
collide add a capsule. Let's just adjust the collider, so it fits with the character. Okay. And then we want to also add a
action, an intractable. And let's choose a sprite cursor and let's use the pop cursor. So whenever we are
over this character, our mouse will show
this pop cursor. And then we can add an action. The first action that
we want to add is the message action and the other one would
be the audio action. Let's add a couple of
action. For the message. Let's just say that. We can add a name bouncer, and then enter and
then we can say you're not allowed to enter. Then we can add an audio clip
and under the audio folder. We will have this dialogue, and I'm going to use
the mail C dialogue no. This is for rejecting, let's track the message action to
the first action slot here, and the second audio action
to be the second actions, we are going to create an
animation for talking. Let's just go to the
animator folder, and then let's create a
new animator controller, and let's call this bouncer. And under the bouncer object that have the animator,
the child object. Let's direct the bouncer
to the controller, and let's open the imator
we can put the idle. Okay. And for the idle. Let's just use the I think
we have this male d A. Let's create a new states do we have the talk only? This is t. We can just
try to use this later we can search for animation that have a
talk while standing, and let's create a trigger. The transition and
for the transition, I'm going to create
a new parameters and add a trigger and
let's just call this talk. I want to trigger
the talk animation whenever this triggers
gets triggered. Let's just add a condition
and add the top condition. And we want to make another
transition from talk to back to idle and enable the exit time and
for the idle to talk, we want to disable
the exit time. Now we have set up the animator. Let's go to the object of our bouncer here and
then add the action. And let's create an
animation parameter, and we want to trigger
this talk action. So let's just trigger Paul. And then under the bouncer, let's strike the child object
to the lowest actions here, the third actions, and it will grab the animation
actions right away. Let's save the scene
and let's test this. Let's click on the
bouncer and it will go to the bouncer and then the SM. Okay. It it shows this message and then it play the animation, and it also play the
audio, as you can see. Then under the interactable, let's just create a
new empty game object, and let's just call this front door front door interaction. Let's create a new box
collider. This is the object. I'm going to put this around here and I'm going to rec the
collide it fits our door. And let's just push this back, it aligns with our door. Then let's create
an intractable. We want to make sure
that this is look only, so the player won't
approach this object, and then let's choose the icon
and then for the actions, let's add an actions and
add the message actions. And let's just type the front door is
guarded by the bouncer. Then direct the
message action to this action slot
here, save the scene. Now we have set up
the front door. Now we want to create
the side door so we can transition to the
club hallway scene. Now let's create the
side door interaction. First, we want to create
a new empty game object. Then let's put this game
game object to be on the side door here.
Something like this. We want to make sure
that the z rotation is facing to the
current right rotation. We need to change this to local and then
let's rotate this. So now we have the zero
rotation is facing here. Then let's call this side door. And we are going to create a
couple of empty gamock here. The first one will
be the no password. So if we haven't found
out the password yet, then we want to
show this message. Let's just add a box collider let's just
adjust the box collider. Okay. And then let's add interactable and we
want to use the top icon. And for the action,
we want to add a message action and we can
add a couple of messages. Direct this message
action to the actions, and we can type for
the first message. And for the second
one, We can type door, and then we can type
what is the password, and for the third one, we can type and then we
can type chicken steak. Then we add a new message
for the fourth message, we want to set the
door to reply. Nice try. This is for the dial up when we don't have
the password yet. Let's create a new empty game object under the interactable. And let's put this here in
front of the wall here, and if we zoom in, you'll see that
we have some sort of a writing on the wall, so we want to put it there,
this empty game object here, and add a box glider. And let's just make the size
smaller 0.6 on all axis. Then we want you to
set this to a trigger, so it won't block
the player movement. Also add the interactable
and let's just use the icon, and we can add two actions. The first one will be the
message action to show the password. Let's just type. Okay. You you saw a writing on the
wall and add the message? The password is H L. It's help. With this password shown, we want to activate another game object using
the activate actions. Let's just direct the
message action to the first slot and the activate action to the second slot. Then we want to activate a certain object and the object that we want to activate
is basically this here. We have this no password and
then we have the other one. Let's just call this password. And for the half password, we want to reply with the correct password that we already read,
which just help. And then we can just type this. Let's modify our
message actions and right take on the
message actions and then open the script. And we want to create
a new default actions. Let's just add a chain actions. And let's open the message
actions editor. Okay. Let's go back to
Unity and then open from here under script editor. We have this actions
actions editor. Under the inspector GI, before we are showing
the dialogue here, we want to draw a
default chain actions. Let's just use the
editor extensions and then actions array. We need to pass a
serious property array. We need to create a new actions. Let's just call this
score chain actions. Let's just copy this here, and we can use the chain actions variable that we've created and search for the
chain actions. Let's just copy in variable name from our message
actions class and then paste this as defined property
parameters. Save this. Now we want to draw the
chain actions for the label, we can just type chain actions. And then close this
with a semicolon. Basically, we have
this default chaining actions for the message
actions component, and if we go back to unity, you'll see here when we select any object that
have the message actions, it will have this
chain properties. Another thing we need to
fix is we need to open the dialogue system
component that we have the dialogue
system and we need to create another show message. So basically, we need to create an alternative of
our show message, and let's just call
this show messages, and we are going to pass
a different parameters. And basically, we want
to pass the message. And then we want to
pass a list of actions. And just call this chain actions
and set the fault to no. Here we want to show
the multiple messages. For the multiple messages, let's just add
this chain actions equal as a second parameter. Here below, we can check if we are not
using any dialogue. And the message we are
currently at the last message. Then we want to run the
actions that we have. So let's just check if
chain actions is not, then we want to use the extensions method
that we created and let's just use
the run actions method and pass
the actions here. Okay, since this is a list, we need to convert
this to a array. Let's just type to array, and this will convert our
list of actions to array. So it is compatible for our
run actions. Save this. So let's just add an action. Here, we want to use the
change scene actions, and if we go to our scene here, let's switch to
the club hallway. Let's just type the scene name, which is club hallway and
then drag the chain action to the chain actions under the message here and save this. We want to disable
the half password first and we want to enable
the no password one, and let's just call the
writings on the wall. For the writings on the wall, whenever we are clicking
on this interactable. We want to show the message, and then we want to add an entry for the
activate actions, and we need two entries. Basically, we want to
disable the no password and make sure this is unchecked and then for the half password. We want to enable this one, we need to check this checkbox. This way, it will disable the first entry and then it
will enable the second one. Let's just save this
and let's try this. Okay, so let's go to
the position here. And right now, we don't have a switching camera mechanism, so we need to add that to
make this easier for testing. So let's just create
a new camera. We can duplicate from
our main camera. And let's just call
this side door camera. Okay. And let's change the camera to be something
like this should do. Select the camera and then
press Control Shift F, it will align this camera here. We want to disable this camera. Then for the camera, we want to add a trigger area that will trigger the camera
here, camera changes. So let's just create a new
empty game object and then add a box glider and set the size to run five
by five by five. Okay. Basically, we
want to set this area. Whenever the player
enters this area here, we want to switch the camera. Let's just make this slightly bigger and enable
the trigger options, and then we want to
use the change camera. We want to use the
switch camera actions, and let's just direct the camera to switch to
the second camera here. Okay. So if we select
this camera switch, let's just call this
side area camera switch. Let's add a new switch
camera actions, and this will be
the main camera. So we need to set a two
switch camera actions, and then we are going
to use a trigger in. So let's just move the
trigger to above this, both two switch camera actions. And then we want to
trigger by the player. So change the trigger
tech to the player. And then when we enter, we want to trigger the
search camera action, the first switch camera actions. And whenever we
exiting the area here, we want to trigger
the second action. So let's save this, and now let's test this. Okay. So if we go to this
position here, once a player enter, we should see that we are
switching the camera. Now let's try to click on this password and
then press. Okay. Now once we already
see the password here, you'll see that the
interactables will have changed the child
activate status. Let's just test this
one more time. Okay. Let's go to this area here, and then let's try to talk to the person on the door here, and then it will ask
for the password, and we will answer it wrong, and it won't open the door. So once we saw the
writings on the wall here, it will change the child object
to the half password one, and if we press this, Okay. And then when
we click. Okay. So we already have this, but somehow it doesn't trigger
the change scene action. So we need to modify the
message action square. So under the message
actions, Okay. We want to use this if we are enabling the dialogue, then we
want to run this one. But if it's not, then we want to run
the dialogue systems, instance, show message,
and then we want to pass the message and also
the chain actions. Let's save this. Now
let's try this again. Let's go to this area here, and then let's
read the password. Okay, sorry, there is an issue here. We already showing the message, but I believe under
the dialogue system, we already created a new one, but we aren't
showing the message, so we need to run
the routine here. Then let's just pass the Let's pass the
current message the message to our
current messages. And let's just set the
dialogue to falls, and we want to pass the chain of actions that we have here and close this
with a Semicolon. Okay. Let's test this. Okay. Okay, let's go to the
position again here. And then let's see the password. We need to check this. Our password has
a message and it doesn't have a chain actions. Let's fix a couple of things
on the dialogue system here. The first thing we need
to fix is this here. We need to test whenever we are finished showing all
of the messages, so we should remove the negative one under the statement here. It will finish
showing and then it will run the chain actions. For the show message, we need to basically
reset the ID and then disable the
button dialogue. I'll just copy two lines here. And paste this here. And we need to also
enable the panel. So let's just copy
this panel set active through before showing before starting the coroutine here. And let's save this. Okay, now let's test this again. Okay. And let's go to
the area here and let's talk to the door and
it will ask for a password, and we haven't read the
password on the wall yet, so it will answer
the wrong password. And then let's go
to the site here, and then it will
sew the writing. And once we know the
correct password, we can talk again to the door. And we will answer
the right one, and then it will open the door and then it will run the
change sheen action. But since we haven't
put the club away to the build settings,
we need to do that. So let's just go to
the build settings, and let's add the
club hallway scene to the scenes in built here, and scene, save
the project again. Now if we try this,
it should work. So let's go back
to the side area here and read the password
and then talk to the door. And then as you can see here, it changed the scene
to the club hallway, but we are going to also set up the club hallway
on the next video, so the player will be starting from this
door position here. Okay, so yeah, that will be
for the CD gameplay, set up. And later, we are going
to also add a taxi so we can go to the park from
the CD scene here.
53. 52 Additonal Item Database Setup: Okay. Now in this video, we are going to set up
the items that we need to set up in this game, and let's go to the data folder
and under the atatabase. Let's create a couple of
items that we need to create. First, if we go to
the sprites folder, we will have this item
sprites from our assets, and this is the
item that we have. Let's just create those
items accordingly. Go back to the data folder, select the item database, and let's change the first
one to a portrait. Okay. Oh. Portrait, and then we can
set the item description to a portrait of the
missing person, and let's disable the multiple. And then we want to
select those sprites, but we need to set up first so let's go to the
sprite folder under the items and go to
the sprite editor, and let's just drag a box
on each of the item icon. And then sorry, once
we finish dragging, all of them, we can select
one by one and then press the trim
button on top here. So it fits nicely and
then press apply. Okay, let's close
the sprite editor. Go back to the data folder
and sack the item database. And for the portrait,
let's select the sprite, which is this item seven here. And the key, we want to
change this to a key here, let's add a couple of items. And this will be a money. And we can call this 50 bucks on description for
the items right, let's select this money here. The third one should
be a name card. Let's call this a name card
with an address on it, and then select the name card for the last one, this
will be a lighter. And let's just put it a small guess lighter on
the description, and then change the
icon to the lighter. Okay. And this will
be all for setting up the item database and let's
just save the project. Okay.
54. 53 Club Hallway Gameplay: Okay, so now we are going to continue setting up
the club hallway. And first, we want to change
the direction of the player. So let's just set the
rotation of the player on the hit X to be 180 degrees. So it face forward here and
move this slightly backward. And for this pawn manager, we also want to set this pawn from city to this position here. So let's just drag this. And then for the direction, let's set the x value to zero, and then sorry, set x
value to one positive one. So it should face
this red arrow here. Yeah, something like
that. Let's save the s. Now we forget to add
the data manager. Let's go to the prefabs
folder and then direct the data manager to be
below the main camera here. Save this. First,
if we press play, then you will see
that whenever we are moving to the room here, the door doesn't interact
with the player, and it stays opaque,
this wall here. We need to make sure to
address those issues. The first thing we can
do is we can select the door here and select
the parent object, which is the pivot. For the pivot, we can
add a rigid body, and let's disable the use
gravity so it doesn't fall. And then add a hinge joint, for the hinge joint,
we want to set the axis on the y axis. Let's set up the x
value two zero under the axis properties and
then set the y value one. Then we want to increase
the spring value. Let's just use the spring value. I found that the value of 15 should do or 20
would be good enough. Then for the damper, I want
you to set this to five. We will have a spring
door and it will close automatically whenever we get passed through
this door here. And we want to use the limits. But in order to set the value, we can just enable the added
joint angular limits pattern here, and we can direct this. Basically, we want to direct this area around this
position here, this angle. And for the other
one, we want to set around this angle here. This would be the
limit of our door, and this is the area that
the door cannot rotate to. Now, let's just disable
the joint angular limits. And then under the
child object door, we want to add the box collider. Let's add the box collider. Basically, in unity, all of the box collider under
a rigid body component, will be considered
as its own collider. Unless we add
another rigid body, then it will be a
separate physics entity. But if we only add a collider, then this collider
will be regarded as the body of this
rigid body here. Why I put it on the box? Because when we put a box
collider on a three D object, it will automatically resize
it to that object here. You can always put the box
caldor on the parent object, but you will need to set
up the dimension manually. So yeah, under the child
object here, the door, I want to modify the box caldor pressing
the end collider, and then move the bottom
area slightly to the top. So it doesn't friction
with the ground object. And yeah, so basically we can save the sceam
and then let's try it. Now if we try to go
inside the scene here, the pair will push
the door and the door will open and then we'll
automatically close. Same goes when we goes out, it will open and
then it will close. We want to do that also
on the second door here. So we'll need to do the
same at a rigid body. And then also disable the use
gravity at a hinge joint. Then we want to set the xis to one on the y axis and
zero on the x xis. Then enable the use spring and I'm going to set this to 20 and the dam pert five and enable
the use limit options, but we want to add this joint by using
this button here and set it to approximately
the same value as our previous door
and then disable this. We want to also add
a box to the child. And let's add the collider, so the bottom area doesn't
friction with the ground. Something like that. Now we have a working door. Now we need to select
the main camera, and then we want to add
a transparent effect. In order to trigger the
transparent effect, we need to add the
transparent trigger component into the camera and
I've already done that. But if you haven't, you can just add a component search for the transparent trigger and then add those components
to this camera here. For the wall object, we can add a box collider. To the wall object, and this will create a new box
that covers the wall. But if we disable the trigger, then the pair won't
be able to move through this wall here. We want to enable the trigger, and then we need to add
a transparent effect. For the object that we want to able to change to a
transparent material, we need to add the
transparent effects, and then we can choose
the transparent material. And we already create
the transparent material here under the materials
folder. I'm going to use that. Basically, the transmarent
effects will change all of the game object and also the child object under
this toilet wall here. Let's go to the
other wall here and then add a box collider, and then enable the
isrier and then we want to add a
transparent effect and then choose the
transparent material again, and let's save the seed. Now let's test this and
see if it's working. If we go to this room here, you'll see that the
wall will change to a transparent and we can see the player inside
this wall here. If we go out, then it
will switch back to the previous material.
Same goes with the toilet. Okay. Yeah, this is for
the transparent effect, and let's add a couple
of NPC characters. Now, let's add the characters, the NPC characters, and
in order to do that, let's create a new
empty game object. Reset its value, and let's
just call these characters. Yeah. And then let's go to the two tiny people folder
and go to the preface folder. And let's direct
the police officer under its character here. And let's put this somewhere
around this position. And I'm going to put it here. And I'm going to
make sure the police face the other way around. So in the game camera, we should be able to see
the police character. So let's just move this
on this position and then rotate this on the y axis slightly to this direction here. So we can see the
police officer. And then let's drag
the other person here. Sorry, male A again, and then put it under
the characters. And let's put it inside
the toilet here. And let's hide the baseball
beat to activate this. And then let's put it around this position and
let's rotate the character. And I want to create an empty game object parent
for the character here. So I'm going to create a
new empty game object. But then I'm going to direct this to be the
child of the characters. And let's call this guy in the toilet and direct the male H to be the child
object of antigamobject. And for the police,
we can just use this and let's just rename
this to security. And let's hide the gun object. So let's just select the hand
gun and then disable it. So let's set up this character,
these two characters. And the first police officer, let's add a capsule glider and let's adjust the
size, so it fits. Okay. And then we want to add the
interactable component? Let's just add that and then use the talk cursor because we want to be able to
talk with this person, and this indicates that. And we can add two actions. And the first one would
be the message action. We only need one actions and then direct this message action. And we can add a couple message type for
the first message. We can ask this security. Have you seen this person And for the answer, we can just type security. And then we can just reply with. I haven't seen him around. And we want to add 18 actions whenever we
finish talking with this. Security. We want to
add a chain actions, and for the chain action, we want to add the
activate actions. Let's just dd this actions
as the chain actions. And whenever we're conversing
with this security guy, it will trigger this action. Let's add a couple
of two entries, and we are going to
enable and disable some of the game object under
this guy on the toilet. Let's just create
the response first, and let's create an
empty game object. Let's just call this
normal response. Okay. And add a capsuler let's adjust the size
of the capsuler. Okay. And then we want to add NPC, but the incable component, and let's use the
top icons also. And for the actions, we want
to add a message action, direct this message actions, and then add new message. And then we can just type guy in the toilet and we can let him
say what are you looking A. Okay. So basically,
the normal response is the default response if we haven't talked to the
security guy yet. And then we want to duplicate
this the normal response. And then let's just change
this to talk to security guy. And we want to create
a different message. So we can just show message that somehow the guys overheard our conversation
with the security guy. So let's just say
that I've heard your conversation
with the security guy and then add new message. Let's just say I believe you will find the
person in this address. Okay. So basically, we want to make that the
toilet at the guy, you will give us or give
the player name card whenever we finish talking
to this guided toilet. So let's just add an item actions and
direct the item database. So direct the item action
to the chain actions. So whenever we finish this, it will give items. So let's just select
the name card. And then we want to add
a new actions showing a message that you got a name card with
an address on it. And we want to show
this message actions together with the item action. So when the player are
receiving this name card, we want to also
show this message. Okay. So we want to disable
this game object here. And for the security guy. We want to switch the active status of this child object with
the guy in the toilet. So for the normal response,
we want to disable this. So let this game object unchecked and for
the top two security guy, we want to enable this, so
let's just enable this. And since the guy in the toilet have a two different response,
we want to save this. So let's just add a safe entity component and it will automatically
create the incense ID, and we don't need to change this because this
ID will be unique. And once we talk to
the security guy, we want to also create another response
and receive card. And we can just type you'll
find him on that address. And we want to make
sure this is disabled. But when we are finished
talking with the security guy, we want to add a
activate actions, and then we want to
disable this one. So we want to disable
the game object itself, but we want to enable
the receive card. Okay. Actions. And
then we want to add this activate actions together with the message actions
and the item actions. So it's get triggered, and then it will switch
to this response. And all of the child
o active status, this checkbox here, we'll get
safe with this safe entity. So if we load the games again, then this will get sloaded the last state of
the child object. So it will have
different response depends on our progress. Save the scene here. And now let's save this
and let's play this. So let's try to talk to
the guy the toilet first. As you can see here,
it shows the message, what are you looking at because we haven't talked
to the security. So if we talk to
the security guy. So if we talk with
this security guy, let's talk security guy again, and it will tell us
that this guy haven't seen the person that
we are looking for. But if we go back to the
other guy on the toilet here, It will have different
response than before. So if we talk with him, it gives us a name card. As you can see, we have this name card
with an address on it. And now if we talk
to this person, it will respond to the
last response here. It will enable the
last response. Now we've already set up the gameplay on
the club hallway. We want to test if the
safe is working or not. So we want to make sure that
whenever we save the state, we will save also
the child status or the child object that are activated for the
guy in the toilet. And since we have
the safe entity, to the guy in the toilet, we should be able to save this. In order to enable the safe
load on our base Canvas, we have the debug safe load. But both of this button doesn't have an onclick
listener added to it. Since the load and save
are on the data manager, when we save the base Canvas, those settings does not
get saved together, since it's on a
different object. In order to test
this, we need to direct the data manager to the on click and then we run the function save data entry. For the load button, we want to also direct
the data manager, and then we want to run the load data entry and we can just set the ID
two zero for now, and this will be useful
if we are setting a multiple safe entries later. Of course, for
saving and loading, we are going to do
this in the main menu, not in the game,
but since we want to test this using
the debug button, we are setting on this
button here right now. Now let's save the scene,
and then let's test this. Right now, if we press play, and then if we try to talk
to the guy on the toilet, it will ask, what
are you looking at? And if we talk to
the security guy, if the security guy have seen this person on the portrait that we have on our inventory, it will response sorry,
haven't seen him around, but when we talk with
the security guy detailed object of the
guy on the toilet change. As you can see here, it changed
to talk to security guy, and we disabled the
normal response. Now if we talk to
the toilet guy, then it will say that I've overheard your conversation
with the security guy. I believe you will
find the person in this address and so forth. Once we talk this, we
will receive the card, and as you can see, it enables the
other child object. So if we talk to
this person again, it will say different
things than before. And inside the inventory, we should have the name card, let's go to this area
here and then save. And we can see in the console
that we successfully saved, so let's stop this, and then let's play again. And when we play this again, it should reset
all of the states. As you can see here
if I try to talk to this person again,
it will response. What are you looking at, this is the default response or
the normal response. And if we press load, it will load all of the state, including our player position, and also as you can see here, it's enable the last response
on the guy in the toilet. If we talk to this person here, after we load our progress, it will say that we will find him on that address and we will have the name card. But since in the editor, descriptle objects
always get safe, whether it's on run time or not. If we press play, you'll see that we have the name
card from the start. In order to test this, let's go to the data folder
under the inventory. Let's just delete the name card. If we press play,
then we should not have the name card anymore, right now, let's
just press load. And after we load if
we press inventory, you see we have the name card. The inventory is also
get safe right now. And you can see here under
the inventory inspector, it adds the name card after
we load the progress. So yeah, This is basically everything's working
nicely as you can see, and in the next video, we are going to develop the other gameplay in
the C. We are going to add taxi and then also a
transition to the park. But before we finish
up in this video, there's one thing
that I forgot under the In the scene here. We basically need to
create a transition to the outside or the C scene. Let's just create an empty
game object interactable. Reset its value, and then
let's call this interactables. Then let's create a
new empty game object, and let's just call this exit door and
move this exit door to this position here around this door here and let's
add a box collider, and let's just resize
this box collider. It fits with the door. Here. And let's make it thinner. So it fits and then add
interactable script, and then we can just
use the hand items. And then add an
action and we can change using the
change scene action, and then we want to go
to the CT scene here. Let's just type the C name, C C, it should be with
Capital C and then drag the change scene action to
a slot here and save this. Now if we go back
to the City scene, we can access the pawn manager. If we don't have
the pawn manager, then we should put one into it. Let's go to the prefab
folder and then drawn manager and put it
somewhere inside the hierarchy. For the city, we want to set the previous scene
to the club hallway. Let's just type the
scene club hallway, and the scene name should be exactly the same
with the file name here. For the club hallway,
it's like this. Now we can adjust the
club hallway position, and I want to set the player direction
to facing that way. So let's just set
the z value here. Let's just set this x
value to a positive one. Our z direction is
facing this way. Yeah, I think that should be all let's save the city here. If we go back to the club
hallway and then we play this, Let's try to load
the scene again. So we have the item and we have the states loaded and let's
go to this door here. It's show transition
to the city scene. As you can see once it's
transition to the city scene, our player is standing here. Another thing we need to do
is we need to add a taxi. After we leave the club hallway, we can go to the park and this taxi will act as a
transition to the park scene. Let's go to the point
clixFolder under the prefabs. I believe under the model, we have taxi there. Under the model, we
have the taxi model. We can just direct this
to the C. So I'm going to expand the CD group
here and let's drag the taxi to this CD object. Okay. Now we have this taxi as the child
object of the city. Let's just drag this and put the taxi around this position, and we can rotate it slightly. So the tire will contact
correctly to the ground. And we can also enable
the taxi to be static. And another thing we want
to do is we want to add a empty game object. So first, this will
be the response. This is for the
normal response and the other one if
we own name card. So the taxi driver will drive us to the address that are
listed on the name card. And on the parent game object, the taxi game object, we
want to add a box collider. So we can click on this taxi. And let's just adjust the size
of this game object here. And once we already set
the size for the collider, let's add an interactable
game object. And for the sprite, we are going to set this
to the top icon. And we want to
make sure that the distance position It's one, but we need to
make sure that the z values or the z position, it's facing the other way. In order to do that,
we need to create an empty game object and
then put this outside. Now we want to rotate this 180 degrees on
the y axis and set this zero on the x axis and put it around
this position here, and now we want to put the tax as a child object
of this game object. And we can just call this taxi again and let's just
move the interactable. Paste it here. Okay. I'm going to copy the box
collator and also put it here. Drag on the offset
the center value. So it fits with our taxi. Or we can just
adjust the collider, so it's easier to add it. I think they should be enough, on the one that on
the child object, let's just delete the box collider and also
the interactable. Now that we have set
up this correctly. I want to put the oxidor on
top and set this to trigger. Now the next thing
we are going to do, we want to put the normal in the response and the
name card response to the pattern game object. This is the visual and
this is the response. For the response, we are going
to add a message actions. Okay. And then for the
parent game object, the tax game object that has
the interactable script. We want to add an item action and just
add the item database, and we want to check
for name card. So we said to give item
and add yes or no. And another thing we want
to do is we want to drag the normal response to the No, and the own name card is to the s. So if we
own the name card, then we want to
trigger this action, but if we don't have it, we
want to trigger this one. Okay. So direct the item actions to be the actions
of our intractable. And for the normal response, we can just ask where
do you want to go. And we can add the name
of the taxi driver. Okay. But for the own name card, we want to ask do you
want to go to the address on the name card and we can set to enable
dialogue and yes or no, and we can add a change action. And if you choose, then we want to trigger the
change action here. Let's just change this
two part and save this. Okay. Now if we go to the data folder
and go to the inventory, I'm going to delete our name
card and try to play this. If we try to talk with
this taxi driver. You see, ask where
do you want to go. But since we don't
have the name card, we don't know where
we want to go. So for example, if we add the
name card to our inventory, then we can try to talk to the taxi driver again and
it will ask if you press, then it will load
the park scene here. Okay. Okay. So yeah, that should conclude the
club hallway setup. Okay.
55. 54 Park Gameplay: Okay. So now we are going to start
developing the park scene. And in order to do let's open the park scene
under the scenes folder. So here we have this park and we have this taxi and police car and make sure both
of the car objects are the child of the park and we want to
enable the static. So if this is not enabled yet, make sure it's enabled
and make sure it's also applied to all
of the child object. So I'm going to check
okay the static is also as a static object. Okay. So Now we can
generate a navigation mesh. Let's just open the
navigation panel. If you don't found the
navigation panel here, just go to the window and
I believe under the AI, there is a navigation panel. We can open this and it
will open this window and now choose the big
tab and press bate. This will create a area where
we can walk for the player. The next thing we
need to add is we need to add the prefabs
that we already prepared. The first thing we need to
add is the base Canvas. For this base canvas to work, we need to add an event system. Let's go to the UI
panel and then create a new event system and put it this outside
of our base canvas. I'm going to reset its
value, the transform values. Let's add data manager, the camera manager, and the pawn manager and
also the audio manager. Let's select all of this
manager and put it on top of the hierarchy. We
can save the scene. Now, under the prefect, we can direct the player and just put the player
somewhere in the season. I'm going to move on the
y axis slightly downward, and then put it here, and then I want you
to rotate the y axis on the rotation to 180 degrees, so we're facing the
other way and Sos. If we go to the game,
now let's test this to see if we can work if we
can walk on the scene or not. Somehow we cannot click it. First, we need to expand
the park and then select the ground grass and make sure it sets to
the ground layer, and for the tag make sure
the layer is ground for both of this ground object we
can set it to default, but we need to create a empty game object
that will act as a cli. Let's just create a new empty
and then add a box glider. And adjust the Y value on the size to be
a very small one. But let's just set the sie
to around 20, I believe. Then for the z axis, let's set this to 15. Basically, we need to
create a box lider that covers the scene,
something like this. Then we need to make sure that game object set to
the ground on the layer. Rename to a ground collide
and save the scene again. Now let's test this again. And now we can click
and as you can see, we can see the character is
moving around the scene. The next thing we need to add
is we need to add an NPC. I'm going to create a new tame object
and reset its value, and then I'm going to call this scene objects and put
it under the part here, and then I'm going to add a
new PC game object NPC group. Let's create a new and this
will be the police officer. Let's add the police
officer character under the tiny tiny people. Then under the TT demo folder, we have this prefpolder
and let's direct the police prefab and put it
under the police officer. Reset its position value to
a 000 and then also reset the rotation to also
000 on all of the axis, and we want to move
the parent object, which is the police officer. Let's just move
the police officer or on this position here. And we can rotate this a bit. If we look in the game view, we can see the police officer and we are slightly floating. I'm going to push on the y axis to make the police officers
contact the ground, its feet and then move
this slightly backwards. Then another thing we want to do is we want to add
a interactable. We will create a couple
of interactable. The first one would be the wall, and we want to add
a box collider here and move this
slightly sideways. I'm going to rotate this so it's facing forward like this. Now first, let's move the
box object on the y axis. We move this slightly
to up position then let's scale the
collider by pressing the ded collider button and I'm
holding the control. I want to create
a large wall that covers the scene
and this one also. So if we go to the game view and then we enable the gizmo, we can see the collider is covering all of the
rest of the scene here, and let's set z
value to around 0.2. We have a tin wall, and let's intractable or we can just add the
interactable component and it will set the
layer automatically to interactable because we already
set that in the script. And for the icon. I think I'm going to
use the hand icon. We might need another icon
for this, but it's okay. We can just use the icon, and we can add a message action. And we can no, you cannot pass says
the police officer. So we need to inter and then type the name, the
police officer. Okay. And save the seal, and we can trigger
the animation. But in order to
trigger the animation, we need to put the ate
action on this game object and then we can direct the
action to the wall here. The other thing we need to do is we want to create
another anti game object, and this will be
the interactable for talking to the
police officer, let's just add a busider
and then adjust its size. It fits the police officer Okay. And we can add an interactable. And we want to use
the talk icon. And let's just add a message actions and type
its name, police officer. Okay. Let's just say sorry, but you aren't allowed to
enter the crime scene. Let's save the scene,
and now let's try this. Basically, if we go
to this collide here, it will go there and then, we haven't triggered
the action for the wall here at the actions
and directly messes action. Then for the other
interactable object, we want to add actions
and then put it here. And for the wall
here, let's just move this position slightly
on the middle, so it will stop
at this position, our player when it
approach this crime scene, and then we want to slightly
center position a bit, so it covers our scene. Save this and test this again. So right now, we go
to the wall here, the police will say
that, no, you can pass. And if we talk to
this police officer, it will say a different message. And here we are going to give
the police officer a money to make sure that we will be able to
enter the crime scene. Okay. So now let's continue and we are going to add a
couple of game object here. So for this is the wall object. Okay. And when we click
this, the police will respond to this
message action, and this if we click
on the police officer. Now we want to create a
two different response, let's just create an
empty game object as this child here. And let's rename this
one to talk to officer. Okay. And the first one, we want to set this
to give money. So this is for bribing. So if we respond, yes, when we are going to
bribe the police officer, and let's add another
empty game object. And let's just rename
this to not enough money. So this is the response if
we don't have enough money. And then we are going to add
instead of a mess actions, we want to add an item action. Okay. Let's just add our database, and then choose the item
that we want to give. Here, we have the money. In order to have this item, we need to go to
the data folder and select the item database
and create a new item. And if you haven't done that, then let's just do that. Let's go back to the talk to the officer game object here. Let's just select give item. Let's add actions,
and no action. For the action, we want to
direct this give money here, but we haven't add an action, so it won't work right now. Let's just create
enable dialogue. Let's just sorry, but you aren't allowed to enter
the crime scene area, and let's just add a unless
and then add a grin here. So the police are expected to
be bright for the y label, label this, give him money. And for the no response, just add ignore him. We want to add AS actions, and we don't want to
add a no actions. For the action, we want to
direct the item action. If we press yes, then
the item action we'll try to give this money
item to the police. If we enable to give item, this item will be removed
from our inventory. So, we are responding with. Then we want to add another
actions for another actions, we want to activate
Oh, not under actions. We don't want to add actions, but here on the item action, we want to activate actions
to this yes action. I succeeded giving the
police officer money, then we want to
activate some object and activate some object. Let's just add entry and direct the wall object and set
this to the activate and also add another entry
and talk to the officer, let's duplicate
talk to the officer and let's just call
this money gave. Or gave money. And we want to delete the item action on the gave money and also
the activate action, and we want to
disable the dialogue. And let's just say now
you may enter the scene. Okay. For this item game object, we want to disable
this on start. If we succeed giving money, then we want to enable that. Under the activate actions, add another entry and drag the gave money game object,
and then enable this. We don't need the child so we can safely delete
this child here. And for the talk to the officer, if we succeed giving him money, then we want to say a message. So under the give money, let's add a message actions, and also for not enough money, let's just add a message action. For the give money, just
write police officer. Thanks Thanks for
not enough money, Let's just say, are you serious. Okay, and go back
to the touch to the officer game
object and we want to put this give money message, show the S actions
on the item actions. So if we succeeded giving, then it will show this message. And if we failed or if
we don't have the item, the show not enough
money message. Let's save the scene and go to the inventory and
we don't have money. I show enough money. Let's just play this And let's try to go
to the wall and yeah, the police say you cannot pass, and let's talk to
the police officer. But you aren't allowed to enter the crime scene
unless grin Okay, but it doesn't show the button. So let's just check
for any errors. Under the talk to the officer, we enable the dialogue. Okay. Under the dialog system, there is a slide bug and with this current
state of the code. If for example, under the police officer,
the touch officer. If we only have one message, then it won't show the button because we are showing
the button after we press space or we click on the
windows digi, and it will show. But if we only have one message, then we want to
show it right away. Let's just copy this cut this line here where
we are showing the button and put it outside. Here. Inside the y loop
but outside the statement, and then let's save this. Now if we go back, let's
test this press play, and then let's talk to
the police officer. If we talk to the
police officer, it will show the message right
away and also the button, and we can just try to give him money, but it
won't say anything. If you see in the item actions, we have this yes and no actions. Basically, If we press yes, but we don't have the money, I show the not
enough message here. In order to fix that, we
need to go to the code here, the item actions code, and I have this open here. Basically, we are checking
if we have the item, if we are giving item, but we
have all of this code here. But we check if we don't
have the items here, let's just put else, and then we can just copy this code here and just
run the no actions. And save this. Once we
modify the item action, we should be able to respond. Right now, let's
just try to talk to the officer and then
try to give money, and then it will say, are
you serious and we cannot pass through because we haven't give the police
officer a money. Let's just top this
and then go to the data folder and
under the inventory, select the money item and then add those items
into our inventory. Let's play this again. Now, if we talk to the police officer here
and we give him money. Take a notice here
on the hierarchy, we will see that the
talk to the officer and the wall game object will be disabled. Let's just press this. As you can see here,
now it's disabled, and we have this object enabled. Once we have this enabled, if we talk to the officer, it will say another
different messages, and now we can enter
the crime scene. Another thing that
I want to test here under the talk
to the officer. Let's just add a message. Do you want to give him money? Let's test this, see if the dialogue is working
with multiple dialogue, so let's just run this and
then talk to the officer. And if we press. It shows the next message and
it shows the button. If we only have one message
action or one message only, then it will show the
button right away. Now, we are going to add the corpse or the victim
on this crime scene. Let's go to the uni tiny
people and under the folder, go to the free and
we want to add the male A character and
put it under the NPC group. Here, I'm going to create
an empty game object, and then I'm going to put this male A object to be the
child of the game object. And let's call this
corpse or victim. Let's just set up its position, we want to make sure that the of our victim is facing diagonally
to this direction here. Let's just rotate this and make sure it if we select local, you'll see that
our access local? You'll see that our Z
access is facing this way, so we can rotate this bit here. Now we want to set
the child object. But first, we want to
hide the baseball bet. Just select the baseball
bet and then uncheck this and go back to the
TTmo male A object. Rotate this, so it's
facing forward there. Now turn this down here. Okay. So we want to make sure the victim is
position, something like this. Now we want to add a box glider. Let's just add a box glider. Let's move the position on the x axis slightly
and also the z axis. It's there and we can addit the glider and make
this slightly bigger, so it's easier to click here and let's one this next thing we want to do is we want
to set this to trigger, and then we can
add a intractable. And we want to activate
an object here. But we haven't created
those objects. So let's just create this. Safe. Now when we gave money
to the police officer, the wall will be removed,
gets hidden, activated. We can go through to
this victim here. We can check and
now we want to play a timeline or a cut scenes
whenever we check the victim. Let's just create a
new empty game object. I'm going to reset its value, and I'm going to call
this ending cut scene. And save the scene as always, and we want to create a
couple of objects here. First, I want you to
create a new camera. And I want you to set the camera to be around this position here. Okay. Or something like
this, something like this. I've set the view in
the scene view and then select the newly
created camera and then press control shift and F. We snap the camera
to that position. If we go to the game
view, you'll see that we have this area shown. We want to decrease
the field of view, so I'm going to set this two 50. Now we have a much
tighter framing and we won't see the
gap on the scene. For the ending cut scene, we want to add a object, M A, male B, which is the
detective or our player, and we want to add
the female one. Now we already create this, push it down, right above on
the ground for the player, and let's put it around
here in front of this. For the female, we want to
put it also down a bit. Now we are going to create a new timeline file
or timeline setup. Let's open the
timeline tab here. Or we can go to the window. I think it's under
sequencing timeline. Then press and it will open this window and press
create by press create, but make sure we are
selecting the ending cut sin. Select the object here and then press create and we want to go to the access
folder under the timeline. Let's just create
a new ending cuts or just call this ending, should be okay. Now
we have a timeline. First, I want to
create a tech group. I want to create two
different tech groups, and this is for the characters. And the second one is for the scripting event
or activation event. Let's just delete this
and call this activation. For the characters, let's just drag the male A to this group here and it will ask
whether we want to create an animation
activation or audio track. I want to create an
animation track. Now I'm going to
add right click on the timeline here and then
add from animation clip, and I'm going to select idle A and make sure this
starts from zero. And with selecting the clip, I want to set the
animation extrapolation, the post extrapolation to looping Whenever it's
finished playing, it will look the animation
clip and we don't need to drag this clip. We want to also
direct the female to the characters and then
add an animation track. For the female, we want
to set its position. But first, let's just select the game object and then
select the knife here. Let's select the knife
object here and hide it. Let's select the ending cut sin again so we can
see the timeline. Okay. Now for the female, let's add a clip also
at an animation clip, and we are going to
use this female walk, this one, female walk. And stretch to be around this time line here
around 120 frame. Now we have this
walking animation, and I'm going to
direct the female again to create another
animation track, and I'm going to
record this one. I want to move the
female game object, the female game object
here to be around this position on start
outside of our camera frame, and then I'm going
to push this down. Yeah. And we can we can
still see the female. I object so let's just push this around this position and make sure we
cannot see her again. And we want you to stop her
around this frame here. Sorry, first, I want
to rotate this female, it's facing to the
crime scene and drag this timeline
around this position, we want to move this again to be here or probably here and
we can rotate this a bit. If we play this, you'll see that it will move to the scene here, and we can stop recording. Here on the first track of
the female animation track, we can right click and
then add another clip, and let's just use
the female idle pan. We don't have any animations
with crying and stuff, so we can just use this
and I want to push this so it overlaps with the wall clip and it creates
transition like this. This would be the transition. Now we play this, you'll
see that once we stop it will transition to
the panting animation. And this is close
enough like crying. I should do. But if you
have a custom animation, you can try it with
that animation. For the male, since
it's an idle animation, it doesn't have position. What we want to do is we want
to select the idle clip, and then we want to
create motion offset. Let's just press
this button here and it will show a cursor. Let's just put it around
here and then rotate it, it's facing to the body. We want to move also
this again and make sure it right above our ground. Okay, something like this.
Let's just table this again. Now if we play this, you'll see that we have this sequence. For the panting,
the idle animation, select the clip,
and we want to also set the post extrapolate too. Now you'll see that it's
crying, it's crying. Now we are going to create a dialogue and also a transition to the main scene
or the man scene. Now I'm going to create two different I'm going to create two new empty game
object, create empty. And let's save the scene
first before anything happens and change
this to dialogue one, add a start we want to make sure that it's
disabled on this game object, and also enable the
height message on disable and we want to
add a message action. First, we are going to we are going to make
sure that we are showing information that our
player is saying that damage I think Let's direct this action to
the action slot here. Okay. And I want to duplicate this. And for the second one,
I'm going to say, wait. Okay. For the third dialogue, I'm going to change this to the grieving wife and
probably just crying. No, something like this. We have three dialogues. Let's just select the ending
cuts for the activation, let's just rename
this so it's clear. This is the second dialogue, and this should be
the third dialogue. Select the ending
cut since again, direct the first dialogue
to the activation group and activation we want to Make this short. And for
the second dialogue, we want to direct this
to the activation group also add an activation tag and make sure it gets activated
after the first dialogue, but make sure we
have space here. Otherwise, it might get disabled by the script accidentally, for the third one, let's direct the third dialogue
and add an activation tag. We want to also make sure
that this message is shown after the female
is stop and panting. So should do this, and we want to create another tigame object
for changing scene. Add a start track and a change scene action and
disable the game object. Direct the action here, and we haven't created the
scene target, but right now, let's just test this out, save the scene and select the
ending cut scene again. Direct the change scene
and we want to add an activation track and we want to activate this
after the last dial. This way, when the change
scene gets activated, the script will execute this action and it will change
back to our menu scene. Okay. Let's save this again. Now we want to
disable the cut sin. We want to disable the cut sin. We make sure that it doesn't
get played on start and it's hidden and make sure that your body is not
inside the timeline. It's not the child
of the ending cin, it should be the child of the NPC group under
the scene object. Okay. Now for the scene
object and PC group. Let's select the victim. Now let's add entry to
the activate actions and whenever we are interacting with this
corpse or the victims, we want to activate
the ending cut scene. Let's just activate
this by checking the checkbox here and
add another entry, and we want to
disable the player. Here we are going to
disable the player, the original player, and once
the cut sins is playing, we have another
instance of our player that will act out as this
timeline gets played. Okay. I think that all thing we need to do is we need to add the actions and direct
the actions here. This position should be
okay, should be around here, and we want to add a icon
and save the scene again. Now let's test this out. But before we test this, we need to add a money. Otherwise, we cannot
bribe the police officer, let's select the money
and then add items, so we have this
on our inventory. That will be all let's
test this out. Okay. So let's talk to
the police officer. Okay, I don't know why we
are missing a dialogue, so I'm going to add a message to the police officer under the talk to the
officer message here. And we are going to ask you
want to give him money. Let's save the scene again
and let's test this again. We have the money
in our inventory. Let's press play So now we
can approach the corpse. It tells you that
no you can't pass. So let's just talk to the
police officer, and we press. So yeah, give him
a knee and it will consume our money
here and now we can go anywhere we want to
go to the crime scene. So let's inspect
the corpse here. Once inspected, you'll see that the player is it's playing. So we have successfully
played the timeline. But it's a bit fast so
let's just fix that. Open the timeline object. I'm going to make
this much longer. I'm going to also probably
we need to edit this too. Let's just edit the
animation window and make the female arrive at 4 seconds
and go back to timeline, and now we can move the
female idle penclip and drag the wall,
so it's longer. Now we want to make the
dialogue longer Okay. And the third one
should be here. And activate the scene here. This should do and you'll see that we have an
error because we don't have the scene
name available, but we are going to do
that in a later lecture. Yeah, I think this
should be enough. Let's just try this again, add the money, and let's disable the
ending cuts first game. Let's test this again and
see if this is much slower. Let's bribe him and
then once bribe him, let's Now it's better. Okay. Okay, this
is basically done. Another thing that
oth thisis if we play this and then we try to
interact with some object. For example, the wall here, you'll see that our character
is tilted slightly. We need to fix that.
In order to fix that, we need to go to
the player script. So let's just add it the script. Now, the player script is open. Let's fix the rotation issues. In order to fix this, first, we need to set a new vector that has the directions between our interactables
and our player, and then set the y value zero. So in order to do that, let's just create a new vector three, and let's just call
this vector direction. And we want to cut this out. And then paste this here. And now that we
have new direction, we want to set the factor
direction, y zero. This will make
sure that we don't have different elevations
on the direction, and we will have the directions on the same plane x z plane, so it won't modify the x
rotations to the player. Once we set this value two zero, we can just simply put
this inside our rotation. This is the changes
that we need to do. Another thing we need to do
is here on top of turning, we need to set that if
currently we are turning, and then our transform that rotation is equal
to the target rotation. Then we want to set
turning to falls again. Turning will be
set to true again whenever we trigger this set
direction as you can see. This will prevent unneeded code execution
inside our update. Thus, we'll make this
much more optimized. Okay, now let's
go back to unity. Let's go back to it and
let's test this out. Now we can select the
player here and press play. And let's go to the
slide wall here. And as you can see, it's
top and the rotation, we have a zero on
the x rotation and also zero on the zero rotation. You see that we don't
have that dealer anymore. So now it's working nicely. And let's just test
this one more. Yeah. So now we fix that rotation
and a slight tilting issues.
56. 55 Menu Scene Setup: Okay. Now let's create the menu scene this menu sin will be
the starting screen. First, I'm going to
create a new scene, and I'm going to call this menu. Here, for the ending cut
change scenes game object, I'm going to insert the
scene target menu as the target for our change
scenes actions component. It should be menu. I forgot to put the menu scene
inside the build setting. Let's just do that drag the menu and put it on top here to be the first
scenes in build. Okay. And let's try it one more time. Okay. Now let's talk to the police
officer again and then give him some money and
then go to the victim. Okay, now it loads the menu, but since we haven't done
anything in the menu scene, so it just shows the
default let's stop the game and then open the scene here and let's
create a new canvas. Okay. And let's set up
this canvas here. We want to change the
Canvas scalar options, the UI scale mode options
to a scale screen size. I'm going to set this to a
full HD resolution 10911092, and then the height
should be ten 80. Let's save the scene
again for the camera, I'm going to change
this to a solid color and set this to black. Now we want to create
a couple of button. Let's just select the Canvas
and create a new button. And set the button size
to around 300 by 100. Okay. And here, go to the scene view and press the button
and we can adjust the button position for the
child game object here, the text, I'm going to remove this and then add a new text. But I'm going to use
the tech mesh pro here. Let's just add that then for
this child game object here, we can hold out button and then under the
cransform presets, we can click this
button right preset. It fits nicely with our
button for the button, I'm going to de the image. Or we can just set at
source image to none, and then set the color to black. We will have a black button. Here you'll see that I'm going to add an outline component. So let's just add
outline to the button, and then set the
color to a white, and I'm going to set
the alpha to 100, so it's opaque and let's increase the width by increasing the effect
lessens value each to five. So we have a ticker button. The other thing we need to
change is we need to go to the text object and
set this to new game, and I'm going to
enable the atoing but I'm going to
decrease the max size. So I'm sorry. Let's just leave the x sie, but we want to add a margin on the left
and the right side. So for the left, I'm going to feel 15
and for the right side, I'm going to also feel 15. Sorry 50. It looks better, and we
can copy this button here. And let's move the
first button on the y axis should be around negative 50
or negative hundred. I think 200 should do. For the second, sorry. For the second button, accept this two negative
400 or the h 250. Now we have two bon, and
for the second button, we want to change its
text to continue. Okay. Now that we have
set up the button, let's create a couple of scripts under the scripts
folder, the safe system. We want to create
a safe load menu. Let's just do that
safe load menu, and the other
script would be the Safe entry UI. This will be this is for the UI entry when we are
loading multiple safe, we can choose which one of the
safe that we want to load. Now let's create the UI for loading the game progress
and under the Canvas, let's create a new image. Okay. And we want to set
this to a bigger size. So first, I'm going to set
this to 800 for the width, and for the height,
let's just set this to 500 or even more. 700 should be okay, and let's just increase this. This is our load UI, and we can change
this to a dark blue. Now for this panel, let's
just call this load panel. We want to add a
vertical layout group. This vertical layout group will lay out the child object evenly. For example, if we create another image here and
let's create another image, you'll see that it will
get layout evenly here. We want to set a couple of things for our
vertical layout group. Let's just set the spacing
first around 15 and we want to add padding for each
of the side 25. And set the alignment to
middle sorry, upper center. And we want to set the
control size to the width. So the width will fit. And We want to disable the child force expand on the hype options here.
Uncheck the hype. We have this settings, and let's just set
this to around 250 or 200 should be
okay and save this. This is the data data entry, and we want to set a couple of things here for data entries. Let's just delete
the second image. Let's add a couple of things. First, we want to change its
color to also a dark blue. And let's add a tax mesh pro. And this will be
our current scene. Let's just set the
alignment to the top right. Let's just rename the
scene description. We want to duplicate this, but we want to put this down here. I'm going to move the text. And this will be the
dates where we safe. So let's just call this
dates safe dates. Okay. And we can we need to
add also a button. So let's just add a
button for loading. And let's just put this
on the right side here. Let's just increase
its height to run 60 for the child object, I'm going to delete the text and set this as a text mesh
pro for the child object. I'm going to fit this
transform by holding the button and
accessing the fret and press the bottom right options. For the button, I'm going to set the source image to
non, it will be solid, and then I'm going to pick the same color as our badgron But I'm
going to add an outline. I'm going to set this
outline color to it, just like the one we
create on the menu, and let's increase the
Alpha and also the y value. We should have something
like this, and for the text, just type this load let's
increase its size 36. Okay. So for the other two texts, we want to enable
the out of sizing. And for the description, let's just increase
this text here, but let's just put the anchor to the middle and let's decrease
the width to around 800. Okay. And let's move the y position, so we have margin
on the top here. This is the data entry, and we want to save
this later as prefabs. But now let's access
the script first. Let's just open
the safe entry UI. Here, since we are
using the text mesh. Now we are going to
import or we are going to use the text
mesh pro namespace, and then here, we want to
declare a couple of variables. The first one will be
the text mesh object. Text mesh pro, UDI, and we can call it the
scene description. The second one should
be the date Okay. And then we want you
also access the button. So let's just import UI
namespace Unit engine UI. I then let's just type
button for the class, and let's just call
this load button. And we want to add
a private integer, and let's just call this ID, and this will hold
the data entry index. And with this ID, we will load the
correct safe data from the list of safe data that we have
on our data manager. Let's just delete all of
this built in method. And first, we want to
create a public void, and this is for initializing the initialized
load entry. Okay. And we need a couple
of things here. First, we need a
string parameter, and this should
be the C name and also a string or we can
just import a dates. System and we can
just import the time. And also, we want
to pass the ID. So let's just create an
integer ID, and save this. So when we are
initializing this entry, we want to update
the text object. So let's just access the scene
description and access it text properties and assign
the s name to its properties. And the next thing we want
to update is the date text. Since we are having
the same name, we need to change this to this. The date and this will revert to the date that we declare
on the class here, and then type text. And we want to assign
a string here. So let's just change this to string and we don't need
the system using system, but we are going to
use the dead time when we initialize this load entry. So we are going to do that
in the safe load menu, and right now we can
just pass the date. For the ID, we can just
type this ID equal ID. We want to also create a load
function or load method. Let's just call this load data. And here we are going to
implement loading later. Now, we want to assign the load button on
click Add Listener, and we want to pass
this load data method as the listener of the onclick event for
this load button. So when we load the button, it will load the correct data
according to its ID here. Now, we can save this and we can create the safe load menu. For the safe load menu, first we want to add
a safe entry pref. Let's just type the class
here, safe entry UI, and this is the
entry prefep entry or the second variable that we need to have is a transform, this should be the p Let's
just call this parent. Let's delete the pate method since we are not
going to use it, and let's created in it, and let's just call this panel. And we want to create
a temporary integer, and let's just call
this safe count. And let's access
the data manager. Instance, safe. Do
we have the safe? Okay, let's go to the
data manager first and we have this list
of safe datas, right? So let's just create
a public accessor. And let's call this
safe data with as we want to we want to
only have properties, and then we want to
return the safe data. These properties able to return the safe
data that we have, but we cannot write it. It's protected and we can only write it inside
the data manager here. Let's just access
the saves count we want to look through
this count here. Let's just change the
length here to save count. By the way, if you are wondering
how I did the four loop, just type four and double tap and it will automatically create the four loops code and
just change the max length. Now we want to instantiate
the entry prefep Let's just instantiate a new entry
prefps save entry UI, temporary three, and then instantiate an entry prefps we want to set the
p as the parent, let's now we want to initialize
this temporary entry. Let's just type the t entry
and then call the it entry, and it as f and it
as for string name, string date and integer ID. Let's just go to
the data manager, and under the safe data, let's add a public system. We want to access the system
and then let's access the date here or public system, and we can access the
date time class here, and let's just
call it safe date. And now that we have this, we can access this by
creating a couple of strings. This should be the scene name. Let's describe this from
data manager instance. I instantiate, but it should
be instance and access the safe data index of I and let's grab
the current scene. For the date, We can just create a new string called safe date and then access
also the data manager. Instance, save data,
the capital S one, the public accessor that
we just created before, and access the safe date, but we want to form at this. We can just type two string. And we can set a format here. For the format for the format, we want to set the
first entry is date, we fill D character
four time and add a comma and then two D and this will
represent the date, the number number date. This will be the day name and this will be the day number, and for the month,
we want to set this to four time and space
and for the year. We want to also set
this four time. We want to also add hour full
column and then minutes, change the minutes
to a lower case one. Let's just save this. Now that we have
this, we can pass the sname to the first parameter of the edit load entry method, and the date put it to the second parameter,
second argument. For the ID, we can just
pass the I as the ID, and this should do the trick. Okay. Now that we have this setup, let's just call the
init panel method on the start method, and save this. Now we need to update
the data manager. Whenever we save data entry, we want to also save the date. Let's just type the
lower case one, and then access
the save data ID, and for the safe date, we want to use the
system the day time now. And this is a property, I think, this is the property,
so just type like this. And this will return
the current date time when we are executing
this method. When we are saving and
this will get safe, And for loading, I think we
don't need to do anything. We can just access the load
in here in safe data entry. Now we can implement this. Access the data manager
instance load data entry, and pass the ID that we've received from when we are initializing
this load entry. Just pass the integer value, and this will load test. Let's just set up
this in unity first. Let's select the data entry, and it's still
compiling right now. I'm going to direct
the safe entry UI to the data entry that
we've created, and as for a couple
of objects here. For the s description,
let's description object. It will automatically the text mesh UI and also the safe dates. I think I'm going to
rename this without the safe date for the button
just direct the button here. Like so, and now we can go to the prefap folder and just drag this data
entry as a prefps. Once we have set as
a pref we can safely delete this from the scene
and for the load panel. Let's go back to
the script and save system and select the load panel again and drag the safead menu. For the parent, we can just drag itself its object because it has this vertical layout group. And we want to drag the preface
from the prefix folder. So let's set the
prefix folder and direct data pref that
we have just created. One thing though we want to
do is I want to change this. I want to duplicate
this load panel, and I want to remove the image and also all of the
other component. I want to set this as
an empty game object. I want to set the load panel as of this empty game object. And let's put the
safe load menu, copy this component here and put it on the
parent game object. So the way it works
here, as you can see, we are putting the
safe load menu here and we can safely
delete this one, and this will refer to
the parent game object. So we can hide this. Still
the script will gets executed because the
parent game object is not the activated. So it will instantiate all
of this safe entry on start. But it's disabled,
so it won't show, and it will only be shown when we click the
Continue button. Okay, let's save the
scene, and now menu. We want to create
a couple method. So we can just create
a new public method and call this new
game for new game, we want to go to
the Data Manager and also create a
new public method for setting a new game. Let's just type void new game. For the new game, we want
to add a new safe data to our safe data add and
call this new save data. We are creating a new instance
of safe data and we add this right to the end of our safe data at
the last member. Then for the safe data ID, We want to set this to the data the current save datas count but we need to
substract this by one. So we will get the correct
ID for the last member here. Since ID is always an
index base of zero, so it starts from zero. So we need to make
sure that we substract this with one and save this. Also, in a new game, I want to access
the level manager, and then we can just
sad the office. I think it's the intro. Okay. And for the safe data, I don't think we
need this anymore, but just comment comment
this out for the time being, and if there are any issues, then we can modify this later. Now that we have the save data, we can go to the safe
load menu and access the data manager instance, and we can run the new
game method. Okay. For loading, we
want to also create a public method void can
call this show load menu, and we can just type
per game object Sebcte Also we want to create a closed button if
we are plan on canceling. Let's create a new
button and Okay. We can just since we are using
the vertical layout group, so we need to find
another way to do this or we can just
duplicate this. And put this as a child, and let's just call this D vertical layout
and remove the image. For the load panel, just
remove the vertical layout. We have this vertical
layout sits nicely. Now we need to change this. For the parent game object, make sure it's the
vertical layout. The data entry will
be loaded evently or nicely using this
vertical layout group. Here, under the load panel, we want to create a button. That will act as a close button. Let's just set this to 50 by 50. That should be okay and
put it on the top right. I'm holding the alt
and click here. I sits on the top right and sets the value on the x
and the white two zero. Sorry two zero. It sits on the middle here, and we can just set this to and also set this to a dark blue. Remove the text
game object and add the text mesh pro since
it's much better. Using the text mesh b is very sharp and
very light weight. I'm going to set this to x for the character
and to size this. We have this x pattern for closing when we are
closing the button, we want to hide this panel
here, the patent panel. Let's just select
the load panel and then set game object
set act to false. And we need to add another
entry on the safe load menu. Let's go to the scripts here, the safe lock menu and
create a new cerrized field, and this would be a game object, and this would be
the load panel. So we want to hide
and show this. So let's just change this para game object to the
load panel, sect to true. So let's head back to unity. And once it's updated, we will have a new
entry on the inspector. Then we want to direct
the load panel. Okay, so save this. Now let's test this. But since this panel is shown, we want to automatically
hide this. So let's just hide on start. So let's just dip the load panel set active two false
and save this. Now if we press play, it should hide the panel automatically, and we can start a new game. But there is an error here, so let's just check
what is the error. Okay, there is So, the manager. Do we, we haven't put data
manager here on the menu. Let's just go to
the prefix folder and direct the data manager. So we have one instance in it. Now if we press play, this should be done,
the error message. If we press new game,
One thing that I forgot, we forgot to assign the button
here under the load panel, let's just create a new button, but we need to import
the unity I namespace. Let's just access that.
Here, let's create two new entry button first for the new game and the second one for the load continue. Sorry, continue game. Okay. And on start, we can assign this button on click Listener to the new game method that we've created
in this class here. Let's just copy this line here, paste this and change this
to the continued game. For the continued
game, let's just use the show load menu
method, save this. Now if we go back to Unity, let's update the entries here, for the first button, it's going to be the new game button, so direct this button and put
it on the new game entry, and the second button,
put it on the continued. Hopefully, this works. Press play run and
then press new game. You see that, there is an error because we haven't put all of the scenes
on the build settings. Let's just do that now, go to the scenes folder and then press the build settings. Okay, now we have
the sample scene. I'm going to remove
the sample scene, but I'm going to add the
office and also the part. This is all of the scene that should be
included in the build. And save this, save
the scene and save the project because we
change the bill settings. Now if we press play,
it should work. Press new game. There you go. It's loading, and now
it's playing the game. So this should be
all for the menu, and we need to continue
working on the load. But first, we need to change a couple of things on
the game play scene. So it works right now
if we press continue. It doesn't show anything, and we are going to address
this issue on the next video. Okay.
57. 56 Menu Continued: Okay, now let's continue
our safe and load system, the first thing we want
to do is we want to go to the data manager script here and we have
this load method, but we never load any data. We want to load this
method on start. Let's just create a
start method and then access the load method
that we have created. Basically, this load data will only load the
safe data from the binary format or the safe data that we
save on the storage, and if the load, but
it won't do anything. Just load and fill this safe data list with the previous data that
we have on our storage. In order to load this, we need to run this load data entry. Also under this load method, we need to check the
safe system or if we have a data that we
have saved before. Otherwise, it will throw an error when we
try to load this. Now, you see that
if we press plea, and then if I press continue, It will show the save
data that we have, and we have a save data in the sample scene because we are testing on the
sample scene before. But since we don't have the
date time object before, it shows the default value here, as you can see,
Monday, the date, the month, the year, and the time or
the hour minutes. Now let's fix a couple
of things here. Under the load entry when
we are loading the data, we want to load the scene. Let's just access
the level manager. At manager is the child object of the data manager
that we've created, and this handle
for scene loading and just access the scene load, and then we can access
the sorry, the save data, and let's just access the
save data ID index ID, and let's just get
the current scene. So Whenever we try
to load this data, and this method is going to be called by
this safe entry UI. Whenever we press the
load butthn here, since it has a listener, then this load data will run the load data entry
based on the ID. Here, when we run this method, we don't need this
load method anymore, so we can safely
delete this because the load method it's
already being called on start and we only need to
call the load method once. And after that, we need to only load the inventory and
call the unload method and load the currencne that we have on the save
data and save this. Now if we press play, you'll see that we will be able to press continue
and press load, and we will load
the sample scene. But there is an issue because I've removed the sample
scene from the bill setting. In order to test
this, we need to put the sample scene
back to the bill setting and test this again, press continue and then load
and it should load the test. As you can see it loads, but it doesn't
load the position. Let's just check Let's just
go the sample scene here. I believe for the NPC, we have the entity, we should have the safe
entity. The third PC. We have the safe entity here and we can
check this on start, we want to create a new class for initializing the entity, and we want to also
modify the data manager. Here on the data manager, we are going to create a
new method and this will be in entities for
the entities here, we can just call
the unload method. And we don't need this, but
we can just leave this. It's okay. We can just run that. We need to create a initializer. Let's create a new subscript, and let's call this its initial And this will
be a very simple script, but we need to delay the
script by 100 milliseconds. So let's just set this. On start, we want to run
the entities initialized. Let's just access the data
manager that instance, I think it is init entities. Let's just run this method
and this will trigger Here, on the sample scene, we can just create a
new empty game object. And let's just call this
entity initializer. And we want to add the
script that we have discrete it entities
initializer. If we select this script here, we can go to the inspector. If you select the script
from the project manager, go to the inspector and then
press the execution order, and we want to add the entities initializer by 100 milliseconds. Press apply, and this
will apply the changes. The script will gets executed after all of the other script already present on the screen so we can initialize
the entity by calling this unload method unload
event in the deaminator. Now let's go back to
the menu sin here, save, and then press play. Now let's try to continue the
game and then press load. There you go. Now you see the NPC is already moved
to this position here, and I believe we have inventory, let's
just test this out. Go to the data folder, we can go to the
inventory and we can delete the name and
delete the key. I'm not sure, but
let's just test this. And then press continue and
then load. There you go. As you can see, the inventory
is also gets loaded. Another thing we need to
do is we need to create a small script for
saving on the game play. But we are going to do
that in the next video.
58. 57 Add Save Menu In Game: Okay. So now I've loaded the C scene here and I'm
going to adjust the UI for saving and we are not going to be able
to load in the game, but we want to able to save. So here, under the
debug safe load, I'm going to direct
the safe button and put it under the inventory, and we want to hide
the safe load. So let's just go
to the scene view. With the TDs press, we can adjust the UI. Let's just set the
anchor to the top right here and move the position to be
below the inventory. We want to make the button to be the same size of the
inventory button. It's 160 by 60. It's just set that value
160 by 60 and put it below. And let's just delete all of the text game object here and add a new text mess P, I game object as a child, and for both of the game object, let's just set this
to the stretch to the parent game object. You'll see that each will
fit nicely with the button. For the button, we want
to set this to a value of and we set this to
black's just add an outline. For the outline change
its color to white and set the value to
the alpha value to 100 and we want to increase the thickness by changing
the effect distance value. I'm going to copy this
component here and go to the state button and also set
this to none for the color, change it to black
and we can paste this component that we
have just copied before. Paste component is
new and it will paste the the outline and call this inventory and
enable the to size, but I'm going to add
a slight padding ten by ten on the left
and the right side. For the save, I'm going to
call the save and also enable the toe and add a
top button padding. Now we have a very nice button similar to our other button. Now, let's open the
inventory system UI. Okay. And I want
to add a button. So in order to do that, we are going to or we can
just create a public method. Okay. I can just call the
public method and save progress and this will
access the data manager, instance, save data entry. Okay, so this will save
our current progress. And maybe some of you may ask, since we are going
to use this button, this method from the
inventory system UI. So here under the
inventory button, you'll see that we have
the unclick event, and this is showing
an inventory. And for the safe
button, I'm going to do the same direct the
inventory panel. And I'm going to access inventory system UI and
call the safe progress. Maybe some of you may ask
why don't we just tract the data manager to this click event and then access the safe
data entry right away, I cannot do that because
we cannot do that because when we are
transitioning to other scene, our data manager on that new
scene will get destroyed. If we do that, the
button will refer to the data manager from
each of its scenes. If we have the Singleton
persist to the other scene, then it will destroy
the other data manager. And when the data
manager gets destroyed, The safe button
on that new scene that we have just loaded, will miss or we'll find
the data manager missing. So we cannot safe progress, and that will be an issue. By doing this, we
are making sure that the safe button will
always have access to the safe progress method and safe progress method
will have access to the data manager via script by calling its static reference
the instance here. Let's just save this and I'm going to apply the base Canvas. Since this is a pref
and save the scene. If we go to the other scene, let's check to see if the prefps is updated and as
you can see, it's updated. Here, if we check the
reference of the flix event, this still refers to
the data manager. Let's just drag the
inventory panel again, and this should call
the safe progress. Let's just first apply. Now, this should be safe and let's check on
the other scene. This is the C Hallway city and the other scene that we
have is the part and save the scene and check its base canvas here
under the safe button, as you can see, it's accessing
the inventory panel. I will never miss this object here, the
safe progress method. Since it's the same preps now
if we go to the menu here, We can try to save the progress. So let's just press play, and then press new game, and let's just
watch the cut sins. And once we already
on the scene, I want to go to this area here, and I want to look
at the password, and now I want to
save the progress. Let's save this and
you'll see that we have successfully saved
and let's just stop this. Now if we press, if
I press continue, you'll see that we
have two entries, the sample scene and
the city for the city, we have the correct
date if we press, it will load the city But there
is an issue with the city because we haven't put
the entities initializer. Let's just do that quickly, create an empty game
object, reset its value. And I'm going to call this
entities initializer, and I'm going to add
the z component. Now we have created the
entities initializer, we need to do this
on every scene, so I just copy this
game object here. Save the scene and go
to the club hallway and then paste the game object. So I have this
entities initializer, and also for the part, save the scene and paste the entities
initializer. So if we
59. 58 Saving Thumbnail: Okay, in this video, we are going to continue
on the safe pictures, and right now we have a
loading and saving system. And if we go to
the prefix folder, I want to drag in
the inventory UI, but the Data entry, I think. Yeah. And Let's just open the canvas here and
under the load panel. We have this vertical layout. I'm going to put
this data entry to the vertical layout,
and we can see here. Let's just adjust
this and basically, we want to create a screenshot
of it of our progress. We are going to adjust the
composition of our UI here. For the scene descriptions, let's just decrease
the size here. And for the date, of course, we can make
this even smaller. And push this to the
middle this position here, and make it also smaller on
the horizontal size sizing here the tax, but it
should be the object. And for the load button, we are going to change this
this position by holding out, you can move it to this
position here and I'm going to add a
space to the edges, so it doesn't to cram. And now we have this
new space here. Let's just create a new UI, and this would be a raw image. It's not going to be an
image, but it's a raw image. And for raw image, it as for texture. So we are going to provide this texture from
our screenshot. So now I'm going to set
this to the site here, and I'm going to set
a certain value, so probably around 240 by 170. Yeah, this should
do and add space. And this is basically our
pixel size for our screenshot. So save the scene, and I'm going to apply this data entry. Okay. So now we need to
modify a couple of scripts. So let's just do that. I'm going to go to the manager folder under the data manager. And the first thing we
need to do is we need to create a new custom class that
will hold the color data, the pixel data or
the image data. And basically, the image data, it will be a series of pixels, and it's going to be
safe in a color array. But we are not going
to use the color class because the color class
from unity cannot be safe. It can be serialized. I'm going to create a new class that it can be serializable. Therefore, I'm going to add
a system serializable tag, and then I'm going to create a new class and I'm going
to call this custom color. This custom color, we
have a public float, and this is for the color data, which is the red, the green,
the blue, and the Alpha. But I don't think
we need the Alpha, so we can just save the RGB and this will be more smaller
in terms of safe file size. Basically, we want to
create a new function to return a color from
this custom color data. We are going to
create a new public that will return a color, and let's just call
this get color. Let's just type
return new color, and we can add the
value, which is the r, the G, B, the blue,
red, green, blue. For the alpha, we're
going to simply put one, so it's opaque and safeas. Now that we have saved
the custom color, we are going to
add this new class as a field on our safe data. Let's call this
custom color array, and let's just call
this thumbnail. Now the next thing we need
to do is we need to create a helper class that will
save the screenshot. In order to do that,
I'm going to go to the safe system folder and I'm going to create a
new shop script, and I'm going to call
this screen shot saver let's open this
new created class. Okay, now we need to create
a new private integer. Okay. And this
would be the width. Okay. Should be around 270, I think for the height, it should be 170. But
I'm going to check this. So let's go head back to unity and under the dree I'm going to access the raw image
object and see it size. Oh, it's 240. So
let's just modify this and make sure this is the value that we
have on our raw image. The next thing I'm going to create is a custom color array, and let's call this thumbnail. The next one is a color array Let's just call this pixels. And the last thing we need to create is a new core routine, and this is a route
object that can hold a coroutine that
are currently running. So let's just call this
screen capture weight, and this will be a property
and we can get it publicly, but we can only
set it privately. So I'm going to add
a public getter, but a private setter. Save this. I'm going to
delete the update method. Since we are not
going to use that, We are not going to use
the start method also. Let's just delete this. I'm going to change this
to a public method. Type public void and change
the name to get snap shot. Basically, this
will run a routine. Let's just create
the routine first. I enumerator and call this take or we can just
call this process snapshot. Now that we have this, we
can run the coroutine, but we can put it inside
the screen capture object. Let's just type
screen capture weight and then equal start
coroutine and let's just run the process pshutin Now that we have
this process nash, we need to create a
new render texture. Let's just type render texture, and we can just call this RD, and then we can construct
this rendered texture. I believe we can set the size here. We are going
to use this one. Basically, we are going
to pass the width, and then the height
that we created on top on the variable
decdationF the depth, I'm going to set this 2204. We don't need the Alpha, so
let's just set this 2204. Then we are going to
access the camera manager in Conmra Carmra is
the active camera, and we can access
the target texture, and we want to set
the target texture to the RT or the render game objective we have
just created before. Once we set this target texture, we want to make sure
that we are processing the screenshot at the
end of the frame. So we are going to
delay the code, yield, return new weight
for end of frame. And this is basically going to wait until the frame
is almost finished, and then after
that, it's going to start execute the
code below here. And this is to make sure that all of the object on the
screen is already been drawn, including the three D object, the UI, and stuff, and
it will get saved. Now here, we need to access the render texture class here, and it has a active and
As you can see here, it tells us that currently
active no texture. We need to set the
active to the RT that we've just created and we
assigned to the camera manager. One thing that I
forgot, we need to also create a new texture
to the game object, and let's just call
this screenshot SS Let's just construct
this new texture, and for the width, we
are going to set the, the, and for the format. This should be a texture format, and I'm going to
choose this A RGB 32, and this is for the MMP options and we are going to
set this two falls, it doesn't have a map. We don't need that.
Let's save the script. Now that we have created
the texture to the object, we need to read the
pixels from our screen. Let's just use the
read pixels method. Basically, this is as for
a rectangular source, the source size and the
destination initial position. We can create a new
rectangular and first is 0.0 And then for the width, I'm going to get the render the textures with and also
render textures height. So we have a new rectangular
size of our render textures, and for the destination x and decision y we are going
to set this two zero. It starts from the bottom
left of the screen, and it will read all of the pixels up to the
top right pixels. And once we read the pixels,
we need to apply them. So let's just SS apply. And then we want to save
the pixels that we just read to the color array. So I just type pixels
equal to the SSG pixels. And this basically will save whatever pixel array data that we have on
our texture to D, and it will get saved
to the color array. And now we need to
convert the custom, but the color array to
custom color array. In order to do that, we need
to create a new function. Let's just go below
here and I'm going to add a fit texture to array we need to
pass the color array, and let's just color
this color pixels. Okay. And inside
this color pixels, we are going to declare
our thumbnail object here. In order to do that,
we can just type thumbnail equal
new custom color, and for the length,
we can just grab the color pixel that length. It will have the same length as our pixel that we grab
from our no texture. Then we are going to
look that length here. And now we are going to copy each of the data to our tonal. For the tonal index of the
R channel or the red color, we are going to grab
the color pixels, i dot r. The same value,
float value here. It's going to be safe. I'm going to copy this a couple of times, but I'm going to set this to
g the last one should be B, in blue and same goes
with the color pixel. And this one also.
So now that we have saved the color pixel that we
grab from the nto texture, this will temporarily be
saved in the tmnail object, and later we can grab this
since it's a public field. Now that we have created
this method, let's just run. The texture to array and pass the pixels that
we just grabbed before. Once we grab the pixels, we don't need the
random ture anymore. So we can just safely
access our camera manager, that instance, current camera. And for the target texture, target display, the
target texture, we are going to
set this to know. And then we are going to
destroy our RT game object. This one here. We are going to destroy to clear
it from memory. After that, we are going to stop all coroutine that are running inside this class,
the screenshot saver. But it's only this process
snapshots actually. For the screen capate we
are going to satisfacon no. So now we've finished
the co routine. Now we need to create two more
method in this class here, and the other one is a public method that returned
a custom color array. And let's just call
this get thumb nail. And this is basically
we'll return the thumbnail object that
we just created here. And next method is a new method that will
return a texture. Okay. And this is for loading the custom color
back to texture to object. So let's just call
this array to texture, and we need to pass the
custom color array object. And let's call this
safe Tumbnail. For this, we are going to create a new
integer called length, and let's just create
the safe t length. This object that we are going to pass when we are running
this method here. We are grabbing those length. Then for the pixels object, we are going to clear this and
set this as a new array of color with a number of
member of the length. It will have the same member or the same index with
our safe object here. Now we are going to look through
the length. Yeah, Delay. Here? And with color, we can just set this
to a new color. So basically, we can
set the col index of I to be the safe
tuna index of I, and then we have created
a gate color function. So let's just run
that get color, and this will return
a color so we can put it to a
pixel color array. Once we finish looping through the safe tuna colors
to the pixel here, we are going to sorry,
but before that, we are going to create a
texture to the object. And call this S S and we
are going to construct this just like we construct it in the orotin here so
it just copy this. It should be the same and paste this here,
makes things easier. Then after that, we are
going to use the as pixels. It's not get pixels,
it's set pixels, and basically it as
for a array of color. So we can just pass the pixels. And then after that, we
can just apply this. So it gets applied to the
Texture two D. And after that, we are going to return
the aces object. So we are returning this
object to the return type. And whenever we
run this function, it will return a texture
two D that we can show on our safe entries UI. So let's just save this script, and I think this should be all
for this screenshot saver. And in the next video,
we are going to continue how to set up this onto the saving system and loading system and also
the safe entry UI.
60. 59 Add Thumbnail to Load UI: Now we are going to
continue setting up the Tmnil system
for the save data, and let's open the data manager. Now that we have this
data manager open, I'm going to create a new private screenshot saver object, and let's just call
this as saver. Here, below the
component level manager, we are going to grab the screenshot saver
and we are going to use the get component
screenshot saver Okay. And we are going to put the screenshot script to the
data manager game object. Now, basically, we need to
create a new coroutine inside the data manager to save a screenshot whenever
we save a data. Here, below the save data entry, I'm going to create
a enumerator, and I'm going to call
this save screenshot. And for saving the screenshot, I'm going to access the saver, and then I'm going to run
the snapshot function. And after that, I'm
going to wait return. I'm going to return for the screen capture
weight to be done here. This is basically we'll
wait for this process, and if it's new and it will continue to execute
the code below here. Once we've finished
grabbing the screenshot, we can save the screenshot to the safe data object index of safe data ID or our
current safe data ID. And we have the tumnil
object that we've created in the last video
here, as you can see, and we are going to save the newly generated Tmnila
which is using the gun method, and return a custom color, Tumnail is a custom color, so we are going to use this
method and this basically will save to the safe ta object. Once we already fill this out, we are going to run
the safe method. Now, safe method are being
handled by this orotine. We need to remove
this. Now instead of using the safe in
the safe data entry, we are going to run the start
coroutine safe screenshot. Yeah. So it's like this. Whenever we save
things, we are going to save all of the
entity in the scene, and then we save the s name, and then we save the scene date, we save the inventory, and
then we save the screenshot. And when the screenshot
is finished, it will run the save function, we will save the data to the binary formatter or
the storage on the disc. Now we need to create
a method that will output a texture from our custom color in
the data manager. We already have these function in the screenshot saver here. But this is basically is a function to convert the
custom color to a texture. And in the data manager, we need to make sure that we
have a function that we grab a certain ID from our safe data and then convert the
thumbnail into a new texture. So I'm going to create
a new method here, a public method, and I'm going to call this or we can
just call this thumbnail. Based on ID, and we are going to pass
the integer data ID, and we are going to
create a new texture two D. But this is going to be an output
variable, not an input. An argument is usually an input. In order to create
an output variable, we can create a new out
keyword in front of the class here. We have this. Now, we are going
to run the method. This ray texture method
into this object. Let's just type. Here, going to type S
S equal the SS saver, and then we can use the
ray texture function and we need to pass
the custom color. This will be the safe index of the data ID that we pass
here and grab its thumbnail. We are going to pass the
thumbnail custom color object, and this will convert
this to the texture, and then the texture will
be saved to the SS object, and this will be
outputted by this method. And this method gets called by I think it's a safe entry
UI or the safe load menu. I'm not sure, but we
are going to check that. Okay. Save this. And now we need to modify
the safe entry UI. So where is it. Let's just go to Unit, and I'm going to go to
the safe system folder and I'm going to open
the safe entry UI. Basically, the script that's on this data entry
object safe entry UI, let's modify a bit. We are going to add a
new serialized field, and this will be a type of
row image and let's just call this screen capture
or screen capture. Basically, we are going
to add a argument here, which is the texture, D, and let's just call this SS. Basically, we are going to
sell the raw image, row image. The screen capture texture to the texture that we
pass on the method here. Now that we set this, let's go to the safe load menu. I'm going to open
the safe load menu here and put it near
on our data manager. Here, the saved menu. Now, as you can see,
we have an error in the in the load entry
because now it asked for a texture two D. In order
to load the texture two D, we can access the time
manager, Instance, and then we can run d. I think it's load tunail based on ID, and this ask for the ID and we have the ID,
which is the I here. Let's just pass the. We are going to output this texture
to a new texture object. Let's just create a
new texture object, Texture two D. Sorry, Texture two D and call this SS. For outputting, we need
to put out keyword. This will make sure that instead of inputting the texture, we are outputting the result
to this variable here. Now, once we finish outputting, we can pass this S to
the init load entry. Basically, the screenshot
will get loaded whenever we are populating the safe entry
UI to the load menu here. We can see it on the UI later. Let's just save this.
I believe we are done. Let's go back to unity and start setting things
up in the editor. Okay. Now we are going to direct our raw image as the
screen capture field here, and then press apply again
and we can delete this. And on the data manager, we are going to add a
screenshot saver object. Perhaps we can just hide this. Let's just go back to
the screenshot saver. Okay. And we can make
sure it's private. We don't need this to be public. Since the data, it stays
inside this class, and this is for holding the
color array temporarily, and this is also for holding the custom color temporarily, and we are returning the thumbnail using
this function here. Safe now that we
have set this up, I think it's going to probably throw an error if we
press play now because our previous safe data doesn't have a color as
you can see here. So if I continue,
There is an error. Okay. So as you can see, we have an error
whenever we start the game because when we are populating the load entries, it tries to grab a texture, and we don't have this
on our previous safe. So we need to make sure
to create a fail safe. If the save tumnil is null, then we want to return
a null texture. Other thing that we need to
fix here in the data manager. I made a mistake here. The custom color, it should
be a strut not a class. And struck is basically
like a class, but a much simpler data, and it has a less memory
footprint on the memory. But it's more suitable
for a data that is color, which is plenty monogamous data, which is a float class
is more suitable for data that stores much
different data here, custom color, string, day time. Now I've changed this to struck here. Let's
give it a try again. Let's press play and
let's run a new game, and we can just watch
the intro again. Okay. And now, let's try
to save this game here. Safe. As you can see, it holds, and then it
throws a safe success. So if I stop this and
then press play again, if I try to continue, you'll see I save data that
have a screenshot here. And this is the
previous data that I've tried before when
I cut an error. And if we load this, it will start from
this scene here, and let's just go to the hallway we have the
password. We can go in. Okay. Now we are in. Let's
save this again. This is successfully safe, and if we stop this
and then press play again, G continue. As you can see, we
are in the club hallway here and it
grabs the screenshot. Yeah, that is basically how
the screenshot system works. Since color is not serializable, that's why we need to create a new custom color strap to save the color a disc
fire binary ftter the other thing that we
need to do is we need to make sure that the
players pan position gets executed after they
script, save sytem. The initializer. And if we open the
execution order, you see that the
entity's initializer gets delayed by
100 milliseconds. So we need to make sure that
the players pan position. Let's just search for the
player span position here, and this gets delayed even more than
entities initializer. Whenever we enter a new
scene, entities initializer, we load all of the
data object state including the position
of the player, and then it gets
override if we are transitioning from a different
different previous scene. So it will transition
the player to the door or to the player spans position that we
already set up. Press apply. Now, this will make sure that we are responding to a
correct position. For example, here,
if I press play, and then I try to load
the club hallway. If I go to the door here, You'll see that we
are spawning here. Otherwise, it might spawn
on the same position, X Y and Z world space from
the club hallway to the C and it might put the player
on some odd position. H that is basically
how to create a screenshot for the save data. And I'm aware that this
is a very advanced topic, but it's a nice to know
topic with this method, basically you can save
any screenshot anything and do different methods
or not different methods, but you can use this
screenshot for anything for safe data or for sharing
screenshot and stuff. Okay.
61. 60 Finish and Build: This is going to
be our last video in this course, and basically, we are going to
finish things up and one thing that I noticed
in the menu seen here, our canvas, the
load panel canvas doesn't have a scroll bar. So for example, if we
have lots of game safe, we cannot scroll the
UI, for example, if I just open this up and then I duplicate
this a couple of times. You'll see that we have
no way to scroll and we will have this ugly UI.
We need to fix that. In order to fix that first, we can go to the
load panel here, the child load panel, and then we can create a new UI, and let's create a scroll view. And this will create
a new object, which is a scroll view. And for the scroll view, we are going to set its size
to round this parent object. So let's just hold
out and then click this button here under
the anchor presets, we can add a margins
about 20 pixels on each of the edges area. Okay. Now that we have set
up this scroll view here, we want to go to the view port here,
and under the content, we want to delete
this child object, and we want to
direct this vertical out as a child of this
field part object. The other thing that
we want to do is we want to drag the vertical
out as a content. As you can see, when we
delete the content object. Now the content is missing
a rectangular transform. So we need to drag a new one
and this will be the content that will show the save progress and we will be
able to scrawl it. I want to also add a content
size for the vertical, let's just set this
two preferred size. Now let's save the scene
and let's press play. Okay. So now if we
press continue, you see we have a
scroll bar on the side, and if I try to duplicate
the entry, Okay. You see, we have a scroll
scroll bar on the side here. So we can scroll down and then we can select the
progress that we want to load for
setting things up here, we can probably under
the view port here, we can disable the image. So we don't use that, we
need that because it's for making for the scroll view, I think, here we can
disable the agon image. So we have a better, and we have a better UI for
the scroll bar horizontal, we can height this
because we don't use it. We cannot hide it. But I
think we can disable this. Let's just go to the
scroll view here. Let's just set this out of p or we can just
remove it, I think. Yeah, set this to.
So we don't have a horizontal scroll bar
and we can disable this. Once we remove it
from the scroll view, no, set this to n,
we can disable it. Now if we press save, we will have a consistent UI. Yeah and we only
have a vertical you. Okay. Yeah, it's still working. So it's very nice and
this will automatically the radical outt will
automatically fit the child sizes. So the scroll view will always get longer if
we have more content. Another thing we want
to do is we want to create a message whenever we
successfully save currently, we only can see the
message in the console. Let's just go to one of the
scene here, couple way, for example, and let's open
the data manager script. Here, now we have the
data manager opened. Under the safe method, It's this here, I think, not safe entities. Yeah. Under the safe screenshot here, whenever we
successfully save here, we want to We want to
show the message here. I think it would be best if we put it here under
the safe method. Let's just access
the dialogue system that instance and use
the show message. Here, we can create
a new list of strings or we can create new list of strings
outside list of string. And then let's call
it safe message. And let's initialize this. And then add a new message. Save successfully. Okay. Now that we have this, we can pass this list of
string as the message, and then we can set I
think it's this one, the bull dialogue, and we
can set this two false. It's not a dialogue. It's just
a message and close this. Now if we press play
and test this out, and whenever we try to save, there's an error here. Okay. Okay, we have a load, so let's just go
to the menu scene and then play it from here. And let's open one
of our progress. The save is somehow deleted, so I'm going to press new game. And once the cut since is
over, let's try to save. Okay, now we are
in the scene here. Let's try to press save. There you go. We
have this message. So you sir, we'll know
whenever we successfully save. Now if we try to play this, let's try to continue again. There you go, our progress. The next thing we need
to do is we need to set a couple of things
in the build settings. So let's just go to
the build settings and go to the player settings. Now we have this
player settings. And first, we need to
modify the company name. So for example, I'm going to call this Milch because
that is my company. And let's just the
private e or whatever. I'm not good with naming things. The other thing we want
to do is we want to set the Mc store options if we are going to
release this on the mac. So basically, for the
bundle identifier, we need to set this
to the company name, and then the product name.
I'm not going to change this. And we also need
to set a version, version is basically what is
shown to the user and build is the build version
that's going to be recognized by the store. Whenever we update an apps, the build version have to be greater number than
the previous one. Otherwise, we cannot update it. So make sure that we keep
track of this build for each, every updates or path releases. I think that would be all. You can change also the
color space to linear. If you are using a HDRP high
definition radar pipeline and with the PBR
material and stuff, it will look better with linear. But since this is
more a tune style, I'm going to leave this
to gamma because it's more performance on
a lower end devices. The last thing we
need to do is we need to set the default icon. So we haven't had
any icon so far. I'm going to go to
the sprites folder, and I've already
prepared an icon, and this is included
on this module. So let's just direct
and put it inside the sprite folder under
the player settings, it asks for a texture two
D. I'm going to direct this game icon texture Now once we already set up
the player settings, let's just save the project, and now we can try to build it. And if you want to
have a wider audience, make sure it's set
to 32 bit or x 86, and that is 32 bit compatible with the
underscore 64 64 version, and this has a because 32
bit can run on a 64 machine, but a 64 B cannot run
in a 32 bit machine. So for greater audience, wider audience, just
set this to 32 bit. And then once we finish updating or importing the assets for the target platform,
we can build this. Now let's just
press built and I'm going to create a
new build folder. Then I'm going to add a new
folder and this will be the version and
the build number. It's easier to keep track, and then I'm going to select this folder and
once we select it, it will build the
game to that folder. So I'm going to stop
the building process and continue after we finish it. Now as you can see here, the build is done, and we have this file. This is basically
the build is done and for distributing the game, you only need to include
the data folder, the it folder and the
unity player folder. And this is for debugging, just zip it and you can distribute it or
create an installer. Now let's run this game. So if you press play,
It has the settings, and we can set the resolution. I'm going to set a
smaller resolution with window options enabled
and then press play. So now we have the games. We can continue, but
we don't have the safe because this is a different
one from the editor, so let's just run a new game. Okay. There you go. We have this game built. I hopefully you find
this course enjoyable and useful and it can help you expand your
skills with unity, and I hope to see you in another course in the future.
Thanks for enrolling. See you next time.
62. 61 PlayerScript Bug Fix: Hey, one of the student
found an issue, and thanks a lot to Bruno Bayer for pointing
this issue out. Basically, we have
a bug where if we try to play and then
let's see the bugs here. So if we try to top
to one of the NPCs, and before we arrived, we go to the other NPC. I will j see it will show and it will execute the
actions from the first NPC, which is this one here. Where it says that can you
move out of the way and then it moves the NPC
using the move actions, and it should be
playing these actions, which is the pole NPC actions. We are going to fix
that and text again to Bruno Bayer for pointing this out and also
showing me the fix. Basically, to fix this, let's open the player
script component. Player script CSR file. Then let's go to
the onclick event here or the onclick method, and we need to save the
interactable object here to a local variable in our class and keep track if those
variable is now or not. Let's just declare one first. I'm going to create a new
private and let's just call this interactable
and I'm going to call this previous
interactable. And let's go I'm going to
zoom this a bit, the editor. Okay. Now let's go back
to the click method. And inside here, after we
finish running the intractable, we can just try to check if, if the previous
intractable is not new, then we want to
stop the coroutine. Let's just access the
object and then run the stop all co routines method. And this will stop all of the coroutine from our
previous intractable. After we stop the tin, we want to override the previous intractable to our current
newly pick intractable. Let's just assign the intractable back to this
previous intractable. This way, basically, when we click on an
intractable object, it will try to run the movement, the move player, and then it will intract that intractable. And if there are any previous if the previous
interval is not new, then we want to stop the
previous coroutines. Then after that,
we want to replace the previous intervale to
our current intractable. As you can see here, if I
opened the intractable script, you see that we
have this coroutine wait for player arriving. Basically, the stop stop all courtin method will
stop this coroutine here. It will stop any coroutine
that are currently running on our previous interratle
it will cancel out all of these actions and
set directions and stop. So this will fix
that and save this. And we also need to put here if we click on a
area on the ground. But we have a previous
interractable. We want to also cancel that. So let's just copy this
statement here and put it here. But we want to create
a bracket and then put the top cortins inside
this statement. And after we stop the art, we want to set the
interractable to. This will clear out any
previous interracable. Okay. And next time we move
to any other position, we won't trigger the less
intractable by accidents, and if we click on intractable, it will just loop
the code here and then try to check if we have any previous
intractable or not. Okay, this should fix the issue. Let's just give it a try. So it's still
compiling right now. Okay. Now, let's try to talk to this person here and then
go to the police officer. And we cancel out the
actions from our NPC, and he doesn't move. And it shows the actions from the police NPC,
as you can see here. Good day, and then we can respond and we
can show the button. And if we press
awesome, it will reply. But there is another issues where if we try to
talk to the police go jump on fee then before
clicking the button here, one of the button, we go to the other position and it
will disable the message. But if we go to talk
to this other NPC, it will show the button.
So we need to fix that. So now we need to go to
the dialogue system. Okay. And here inside
the dialogue system, we have the hype
dialogue method. So instead of the
activating the panel, we should be using this method, so I'm going to
change this using the hype dialogue method. Okay. Also, the one
that we declare on the no button Let's just remove this and replace
this with height dialogue. This is basically we'll do
the same hiding the panel. For button, we want to set a height dialogue and also for the button to be the
default listener. Then back to our
height dialogue, we want to disable a
lot of things here. First, we want to stop all routines whenever
we hiding dialogue. It will stop any
previous routine. The next time we are
showing new message, it will start a new
fresh routines with a new argument values
or parameter values. And this will fix
the button that keep showing if we didn't
finish our previous dialogue. And then we want to set
the message ID to zero. Yeah, I think it
should do the trick. So let's just give it a try. Go back to unity and let
it compiled for a second. Once it's finished, let's
give it a try again. Now if we press play and we try to talk to
the police officer. And then we don't
finish the dialogue. We go somewhere else, and then if we talk to this NPC, it should hide the
button, as you can see. Okay. So yeah, that is basically the fix for our player script
and our dialogue system. Thanks again to Bruno Bayer
for pointing this out. Okay.
63. 62 Entities Resets on Scene Changes Bug Fix: Okay. So in this video, we are going to fix
another small bugs, and thanks to Bruno buyer
for pointing that out. Basically, the issue
is with the entities. Currently, if we change scene, all of the entities
that are changed in the previous scenes are not
stored in the safe data. So we need to fix that. And I'm going to show
you the issues here. Let's just run the game, and then let's go to the here to the door here
and try to talk to the door, the bouncer, and it will tell us that we
have the wrong password. If we go to this wall here
and we will see the writing, and once we know the password, we can go inside this room. But if we go back to the
outside of the club here, if we try to talk, then it will show the wrong
password again. And we need to fix that since the player already
know the password. Those states where we answer the correct
password should be saved. In order to fix that. First, we need to
open the change scenes action and also
the data manager. I'm going to open the
data manager here. And here inside
the data manager, we need to add a new method to save all of the
entities on our scene, and we can just trigger the event here,
the actions event. Instead, but we are going to
trigger the unsafe event. So let's just type public void, and I'm going to call this
store entities state. And then I'm going to trigger the unsafe event and
save the data manager. Once we've created this
unsafe event here, the public method,
store entity state, we can go to the
change scene action, and after we setting
the previous scene, based on our current
active scene before we are transitioning
to another scene. Let's just access the newly created method
in the data manager. Let's just store
the entity state. And basically what this
is doing is whenever we transition to another scene before we finish transitioning, we are going to make
sure that all of the entities that are changed its child active status
will stored into the dictionary in the data
manager that we have here. I think it's in the safe
data. Let's just check this. Yeah. All of the entities will store its data
to entity data here, and including the child status because we are using the different child to
trigger different messages. So yeah, let's save this
and go back to unity, and now we can test this out. Okay, I'm going to play this again and let's
go to this area here and let's just talk to the bouncer first and go to
see the correct password. And then let's go back
to the door here. And once we enter
the scene here, let's try to go back outside and let's talk
to the bouncer again. And now we will remember the change state hence it will show like the player
remember the password. So yeah, that is
the small issues that we have before
and now it's fixed. Thanks again, Bruno Beer,
for pointing this out. And if you have found
any issues or any bugs, just let me know and
I'll try to fix it, and then I post another video to how to fix it. Thanks. Okay.