Transcripts
1. Promo: Welcome to my course on
inventory and shops in Gado. This course is a continuation of dialogue and events in Gado but can be followed and applied to any project that involves
picking up items, adding them to an
inventory, dropping, using or equipping items
from an inventory menu, and trading items with an MPC. You're welcome to join
our discord server to work on this course
alongside your peers. In this course, we will cover different methods of
picking up items, managing the players
inventory data, displaying their inventory
on the UI in a menu, allowing the player to interact with their inventory
in different ways, and trading items with NPCs. When you're done,
you'll have a simple inventory system for your game that is easily customizable in its presentation
and function. You'll also learn useful skills for working with the
Gada game engine, organizing and designing your projects to
be more scalable. You'll be learning
how to code with GD script with everything
explained in detail. We will apply object
oriented design principles in our scripts,
inheritance, encapsulation, abstraction, and polymorphism,
to keep them organized, customizable, and reusable
for a project of any size. All of the project files are
also available on GitHub, if you need to review
the project as it was after completing
each lesson. These videos were recorded
using Gdoersion 4.2 0.2. The project starts with
assets from K Kits, character and Dungeon remastered
packs made by K Lauberg. Basic Guy bundle
made by Penzilla, and icons made by Shakahi. All are available to
download for free on it. Sound effects made by
Valensbyer are also included and are available
on freesound.org.
2. Coins: Hello, friends. Before
we get started, you should have a game
seen in your project, which contains a character
that the player can control and a level they
can walk around in. You should have at least
one other character to act as a shopkeeper, as well as assets for coins and items for the player to
pick up, use and equip. Throughout this course,
we will give the player an inventory to keep track
of their money and items, display them on the user
interface, use consumables, equip items to the character, and buy or sell them from NPCs. You can use your own project
to follow along with this course or download a
starter project from my GitHub, titled Godo Inventory
course, Main branch. Your project can be two D or three D and almost any
game genre that involves the character having
money and or items in an inventory and the
ability to trade with NPCs. Let's start by adding
a variable to keep track of the players
currency in their save file, which in this project is
the progress resource. Exporting the variables, so it will be serialized and saved. Let's call it coins. It will be an integer with a
default value of zero. Next, let's display this
information on the Canvas, using a label node. After setting the
font and font size, it's a good idea to
decide on the limit of how large a number you want
to allow to be displayed, experimenting with a
placeholder number. Suma limit of 10 million. It would be nice to
have an icon beside this number to tell the
player what it means. Using a texture wrecked node, populated with a coin icon, We can put both as children of a horizontal box container
to arrange them for us. Let's rename the
parent node wallet. And move it to be
behind the fade. I think the coin counter will look best in the
upper left corner, which it is already anchored to, but with 32 pixels of padding from the
edge of the window. The icon is too large, so I'll change its stretch mode to keep aspect to maintain its aspect ratio and have it automatically resize to fit its width proportional
to its height. Resetting the height of
the parent container. It will now be set by the
label nodes font size, which is apparently 42 pixels. The height of the icon
will also be 42 pixels, and thus with a one
to one aspect ratio, the icon will also have
its width set to 42. I'll also have the text
centered vertically and add a 16 point black outline to the number to match the
outline around the icon. Thinking ahead, I
would like to have the icon pulse slightly
when the number changes. Setting its custom minimum
size y value to 42. Click and drag it to the right, and we can see the effects of increasing and then decreasing
this value over time. Doing this rapidly, we can see the effect of
applying a quick, but subtle pulse effect, which will also slightly
move the label, grabbing the player's attention. I would also like to
keep the Canvas clear of irrelevant information while the player is playing the game. So we'll have the money counter hidden when it isn't needed. Selecting the wallet node. We can click and
drag the position y value to see how it can move up off the screen to hide and back down
again to show itself. Let's start the wallet
in its closed position, where its position
y value will be the same as its size y
value but negative. After displaying the wallet, we can start a timer to
automatically hide it after a duration of time
using a timer node. Let's name it auto hide, and set its one shot
property to true, so it will only
count down once when requested to keep the
demonstration short. I'll auto hide the wallet
after just 3 seconds. Adding a script to this node, let's call it counter and save it in the UI
scripts folder. It can inherit from
control since it doesn't need any behaviors of the
horizontal box container. We'll need references to all of the child notes set
during the ready phase. And a private tween variable. Public functions
will be available to tell this note
to open or close, returning the tween
finished signal when done, and one to set the
value of the counter. Opening the counter
will be accomplished by tweening its y position
to positive 32, and closing it will do the same to the negative of
its size dot y. It would be a good idea
to export these as variables to make the script more reusable for
other counters. Adding on screen
position and off screen position variables,
both as vector two, O pen can now tween the
nodes position to be on screen and close
tweens to offscreen. Changing the float argument
into a vector two. Tweening the nodes position, accepting a new position
as a vector two parameter, and returning the
tween finished signal. We'll first check if a tween
exists and is running. If so, kill it and
create a new one. We can then tween the
nodes position to reach the new position
over a duration of time. Let's export this
as another variable and give it a default
value of 1 second. Remember to set the values of the onscreen and
offscreen positions of this control node
in the inspector. To set the value, we need a new value to set it to,
which should be an integer. We will be setting the text
property of the label node to match this integer after
casting its type to a string, but clamping it to
be between zero and the maximum value
allowed buyer display. But first, we should check if the counter is currently
being displayed, and if not, await
the open function. For that, we will need
a Boolean variable telling us whether or not the
counter is currently open, which will be set to true
during the open function, but only after the
tween has finished. Then also await the
icon pulse animation before updating
the actual number. Or you can omit the a
weight here to have the pulse and the number
update simultaneously. Adding a pulse function, returning the tween finished signal after doing the
usual tween checks, I'll simply tween the icons custom minimum y property
to be slightly larger. Let's say 46 pixels. Over a duration of one
tenth of a second, then tween back to its original size over
the same duration. We can also export variables for the duration and size of
the pulse if we want to. Note that because of the
node settings I'm using, shrinking the icon to be any smaller than 42
pixels will be ignored. You can always change the type of the horizontal box to be a normal control
node and manually position its children if
you want more flexibility. Selecting the auto
hid timer node, we can connect its
time out signal to the counter's close function. By selecting it from the list, the green arrow
shows the function is connected to a signal. Sometimes we may want to
disable the auto hide feature. Let's add that
functionality with an optional Boolean parameter called stay open,
defaulting to true. Storing this in a local
variable of the same name, We can use this as a condition before requesting the
auto hide timer to start, disableling the
auto hide behavior and keeping this counter open. When setting the
value of the counter, if the counter is
not already open, then it stands to reason that the counter has not
been told to stay open. So we should await
its opening and pass false as the argument to
trigger the auto hide, and set is open back
to false when closing. Now let's switch to
the level scene and create a stack of coins that
the player can pick up. Starting with an
Aa three D node, let's call it coin stack Small. Then drag the model asset onto the area three D node
to add it as a child. Right clicking on
the coin stack, select make Local to separate this node from
the imported asset, making it just a local copy, instead of a reference
to the asset. Now we can extract
just the mesh instance three D node that is drawing the coin stack and
remove everything else. Make sure the coins are
resting cleanly on the floor, adjusting your
perspective if necessary. Next, we'll add a
collision shape for the area three D node. I'll use a sphere,
give it a radius that surrounds it sufficiently and move it up above the ground. Let's attach a new script to this coin stack
called coin stack. I'll put it in the
event scripts folder. T. And have it inherit from proximity event to automatically react to
collisions with the player. For more information on events, see the dialog and events
course in the series. Let's export a variable for the amount of coins
that are in the stack, a an integer with a
default value of ten. We can also restrict this
value to be contained within a range using
the export range tag, specifying a minimum
and maximum value allowed for this variable. A stack of coins
should not be empty, so the minimum
value would be one, and I'll just put the
maximum at 10 million. A providing a definition for the event manager to call the scripts run event function, accepting the event
manager as a parameter, we won't need to use it, so it can be proceeded
with an underscore. We need to set the
progress variable to be plus the value of
this stack of coins, then destroy it using Q free. We'll also need to override the on body entered function to tell the game manager not to disable the player node
during this event. Let's make this stack
of coins its own scene, saving it in the scenes folder and create a new
sub folder for items. Then move the stack away from the player's
starting position. The last thing we need to do is update the UI when the
coins are collected. There's probably
going to be a lot of coins in the
game and we don't want to manage their signals manually. That would
be a waste of time. An easy way to accomplish this is to use auto loaded signals. Creating a new auto load script, let's call it global and give
it a signal called coins dated with an integer parameter
for the new coins amount. This script is also useful
for providing values for commonly used variables across different scripts
throughout your project, particularly ones that
are not meant to be adjustable by the player
in the game settings. We can then add the
global script to the list of auto loaded nodes in
the project settings. The counter script attached
to our wallet can now use the ready function to connect this signal to its
set value function. Setting the label on the
UI to match the argument passed through the signal emitted by the global auto load. And also initialize the
value to whatever is stored in the players save file when the game first loads. But this is no longer
a repurposable script. So let's give it a
class name counter. Then create a new script inherited from counter
called Wallet. Which will have
this ready function directly tying it to
the player's coins. Replacing the script attached to the wall by dragging
it onto the node. The counter script
can now be used to count anything
displayed on the UY, such as keys or health
potions, for example. In the progress script, adding a setter function
to the coins variable, we can tell this signal to emit automatically anytime
the value changes. Using an if statement,
we can make sure the number isn't
being set to what it already was. Let's try it out. Walking over to the
coins, they disappear. The wallet appears with
the balance of zero, which pulses and
updates to be ten. Then after a three
second wait time, the wallet hides itself. We now have coins that
the player can pick up and keep track of on
the game's Canvas. In the next lesson, we'll add visual and audio
effects to improve the look and feel of
these game mechanics. I'll see you in the next lesson.
3. Polish: Hello, friends. In
the previous lesson, we added collectible
coins to our game. In this lesson, we'll use all
kinds of special effects to polish the coin
collection process to make it more satisfying. Starting in the counter script, we can add a variety of
effects to the behavior of our tweens using
transitions and easings, which change how the property changes over the
duration of time. Let's export a tween
transition type variable. And set it when
creating the tween. Looking in the inspector,
the default behavior is a linear transition, uniformly changing the
property over time. But we can change it to
several different options. Taking a look at these graphs, I think it would look nice if the counter used the
elastic transition type. And each of these can also be combined with an easing type, applying them to the in half of the tween or the
out half or both. Instead of exporting
the ease type, I would like to have
different easings applied to the open
closed functions. Since it would
make sense to have the elastic effect
play out on screen, so I'll have the open function and the closed function in. Passing the easing type from the open closed
functions through to the tween position
function in each case. In the coin stack scene, we can wait for the
stack to shrink before destroying it instead
of it just disappearing. Grabbing a reference to the
mess during the ready phase, We can tell the mesh to
shrink using a tween. Since there's no possible way this tween can be
used more than once, we don't need to worry about
it. Just create the tween. I'll use the same
elastic transition type and tween the meshes scale property to zero over a short duration
and return the signal. But we do need to make sure
that the player doesn't collide with this area again while the tween is happening. So let's disable it by
adding a function in the proximity event script that sets its collision mask to zero. We can also add extra polish by adding an audio stream
player three D node. Populated with a coin
clinking sound effect. I'm using the coin
sounds pack created by val inspired or. We need to grab a reference
to this node using add on. And tell it to play
during the event. Then wait for the
sound to finish before queuing the node to be
freed so it can play out. As an audio stream
player three D node, the sound emitted will
adjust its volume based on proximity to the default
listener, the camera. If we want to add sound
effects to the user interface, we should instead use the basic
audio stream player node, which will always
play its sound effect at the volume it is set to. Grabbing a reference to the
audio stream player node. We can play a coin clink sound anytime the counter's
value is set. We now have sound
effects playing from different nodes and
different scenes. One auto loaded node
is also playing background music while other nodes are playing sound effects, and we will
definitely have a lot more as the game gets bigger, and others might
also play dialogue. We should set their volume to match that of the game settings. But managing them individually
would be difficult. If we click on the audio tab
at the bottom of the editor, Gada has a built in
audio bus that we can use to categorize our
different audio sources. This allows us to
adjust the volumes or other settings of each
collectively through the bus, instead of each individual node. Adding a new bus for music and
another for sound effects, both will be passed through the master before being
output to the speakers. Adjusting the master volume will affect everything
in the game. Adjusting the music
volume affects only the music and likewise
with the sound effects. Selecting the sound effect
audio stream nodes, we can change their bus to SFX. Since we can't access
the music node outside the auto loaded script, we can use the ready function
to set its bus to music. Make sure the spelling
is exactly the same. Otherwise, the bus will be set back to the default of master. Save the audio bus layout
as a project resource. Then open the project
settings Under audio buses, select the audio bus
layout to be the default. The music node will no longer need to refer to
the volume setting, since the music audio
bus will set the cap for its volume and can just fade
to a linear volume of one. In the settings
resource, we can add variables to store each
individual volume setting, master, music, and
sound effects, providing default values
for each as well. In the settings menu, we can add volume sliders for changing
each of these values. I'll change the volume
to master volume. Then duplicate it twice, and edit the duplicates to be music volume
and sound effects. Grabbing references to each slider during
the ready phase. Initializing them to match the game settings
when first loaded. We no longer need
to manually set the music volume since it
will be handled by the bus. And when the player
changes their values, change the variables in
the settings resource, and also adjust the volumes
in the audio bus to match. Audio server set bus volume
Decibels audio server bus index Master file dot
settings do master volume, converted from
linear to decibels. Giving each slider their
own value changed function, after we can edit the signal connections
of the sliders to connect to the appropriate
value changed functions. We also need to initialize the bus volumes
when first loaded. Now, we don't need to bother manually adjusting
the volumes of any audio nodes as long as they are sorted into the
appropriate category. Opening up the coins stack small scene Reset the transforms local
origin if it isn't. Let's also add a
simple particle effect to the coins when
they disappear. Pudo has two
different node types for generating particles, one that runs on the CPU
and the other on the GPU. CPU particles that are compatible with slower
computers or ones that don't have a dedicated
graphics processor like mobile devices, but they can't produce as many particles and have
less features. A CPU particle system
is capable enough to produce a small burst of particles like what we're
trying to achieve here. But a GPU particle emitter
would be more appropriate for emitting thousands or millions of particles on a modern system. I'll use a CPU particle emitter. Right now, nothing is emitting because there is no
particle to draw. Expand the drawing
section and add a mesh resource to represent
a single particle. I would like to use the
coin mesh as the particle. I'll drag a single coin asset from the asset pack
into the scene. Make it local, and select the
mesh instance three D node. After making this mesh unique, so it is no longer tied
to the imported asset. I can save this mesh
as a project resource. I'll name it coin mesh. Deleting all these nodes and reselecting the
particle emitter, I can now use this
mesh as my particle. The particles are now emitting eight times per second
and falling down. I would like a one time burst of particles instead of a
continuous emission. Expanding the time section, I'll set one shot to true
and explosiveness to one. Clicking on the emitting toggle, the eight particles all spawn at the same time and
disappear after 1 second, which is the lifetime property. I want the particles
to burst upward, so I'll expand the
direction section and change the direction to up
with a spread of 15 degrees. Next, I'll set the initial
velocity to be a random number 2-3 meters/second and set the gravity lower at only negative 4
meters/second squared. I'll have the particles
align their local up vectors toward the
direction they're moving. Let's see how it looks so far. I would prefer the coins not to suddenly appear or disappear, so I'll expand the
scale section, and at a scale curve. Expanding the curve
resource to edit it, we have time along the x axis
and scale along the y axis. Note that a time of
one is not 1 second, nor is a scale of one, 1 meter. These are percentages of the particles lifetime
and scale properties. At time zero, I want the
coin to be invisible, setting its scale to be zero, and the same at time one. At the halfway point of
the particles lifetime, I want its scale to
be one or full sized. This creates a wave
so the particle will gradually grow and shrink
over its lifetime. We can modify the
anchors of any point on this curve to adjust how it
affects the particles scale. Now it looks like
the coins burst upward and rain down
after being collected. There are a lot of different
settings for adjusting how particles are spawned and how they behave over their lifetime. Try experimenting with different
particle effect settings to get the type of effect
you want for your game. In the coins stack script, we can grab a reference to the particle emitter node and set its emitting property to true when the
coins are collected. Then await its finished signal before queuing this
node to be freed. I'll have the shrinking,
sound effect, and particle effect, all
happen simultaneously. Duplicating the coin
stack small scene, we can make a medium
and a large stack. Replacing the mesh instance three D node with
different stacks of coins. I'll also set the value
of these stacks to have a higher default value by changing it in the inspector
within their scenes. Change their sound effect and their number of particles emitted by their
particle effects. Then add different stacks around the level to be picked
up by the player. We can also use particle
effects in two D as well. Let's make a similar effect on the wallet icon when the
coin encounter updates. Adding a two D particle
emitter to the icon node. I'll position it to
be in the center. A Using almost all the same properties
for this particle emitter, I'll just change the particle
to a copy of the coin icon. Use a spread of 180 degrees, so the particles fly
in all directions and remove gravity. The velocity is measured
in pixels per second, so I'll have it vary 16-32. But apply the same curve so the particles
grow and shrink. And set the min and max scale to only 5% of its normal size. Then in the counter script, grabbing a reference to
the particle emitter, we can tell it to emit when
updating the counter's value. Now when picking up the coins, the behaviors of the coins and the UI look a lot
more interesting. Try experimenting with different transition and easing types, sound effects, and
particle effects to get the look and feel
you want for your game. We now have a more satisfying
experience for the player collecting coins in our game thanks to a variety of effects. In the next lesson, we'll
allow the player to pick up an item by pressing a button and adding it to
their inventory. I'll see you in the next lesson.
4. Pick Up: Hello, friends. In
the previous lesson, we added polish to the
coin collection mechanics. In this lesson,
we'll add items that the player can pick up
by pressing a button. Let's start by
making a couple of items that the player
should be able to pick up. Starting in the character scene, we can use one of the
items in their hands. I'll use the Barbarians ax. Making this ax its own scene, saving it in the
items scenes folder, Notice how the origin of the axe is at the grip where
the character holds it. We can add a rigid body
three D node to this scene, which will make it
a physics object, indicated by the
beach ball icon. Give it a collision shape. I'll just use a box collide. Then adjust the boundaries
and position of the box to encapsulate
the x model. Using orthographic viewpoints
makes this much easier. Right clicking on the
rigid body three D node, I'll make this the scenes
root node and rename it to X. Let's put this on a
new collision layer. I'll use Layer six for items. This object can
mask the terrain, other items, the player and MPC. Opening the project settings, Layer names, three D physics, setting aside Layer
six for item pickups. Attaching a script
to the root node, I'll just call it item. I'll keep it simple
for now and just export a display name for
the item as a string. We'll expand on the
items themselves. Returning to the
character scene, we can delete the item node, since we don't have
the proper systems in place to handle
equipping it yet. In the Dungeon scene,
dragging a bottle A labeled brown into the
scene and making it local, the asset was imported to already have a rigid
body attached to it. We can right click on this
node and make it its own scene and name it health potion. Re parent and rename the health potion and
delete the Node three D. O pening the scene, the world simulation
is toggled off. I'll toggle it back on. Let's rename the root node
to health potion as well. And attach the item script, giving the health
potion a display name. Using the same collision
settings as the axe. The potions origin is at its base where it will
sit on the ground, as is its center of mass, leading to some
unusual behavior. To make items easier for characters to hold
in their hands, it helps to have their origin be where the character
will hold onto it. So selecting both the mesh
and the collision shape, let's move them down about
two thirds of the bottle, positioning the origin around
the neck of the bottle. And selecting the
rigid body node, expanding the mass
distribution section. We can also change the center of mass by switching it
from auto to custom, shifting it to be closer
to the wider part of the bottle down along the
y axis about one third. This will keep the bottle from behaving like a rolly pole. We now have two different items that the player should
be able to pick up. Let's place an ax
and two potions on the table for the
player to collect. But this looks rather boring, and since these are physics
objects that can move around, let's put one of the potions on the floor tipped onto its side. Back in the health potent scene, we can try attaching an
interaction event to it, using an area three D node
and a collision shape. I'll use the same capsule
as our NPCs as an example. While this system
worked great for interacting with NPCs and doors, which are generally
larger and don't rotate, we can now see the
limitations of our existing event
interactions when dealing with smaller
objects and or physics objects that can rotate. For this demonstration, I'll turn on visible
collision shapes. Attaching an interaction event to the health
potion will make it uninteractable if it tips over
and rolls onto the floor. Since the collision
area will also rotate about the local
origin of the health potion. This becomes much harder to reliably target with a ray cast. We could make the collision
shape a sphere and large enough that it will always be
detectable by the ray cast. But let's use a
different solution. Many games also allow the
player to pick up items behind them that are still
in close proximity to the character
for convenience. A cast that only looks forward isn't going
to accomplish this. Another way of interacting with the environment can be achieved using an area three D node attached to the
player character, using either a sphere or a
cylinder collision shape, setting the radius to an
appropriate range limit. I'll use the same range as the interaction ray
cast of 2 meters. Set the height at the character
colliders height of 2.6 meters and center
it on the character at half of that 1.3 meters. But if the character is standing on a downward slope
and the player wants to pick up items slightly below them, they
should be able to. So I'll add half
a meter of height and lower the center by
one quarter of a meter. This collision shape will be monitoring for items
that the player can pick up when they
press the interact button, masking layer six. Attaching a script
to this area node. Let's name it pick up radius. I'll make a new folder for player scripts and
put this in there. Then move the player and spring arm scripts into
this folder as well. Then connect the body entered
signal to this script to react to any body on Layer six entering
the pick up radius. As well as the body exited signal when they leave
the pick up radius. This script will
need to maintain a list of every item
in range of the player that can be picked up using
an array of node three Ds, which will default
to an empty array. When a body enters this area, it will be appended
to the array. And when it leaves this area, it will be erased
from the array. But the downside of
using area collisions in this way is that
there is no way to detect obstacles in between. This will actually
allow the player to pick up items on the
other side of a wall. We will need to use another ray cast to determine whether what the player wants to pick up is obstructed by the terrain
or other obstacles. Adding a cast three D node
to the item pick up radius, I'll move it up along the y axis halfway
up the character, 1.3 meters, and have it collide with only
the terrain or items. When the player presses
the interact button, I'll just have them pick up
the nerest item to them. L et's write an
algorithm that will get the nerest item to the
player within this area, returning a node three D.
When writing an algorithm, it's best to first
come up with what all the possible cases are
that need to be considered. What happens when
the array is empty, meaning there are no items within range for the
player to pick up. We should return null,
meaning nothing. If there is only one
item in the array, we will first have
to check if there is an unobstructed path between this node and the item
node using the ray cast. Setting the ray casts target position to
be the difference between the items
and the ray cast nodes global positions. The ray points from
its own origin directly to the items origin. After changing what the
raycast is pointing towards, the collision
information will not be updated until the
next physics process, but we can force it to update immediately with
a function call. If the raycast hits the
item, then we can return it. Otherwise, we will
still return null, since the only item in the array is
obstructed by terrain. Let's make this code reusable by turning it into
a private function. I'll call it obstructed, accepting the item
as a parameter and returning a boolean if the ray cast was
obstructed or not. Since these cases will cover the vast majority of times when the player presses
the interact button, they should be checked first and handled as
efficiently as possible. In the rare
circumstance that there are two or more
items in the array, then we will need to compare
the distance of each to the player in order to decide which one is
the nearest one, Vastly increasing the
complexity of the algorithm. We'll need a few
extra variables now, the array index of
the nerest item, the distance to
the nearrest item, and the distance to the
next item in the array. After all of our
calculations are done, we can return the item at the
index of the nearest item. When comparing
distances like this, it's more mathematically
efficient to use the distance squared
instead of the distance. Since the distance between two
points in three D space is the square root of x squared
plus y squared plus squared, then d squared is equal to x squared plus y
squared plus squared. Removing the need to calculate the square root of this number, and if d one is
smaller than d two, then d one squared is also
smaller than d two squared. We can start by
setting the index to zero and the distance to the nearrest item to something larger than the pick
up radius squared, so any item within the pick up radius will be considered nearrest by default. Since my radius is two,
I'll just use five. For each item in the array, we need to determine the
distance squared to it. Using the global
position of this node, distance squared to
the global position of the item at index. Then compare this distance
to the nearest distance. If it's smaller, then also check if the item
is unobstructed. If this is true, then this is the nearrest distance
that we've found so far, and I is the index of
the nearest item so far. Note that Boolean operations
are run from left to right. Comparing the distances
will be completed first, which is a fairly efficient
mathematical calculation. If the distance to
the next item is not smaller than the
nearest distance, then it doesn't matter if
the item is not obstructed. The Boolean operation and
will start with false, and therefore, not care what the other side
turns out to be, since false and anything
is still false, skipping the ray cast entirely, which is a more costly
calculation to run. Carefully choosing the order of operations from common
and simple checks and putting off the more
complex or rarer cases until last where they
might be skipped, can help your algorithms
run more smoothly. We will repeat this until we
reach the end of the array. Then we will have
the array index of the nearest item
to the player and can return the item
to be picked up. This should be not equal to. The player needs an inventory
to store their items, which will be part of their
progress information. Let's declare a private
variable named inventory as a dictionary and
initialize it to be empty using braces instead
of square brackets. A dictionary is a collection of key value pairs,
similar to an array. If you consider the array
index to be the key, but a dictionary can use
anything as its key. As long as every key is unique. Using the items display
name as the key, we can easily stack items by
using quantity as the value. This is a very simplistic
inventory structure, but sufficient for
this demonstration. For now, we'll dirite a function
named add to inventory, accepting an item to be added to the player's inventory and an optional quantity with
a default value of one. First, checking if the item already exists in the
player's inventory. We can increase the value at
this key by the quantity. If the item doesn't already exist in the player's inventory, then we can set the value
to be the quantity, which also creates this
entry in the dictionary. At the end, we'll just print out the inventory so we can see it. In the players
script, we can give the interact button multiple
context sensitive uses. Grabbing a reference to
the item pick up radius, we'll also declare a variable to hold the nearest
item to the player. When pressing the
interact button, we'll first assign the value
of the nearest item to be the nearest item returned by the pick up radius
using our algorithm. If the pick up radius
script returns an item, anything other than null, then we should
pick up that item, adding it to the
player's inventory, and queue the item to be freed, removing it from the scene tree. But if there is no item
in range to pick up, the script returned null, then we can proceed with the character
interaction command. What if the player
is both facing an NPC they want to talk to, and there is an item they
can pick up at their feet. When creating context
sensitive commands, consider which
contacts are given priority and how that will affect the player's experience. In this case, pressing
the interact button will pick up the item first. Then the player
will have to press the button again to
talk to the MPC. Sometimes it might
be better to use a different button if it will be more convenient
for the player. Okay. If we run the game, stand behind the wall and
press the interact button, the item is obstructed and we have failed to
pick up the item. Moving into the room, pressing the interact button
without any obstruction. The two health potions and the ax are added to the
player's inventory, each with a separate
button press and displayed in the output log. The health potions
are also stacked together in a single
dictionary entry. We now have the player
picking up items with a button press and adding
them to their inventory. In the next lesson,
we'll display the player's inventory
on the Canvas. I'll see you in the next lesson.
5. Inventory: Hello, friends. In
the previous lesson, we added items to the player's inventory
with a button press. In this lesson, we'll display the contents of the player's
inventory in a menu. A good place to start
with menus is to assemble a mock up of what information you want to display
to the player. This can help give
you an idea of how best to use the
space available and maybe make you consider things you wouldn't have
thought to include otherwise. Let's start with a
basic panel node and rename it to inventory. We'll probably want
a few label nodes, a title for this panel to tell the player
clearly what it's for, as well as an item name and item description of the
currently selected item. The title will just
say inventory, and I'll use dummy text for the item name and description. I'll have both wraps so they don't spell
outside the panel. And also a container, usually either a vertical
box container or a grid container to
organize all of the items. Whichever container
you're going to use, it will likely be
filled with buttons, each representing an
item or a stack of items. Let's add one now. Next, let's create a
theme for this menu. I'll name it inventory. Make a new resource folder for themes and put this in there along with
the existing theme. Expanding the theme in
the inspector panel, I'll change the default font to the more legible
font I'm using for my game and set the
default font size to 96, which won't actually
be that large after I scale down the panel. In the theme tab, I'll
start by adding the panel. Then give the panel a
new style box texture. Clicking on the
style box texture to open it in the Inspector tab. I'll use the gray box
square image with texture margins of 128
pixels on all sides. The image is very large, so I'm going to scale it
down to a quarter size. Then set the panels
size to double the screen resolution
of 12 80 by 720. It takes up half the screen
in both width and height. Setting the pivot
to be the center by using half of
the size property, then anchoring the panel to
the center of the screen. With the panel in position, I can now anchor
each of the elements to different
positions within it. I'll anchor the title
to be centered within the panel horizontally and
10% down from the top. Growing in both directions
horizontally and vertically, so it is centered about
the anchor point. I'll have it used
the fancier font and a larger font size of 128. The item name and
item description, I'll put inside a
vertical box container. Then use custom anchors to position it on the right
side of the panel, covering everything
60-95% horizontally and 20 to 90% vertically. Re setting the anchor
offsets will snap the boundaries of the control
to match the anchor points. I'll use a theme
override to space the mode and extra 64
pixels from each other. The label node wants a custom
minimum size to be set, so I'll just set them to 64 pixels tall to get
rid of the warnings. That leaves the left half of the panel available for
the item list or grid. Again, using custom anchors, I'll have the grid
cover 5% to 58% horizontally and 20
to 90% vertically. I'll give my sample button
a custom minimum size of 256 by 256 pixels. Then also change the font of the item name to make it stand out more from the description. Adding buttons to
the inventory theme, we can also change the
appearance of the item buttons. I'll use the same gray box
for the buttons as the panel. I'll use the same style box for when the button is pressed. Fate it when it's disabled. Change the button to
white when it has focus, or yellow when
it's hovered over. I'll add a texture
wrecked and label to the button to display an
icon to represent the item, and a counter for how many of that item the player
currently has. I want the icon to maintain its aspect ratio and stay anchored to the
center of the button. As for the label node. I'll set it to 99, so I
can see how it looks. I'll anchor the counter to
the lower right corner of the button and give it a thick outline to stand
out against the icon. But when it's a
single digit number, I'll want it to be
centered horizontally. From my icons, I'll be using a sprite sheet made by
Shashi on itch dot IO. Using an atlas texture, we can define this texture
as a single s from a sheet. Setting the textur property
as the Atlas texture, we can then click on
the dit region button, then slice the sprite sheet
into individual sprites. Selecting one to act
as a placeholder, I'll use a health potion and scale it up to
fill the button. These assets are pixel art, which shouldn't be
blurry like this. I'll need to change
the texture filter to nearest neighbor to get the appropriate look for this icon. I'll set its size
to 32 by 32 pixels, and it's pivot to
be in its center, so it sits nicely
within the button. Now that we have a sample item, let's duplicate it
a bunch of times to simulate what a full
inventory will look like. The grid container will need to have more than one column. Five columns fits
nicely into the space. But we can still only
hold 20 items inside the grid container before the items will overflow
outside the panel. Remembering the anchor values
we used for this container. If we add a scroll container, then copy the same anchors for the scroll container as we
had for the grid container. Then nest to the grid container inside the scroll container. Scrolling will be
automatically enabled to allow any number
of items to be held. I'll reset the anchor offsets of the scroll view container to force it to stay
inside the anchors. We can now delete all but one
of the placeholder buttons. The last button we should
save as its own scene. One that we can
use as a blueprint to generate the
player's inventory. I'll save it in a new scenes
folder for UI scenes. Then move the settings
menu into this folder too. Next, we can delete
the last button and remove all of the
placeholder text, leaving us with an
empty inventory screen. Our inventory panel should also include a closed button
if we're planning on releasing this game on
PC. Let's add a button. Re. And anchor it to the top
right corner of the panel. Our theme already has
a setting for buttons, but we can change how
this button looks without affecting other buttons
using a type variation. But add a new type to
our inventory theme, and this time just type a custom name for
our closed button. In the last tab of our new type, we can specify a base
type for this type, which will be button. Then switching to
the style box tab, we can overwrite
their values to use different style boxes
specifically for closed buttons. Using the pre made red X button from the UiPaC for the
normal button state, we can then select the
closed button node and set its type variation
to close button. It will use that
theme setting in place of the normal
button settings. I'll set its size to be 128 pixels and re anchor it
to the top right corner. I'll just use the same style box for every button state
for the closed button. It's attach a script to
our inventory panel, name it inventory, and save
it in the UI scripts folder. It can inherit from a class we wrote in the essential
course menu. Inheriting from the menu class, we'll already handle opening
and closing the menu, give focus to a default focus
item when the menu opens, and provide
breadcrumbs if we make this menu accessible
from another menu. I'd like to override
these behaviors, however, so I can disable the player node while the inventory is open. Copying the definitions of open and close into the
inventory script, and calling the
super definitions. Our scene tree is getting
rather large and complex now, so the node path between the inventory and player node will require going
up multiple levels. When accessing a unique node
through a complex node path, it's better to mark
the node as unique. Right clicking on
the player node, select access as unique name. The node is marked with
a percentage sign. Now when we grab a reference
to it using add on re, the node path is replaced
with the percent sign and is now a direct reference to this specific unique node. When opening the, we can set the enabled property of
the player node to falls, so the player can't
move or jump. And when the closed, if there are no bread crumbs, then we can re enable
the player node so the player can continue
playing the game. We'll also need to
add a button to the input map of the
project settings for opening the inventory. I'll use the backspace key or the select button
on my controller. In the player script, if the
inventory button is pressed, we can tell the game manager
to toggle the invent. But the player must
be able to close the inventory while the
player node is disabled, so that check will need to be separated from the
pause functionality. In the game manager script, grabbing a reference
to the inventory menu. We can open it if it's closed
or close it if it's open. The inventory script
then needs to be able to tell the game manager
if it is open or not. So Declaring a Boolean variable, it will naturally
default to false, and we can set it when the
inventory opens or closes. When the inventory
menu is first opened, the player's inventory is
likely going to be empty, so there's not going to be
anything that can grab focus. We can add an if statement to the menu script
to first check if the default focus item has been set before telling
it to grab focus. Now the inventory
which does not have a default focus item will not bother trying to grab
focus when it is empty. L et's connect the
closed buttons pressed signal to the inventory
menus close function. And set the visible property
of the inventory default, so it is closed by default. Let's try it out.
Opening the menu, the player's inventory is empty, and the player's movement and
jumping have been disabled. But we can still
close the inventory with the same button
used to open it, which re enables the
player controls. We now have an inventory
menu in our game that we can use to display the contents
of the player's inventory. And the next lesson we'll
populate this menu with the players items
and allow the player to select them to get more
information about them. I'll see you in the next lesson.
6. Items: Hello, friends. In
the previous lesson, we added an inventory
menu to our game. In this lesson, we'll add item buttons to the
inventory menu. Our items are going to need a lot more information
than just a display name, and we don't want this
information to be bound to the three D scene
and model of the item. Since when the item is in
the player's inventory, it won't be drawn in three D, but instead it will be drawn in two D inside the inventory menu. We can bundle all of
the information about our items together into a
custom resource script. I'll put it in the
custom resources folder and create a
new folder for items. I'll name it item
inheriting from resource. Then at the top of the script, we'll give this script
a class name item. Here we can export any variables we need to describe the item, like its name and description, as well as a Texture two D for its icon to be drawn in
the player's inventory. And we can also include a
three D scene of the item, but we don't need this yet, and if done incorrectly,
we'll break your project. For now, let's
just add a comment saying to include this later. Save the script,
so the engine will recognize item as
a resource class. Right clicking in the
new items folder, we can now create a new
resource of type item, and name it after the item.
I'll start with the At. I will require
that every item in my game have a
unique name and that the resource file
of that item also use the exact spelling of
that name for simplicity. The resource should
open automatically in the inspector panel
when it is created. But if not, it can be opened
by double clicking on it. We can then view and
edit its properties. Giving it a name
and a description, we can populate the icon
with an atlas texture, selecting the appropriate
sprite from the sprite sheet. I'll change the
sprite slicing to a grid of 32 by 32 pixels, and select the x icon. I'll create the health
potion item resource as well with a name, description, and an icon. But I want the health
potion to be stackable. We can use inheritance to give our items
additional properties. Let's create another
custom resource inheriting from item
named stackable. This script will add
a new class name, stackable extending item, and it will just export
one additional variable, the stack limit of the item, which I will default to 99. Double clicking on the
Health potent resource, under the Rf counted section, we can change the
script this resource inherits from to the
stackable script. Now we can set the
stack size limit. Some other variables may have their values reset
when doing this, and we will need to
repopulate them. Next, we should open
up the item scenes and edit the old item scripts
attached to them. These scripts no longer
need a display name, but instead a reference
to the item resource. Which we can then assign in
the inspector for each item. Next, now that we have a
proper inventory script, let's go to the progress script, make the inventory
variable public and move the add to inventory function to the inventory script
where it belongs. I'll rename it to add item, accepting an item as a
parameter instead of a node three d and leaving the quantity
parameter as it is. Back in the Progress
resource script, I would like to allow
multiple copies of the same item to appear in
the player's inventory, which would be somewhat more
difficult with a dictionary. So I'll change the type of the inventory
variable to an array. But each element in the
inventory array still needs an identifier
the item's name, and if the item is stackable, then also a quantity. So the inventory
will be an array of dictionaries with
each dictionary containing the item name and possibly other details like quantity in that inventory slot. Any specific details about any item that can be changed and needs to be saved
would also need to be included as a
dictionary entry. If the player picks up 98
health potions and two axes, the inventory will end up
looking something like this. In the inventory script, we will need to be able to add item buttons to
the inventory menu that represent each item or stack of items in the
player's inventory. For that, we will need a
prefabricated item button. The one we created in
the previous lesson and saved in the
UI scenes folder. Dragging a scene
file into a script while holding down the
control or command key, will declare a constant to hold it and preload the scene
with a function call, returning a packed scene type. Constants are conventionally
named in all caps, and hold values, which by
definition never change. Let's call this prefab. Since it holds a blueprint
for a prefabricated button, we will be mass producing. Writing a function
which will add a new item button to
the inventory menu, it will accept an item and
a quantity as parameters. Declaring a new item
button variable, we will instantiate a copy
of the prefabricated button. Then change the icon
nodes texture property to the icon belonging
to this item. If the quantity is
anything other than one, I'll set the text
property of the buttons label node to be the quantity
cast to a string type. And if it is one, set its visible property
to false to hide it. We will also need a reference to the container node which
holds the buttons. We don't need to
specify the type of container holding or items, whether it is a
vertical box or a grid, so it will still work if we
want to change it later. After the button
has been created, we just need to add
it to the container for it to be drawn in the menu. When the game scene first loads, we will need to initialize the player's
inventory to already be populated with the contents of the player's inventory items. Iterating through each item
in the player's inventory, we will add a new item button to the container for each entry. Since the value stored in the player's inventory is
a string name of the item, we will need to
load the resource containing the actual
item information. Dragging any item resource from the file system tab into the script will give
us a path to it. We can then replace
the item name in this string with the name of the item in the
player's inventory, since they are all named
exactly as their item name. If you want multiple
items to have the same name but
different properties, then the value stored in the inventory would need
to be a unique file name, not the display name. We'll also need the quantity of the item if the item
has a quantity value. Otherwise, we can just
assume a quantity of one. This will generate the
buttons to display the 98 potions and two axes the game has
been told to start with. When adding an item to
the player's inventory, we can first determine if the
item is stackable or not, since the process
will differ greatly. Then call separate private
functions for adding a stackable item
or a single item. Adding a single item,
we'll just push a new dictionary onto the back of the player's
inventory array. A dictionary containing
a name, the item name. We can then add a new item
button for this item. Let's make the quantity optional by giving it a
default value of one. If the item is stackable, the process is much
more complicated. We first need to search
through the players inventory to find existing
stacks of the same item. Check if those stacks have room for the items being added, check if the stack
limit is exceeded, and repeat until either all
the items have been added to the existing stacks or every
existing stack is full. If there are any items
still left to be added, we will then need to create
new stacks to hold them. We will need to iterate through each stack in the
player's inventory. Checking if the name
of the items stored in slot I is the same
as this item name. Then we need to check if
there's any more room in the stack by comparing the quantity of the stack
to the stack size limit. Then we can add the
quantity to the stack. Then we can set
the quantity to be the stack quantity minus
the stack size limit, which is now the
number of items which are in excess of the
stack size limit. So if quantity is now
anything greater than zero, the stack size limit
has been exceeded, and we can set the stack
size to the limit. If the quantity in the stack
did not exceed the limit, we are done and can
break out of the loop. Quantity now contains the items yet to be added to a stack, and the loop will continue
adding items to stacks until the quantity has been used
up or every stack is full. After this loop is completed, if there is any
quantity remaining, we will need to generate new
item stacks to hold them. Checking if quantity is less than or equal to
the stack limit. We can generate a stack
of size quantity. Then set quantity to zero. Otherwise, we can create
a new full stack and reduce quantity by the stack
limit amount and repeat. Adding new buttons to the inventory menu for
the new stacks. Anytime the quantity of
items in a stack is changed, we'll need to update the label on the button to
match the new value. So let's create a new
private function that accepts the button and
the new quantity as parameters with
the button having the same array index within the containers children
as the inventory index. The quantity will
also need to be updated before breaking
out of the loop. Defining this function,
accepting the same parameters. We have already
written this code when creating our
new item buttons. Let's move this code into our new function, and
call it here too. In the player script, we can use the same unique name method to grab a reference to the
player's inventory. Then when pressing
the interact button, add the resource attached to the nearest item to the
player's inventory. Returning to the
inventory script, To display the
items information, we will also need to
grab references to the item name and item
description nodes. Let's write another
function that displays an item's information on the
right side of the panel, accepting an item
as a parameter. Giving it a default
value of null, we can also use this to show
no information as well. Setting the item names
text property to the item name if the
item is not null, otherwise an empty string, and same for the description. Anytime a new item button
is added to the menu, we can connect its focus entered
signal to this function. But the function requires
an item as an argument, and the signal does not
have any arguments. We can bind arguments to a
callable with a function call, passing the item itself. Lastly, when opening
the inventory menu, if the container
has any children, we should tell the top
child to grab focus. And if it doesn't have children, display item information,
passing null is the argument. L et's try it out. We start with 98 health potions and
two axes in our inventory, and the health potions
information grabbed focus, displaying the
information on the right. After picking up the
items on the table, one health potion was added to the stack of 98 filling it up, while the other started a new stack along
with the other ax. Since the buttons have focus, they're also navigable with the keyboard arrow keys or the controller's
directional pad. We now have the players
inventory displayed in the menu. In the next lesson
will allow the player to drop or consume
items in the inventory. I'll see you in the next lesson.
7. Consumables: Hello, friends. In
the previous lesson, we added items to the
player's inventory and displayed item information. In this lesson, we'll
allow the player to drop or consume items
in their inventory. In the inventory menu, we will need to
present choices to the player of their options
for the selected item. To keep things simple, I'll just have two buttons below the item description organized into a horizontal box container, and rename them
auxiliary and Drop. Removing the style boxes by
overriding the button theme, I'll just have the
left button say use and the right one say drop. Not every item in the
game will be usable, so it doesn't make
sense for the button to say use when the ax is selected. It would make more sense
for it to say equip. This button will be context sensitive based on the type
of item that is selected. I want these buttons
to be evenly spaced out at the
bottom of the panel. I'll set their
horizontal alignment to align with expand and fill. Then also reset the
anchor offsets of the vertical box to keep it
contained within the anchors. And also tell the
description to align with expand and fill the vertical
box as much as it can, pushing the buttons to
the bottom of the panel. T. In the project settings, I'll add input mappings
for Ue item and drop item. Then set the x button
on the Controller for use and the Y button on
my Controller for drop. I'll add these
button indicators to the text properties of
the buttons as well. So the player knows which button on the
controller does what. PC players can simply
click on the buttons. I would remove the X or
Y text and probably add more style boxes
for these buttons for the PC export of the game. In the item resource script, we can add a Boolean
variable that specifies whether
or not an item is usable and another to specify if the item is
consumed when it is used. While we're here, we'll
also need access to the three D scene
containing the actual item, so that when the
player drops the item, we can spawn it into the scene, or if they use the item, we can actually make it appear
at the character's hand. We can't both give the three
D scene a reference to the resource file and also give the resource file a reference
to the three D scene. This is a cyclical
reference and we'll create an infinite loop that breaks any scene which
contains the item. T. Instead, we will just tell the item resource where to find the three D scene, giving it the path to it as a string so we can load
it when it's needed. Then in the health
potion resource file, we can copy the path of the health potion scene and paste it into the scene field. We can set it to be usable
and consumed on use. I don't have any plans
in mind to implement an item which is not
consumed when it is used, but it's a good idea
to make code flexible. And repeat this for
the other item. In the inventory script. We'll need to grab
a reference to the action buttons
using add on ready. When an item button is focused and item information
is displayed, we can set the text property of the auxiliary button to say, if the item is able and set its disabled
property to false. Otherwise, I'll just
disable the button for now. Let's connect the
buttons pressed signals to the inventory script. I'll also add an input
function which will tell the auxiliary button's
pressed signal to emit when the use item
button is pressed. But only if the
inventory menu is open, and the button is enabled. And do the same if the drop item button is pressed
on the controller. I normally wouldn't
put an input function directly into a
script like this, but for simple shortcuts
to press buttons, I think it's fine
to skip the step of using a separate input
handling script. If either the x button
is pressed using the controller or the use button in the menu is
clicked with a mouse, this function will be
called in either case. But we need to know
which item is selected. And it might also be important to know which specific item in the player's
inventory if they have multiple of the same item. Anytime an item grabs focus and its information
is displayed, let's also accept
the button which represents that item
in the inventory menu. Giving it a default
value of null, so it will be optional, we can also bind
this argument to the callable passing the new item
button as another argument. Likewise, when these
buttons lose focus, it would be a good
idea to display no information and
disable the buttons. Let's also connect their exited signals to
the same function, binding null as the arguments. If no item is selected, then we can't check
if it's usable, and we should disable
both buttons. Declaring new private
variables to hold the selected item and its
corresponding button, We can store the selected item and its button when
it's selected. When these action
buttons grab focus, I would like to tell the item
button to grab focus back immediately so the player always knows which
item is selected. Connecting their focused
entered signals, which can both call
the same function. Let's call it on action
button focus entered. But by these action
buttons entering focus, the selected button has at
the same moment exited focus. The selected button is now null. Let's declare another variable called previously
selected button that will be set to the
selected button every time the selected
buttons value changes. So I focus changes from an item button to
an action button, the selected button
will be set to null. But the previous selected button will retain its reference, so we can tell the previously selected button to grab focus. This way, the player will
still be able to see which item is selected if they click on the
action buttons. Now, when either
action is taken, we can easily access the
button which is selected, as well as the item that button represents in the
player's inventory. To drop an item, if
an item is selected, we can first declare a
variable to hold an instance of that item and set
its value to load, passing the path to
the scene as stored in the items resource file
and instantiate it. Then add this item
to the scene tree. You'll probably want it to be a child of the level node to be automatically removed along with the level when
it's unloaded. For simplicity, I'm just going to add it to the game manager. Declaring a variable
to represent a random direction
along the xd plane, it will be a random number
between zero and T. To represents the full
circumference of a circle. We'll then set the global
position of the item to be near the player character's
global position slightly above the ground, applying sine and cosine to the direction to get the x and Z values of the new position. Let's first make sure
we have a reference to the player character
using a unique name. And remove the dropped item
from the player's inventory, passing the item being removed, a quantity of one, and the selected button which
represents the specific item. If the player presses the
auxiliary action button, if there is an item selected
and that item is usable, we can call a separate
private function to use the selected item. As other uses of the auxiliary
button become relevant, then more functions
can be added here. To use an item. If the
player character is on the floor and they are not
busy doing anything else, I'll use the can move variable, even though it's
marked as private, then we can tell the
character to use the item. If the item is consumed on use, then we can remove it from
the player's inventory, passing the selected item, a quantity of one, and
its button as arguments. Since I will be adding
a e item animation to the character and I
want the player to see it, I'll close the player's
inventory upon requesting to use an item so that we can
see the animation play out. Removing an item from
the player's inventory is much the same as adding one with the behavior
being different for stackable versus
single items. So when removing a single item
if the button is not null, then we can go ahead and remove the inventory entry whose index matches this
button's child index in the array of the
containers children. And also remove the item button from the inventory
menu and return. I'll do this in another private
function to also maintain proper control of
the selected and previously selected
button references. If the button was not provided, then we'll need
to search through the players inventory
to find this item. Once we do find it,
we can remove it from the inventory and the
containers children and return. Before removing any item button, we'll need to check
if this button was selected or previously selected. And if so, set these
references to null, so we don't accidentally try to reference buttons
which no longer exist. We'll use free here
instead of Q free to make the effect immediate instead of waiting for the end
of the current frame. This way, in the
event of needing to remove multiple entries from
the player's inventory, the containers
children will still match up with the
inventory indices. But removing this button
which currently has focus, we will need to tell an
adjacent button to grab focus. So I'll call a private
function to switch focus to the next item
in the list here. How this is handled
will depend on the number of children the
container currently has. If there are fewer
than two children, then nothing will need to
grab focus and we can return. If no button was provided
as a reference point, we can just tell the top
child to grab focus. If a button was provided, we'll need to know its child
index within the container. If the next index is valid, meaning it is less than the
child count of the container, then the next child
can grab focus. Otherwise, this is
the last child, and we'll instead want the
previous child to grab focus. Let's combine this into
a turn re statement. We can then proceed
with removing the item button now that
it no longer has focus. Removing a stackable item will
be a bit more complicated. If a specific button
was provided, then we can get its child
index within the container. Then remove quantity from the stack in the
matching inventory slot. If the result of the subtraction is less than or equal to zero, then the stack is empty. Setting the new
quantity value to the negative of the
current stack size, this value will now
contain the amount that the stack was short of the quantity we were
trying to remove. So we can remove both
the inventory stack and the button which
represents it from the menu. If the result of the subtraction
was a positive number, then the stack is not empty, and we just need to update
its new quantity value. Then return since we are done. Now that we're done checking
the suggested button, if there is any quantity
left to remove, we will need to search through the player's inventory to find
more stacks of this item. We can then repeat the
same logic as above, using the index alias I instead of the
index of the button. And removing the matching button whose child index matches
the inventory index. If we ever reach a quantity
of zero, we're done. In a character scene, selecting the animation tree node inside the miscellaneous
state machine. We can add the use
item animation. Since like other
animations here, it also starts and
ends with the dopose. Using the same transition settings as the
other animations, setting the advanced mode of the transition into
the animation to enabled and the switch mode of the transition out
of the animation to at end. Pressing the play icon will allow us to
preview the animation. In the script, let's add a new public
function to use an item, accepting an item
as a parameter. For this, we will
need a reference to the character's main hand, so we know where to position
the item being used. This will also be the
main hand position used to equip weapons. So creating a new exported
category for equipment, I'll export a new variable for the character's main
hand slot with a type of bone attachment three D. Selecting my
character's root node. This main hand
equipment slot will be the same bone attachment node which previously held
the one handed x. When using an item, we will
declare a new variable to hold an instance of the item and assign its value to be load. Passing the path to the
three D scenes stored in the item resource as the
argument and instantiate it. This will create the item, which we will add as a child of the main hand equipment slot. But this item has
physics and collisions. It will collide
with the character and be affected by gravity. We'll need to set its
freeze property to true, so the engine will ignore
all of that and just treat it as if it were glued
to the character's hand. Next, we'll await the completion of the use item animation. Finally, queuing
the item instance to be freed from the scene tree, we are just going to assume that the character's hand is empty
before all this happens. This is where the effects of using the item
would be activated, but we'll just print
out a statement to act as a placeholder, so
we know it works. Minor corrections in
the inventory script. We can only remove the
suggested item button once. If removing multiple
single items, then we'll use null every
time after the first. And when telling the next
button to grab focus, we should not then set
it to null immediately after. Let's try it out. Opening the inventory,
we have 98 potions. We can use one which
closes the inventory, and we can see the character
hold a potion in hand, drink it, and it disappears. Re opening the inventory, we now have 97 potions, and we can drop some items, removing them from
the inventory, which are now scattered
on the floor around the character after
closing the inventory. We can even pick
the items back up and add them back into
the player's inventory. We now have items
able to be dropped or used from the
game's inventory menu. In the next lesson, we'll allow the player to equip
armor and weapons. I'll see you in the next lesson.
8. Equipment: Hello, friends. In
the previous lesson, we allowed the player
to use or drop items from their inventory
through the menu. In this lesson, we'll allow players to equip items
to their character. Each of our characters have several different items in
their hands, a cape and a hat. Except for the
rogue, whose hood is unfortunately not separate
from the rest of the model, so we can't use it as equipment. All of these can be turned into items following the
methods used in previous lessons
that we can pick up and add to the
player's inventory. Note that the rogue does not
have a hat bone attachment, and the mage does not have
an off hand bone attachment. We can fix this by adding our own bone attachment nodes to the character and attaching them to the correct bones
within the skeleton. In the Mags character scene, we can add a child node
to the skeleton of type bone attachment three D and rename it to
something like offhand. Then in the inspector, attach
this to the hand slot one, meaning left, and
now this node will automatically follow the
character's left hand in all of the animations. Dragging the book onto
this new bone attachment, we can see how it continues
to follow the left hand. Any other items
meant to be held in the character's hands can be reparented to the main hand or off hand equipment slots
before making them into their own scenes to maintain their relative positions
and rotations. We can then repeat
this process to add a hat bone attachment for the row character
following her head bone. I'll also reorganize and rename the nodes to match exactly
with that of the mage. The Mags hat slot is also different from
the other characters. It is positioned higher
and rotated slightly. To make our lives easier, let's add a new bone
attachment node that follows the Majes head and use that
as their hats lot instead. Dragging the hat asset from its existing bone attachment to the new one will also give us the exact position and rotation
difference of this hat. When we create the
Majes hat item scene, we can make this position and rotation a natural part of it. Using these methods, all of our characters should have the
same four equipment slots, and any item can be
equipped to any character. The equipment slots can be renamed for consistency,
if you prefer, and unused bone
attachment three N nodes can be deleted from
the character scenes. But the hair of some
of the characters will clip through
some of the hats. In some games, you'll notice
that hair is removed from the character when a helmet or hat is equipped to
fix this issue. In these assets, the hair
is part of the head model, so we will need to
either restrict who can wear which hat, scale or reposition the hat depending on who is wearing it, or simply ignore the issue. Especially when using
placeholder assets, it's better to take note of the limitations
and make sure that the final production
assets address the problems instead
of replicating them. Before we create any
new item resources, let's define a new
type of item resource. We'll call it equipment,
inheriting from item. This will allow us to
add a new variable to items that fall
under this category, the type of equipment
that it is, which will determine
how it can be equipped. In our projects enumerations, we can add an enumerated list of the different equipment
slots for our game, main hand, off hand, head, and back, as well as any other
equipment slots you want. The equipment type
enumeration can now be used as a type for the
equipment type variable, so we can easily set
it in the inspector. Switching the axis script
from item to equipment. It can be set to be equipment for the character's
main hand slot. In the character
script, we can add more variables for
each equipment slot or change the variable
into an array of bone attachments named sockets. When using an item, the main
hand is now socket zero. Then assign each bone
attachment socket in the same order in the
array for each character. Next, we can add new
public functions to tell the character to do or
do pieces of equipment. Donning a piece of equipment will require a
reference to the item, and doffing will only
require the slot. To D a piece of equipment
is much the same as instantiating the potion and putting it in the
character's hand. Replacing the main hand socket with whatever socket
the item is meant for. Dopping only requires us to check if the
socket has a child, and if so, get rid of it. Using an item, we can no longer assume that
their hands are empty. Let's declare a new
variable to contain what the character was holding before being told to use the item. If the main hand
socket has a child, then that is what
they were holding, and we should set
its visible property to falls to hide it
when they use the item. If they were holding something
before using the item, then we can set its
visible property back to true after using the
item has been completed. In the progress resource, we need to keep track of everything the
player has equipped. A simple way of
referencing items within the inventory would be to
use their array index. So our player's equipment is
an array of integers with each element in the
array containing the equipment's array index
in the player's inventory. Defaulting the contents of the array to negative
one for every entry, we can use negative one as a signifier that the
slot is empty. Now to connect all of this
to the inventory menu. When an item button is focused, we are displaying
its information, but we will need to create separate cases for when this
is a piece of equipment. Then further divide based on if this item is currently
equipped or not. In either case, the
auxiliary button is enabled, but the drop button should be disabled if the
item is equipped. Changing the auxiliary button to equip or equip accordingly. How do we know if the
selected item is equipped? We need to know its index
within the containers children, since that is the
number saved in the equipment array of
the progress resource. Then we can check if the
equipment array at the index of the selected items
designated slot matches this value and
return true or false? This can all be done in
a single line of code. Whether or not the drop
button is disabled will also need to be handled
for the consumable items and null cases. When the auxiliary
button is pressed, if the selected
item is equipment, then we can use the
same condition to either unequip or equip
the selected item. To equip an item,
we will be setting the equipment array in the
progress resource at index of the item slot to be the array index of the item within the
player's inventory. Then tell the character
to do the equipment. I'll repurpose the
label on the item but by setting its
default text to a capital E for equip and set its visible property
to when equipping it. I'll also update the
auxiliary buttons text and disable the drop button. To unequip an item, we will set the
equipment array at the index of the item
slot to negative one, since it is now empty, telling the character to dof whatever they have in
the matching socket. Hide the E for equipped
on the button, date the auxiliary buttons text and enable the drop button. But before equipping an item, we will need to check if an item is already equipped
in that slot, meaning that the value of
the equipment array at the index of the item slot is anything other
than negative one. In that case, we should tell the character to dof what
they have in that socket. Then also find the
button in the container whose index matches that of
the equipment array value, so we can hide the
E for equipped. We can then proceed with
equipping the selected item. In order for this
to work, the values in the equipment array must always match the array indices of the items in the
player's inventory. But the array indices change every time an item
button is removed. Let's require the array
index as a parameter when removing the item buttons
from the inventory menu, since we already find
that every time anyway. Then we can search through
the equipment array for any values which are larger than the index of the
item being removed. If there are, then they
can be reduced by one, so they still match the
index of the equipped item. In my progress resource, I'll start the player out
with a bunch of new items in their inventory and
also have some of them. After first initializing
the player's inventory, we can also iterate through
the equipment array. If the equipment slot is anything other
than negative one, then we can find the button whose index matches
the value stored here. To make things simple, I'll
just use existing functions, telling the button to grab focus and equipping
the selected item. Now when the inventory
is initialized, so too, is the player equipped
with items as stored in their file. Let's try it out. The character starts
off equipped with a few items and opening the inventory we have
several to choose from. We can unequip items,
equip new ones. If we try to equip an
item in an occupied slot, the equipped item is
unequipped before switching. If we drink a potion, the
character's main hand item is hidden during the animation and comes back
after they're done. We now have items
being equipped to the player character
through the inventory menu. In the next lesson, we'll allow the player to trade
items with an NPC. I'll see you in the next lesson.
9. Shop: Hello, friends. In
the previous lesson, we allowed the player to
equip items from the menu. In this lesson, we'll have them buy and sell items from an NPC. To save time, I'm going to reuse the same inventory menu
to create the shop. I'll rename the vertical
box container to info and duplicate it to create another control with the
same anchors and size, and rename it to shop. Instead of the item's
name and description, the shop will contain
a scroll container, holding a grid
container of the shops stock just like the one
holding the player's items. The scroll container
can fill the space, pushing the buttons
to the bottom. In place of the
auxiliary button, I'll have a coin icon, the price of the
currently selected item, and a context sensitive
transaction button to buy or sell the
selected item. The coin icon can keep
its aspect ratio, but fit proportionally
within the space provided. I'll figure out the maximum amount of space the price could possibly occupy and use that
as its custom minimum size. Then the button can use the
remainder of the space. I'll also add 32 pixels of separation between these
three controls within the horizontal box container and change the text property
of the transaction to say. Let's fill the shop stock with a bunch of dummy items
to see how it looks. The grid columns should fit
inside the space nicely. To switch between inventory
view and shop view, all we need to do is toggle the visibility between
the info and shop nodes, and we can also change
the title as well. When you're satisfied with
your shop minus layout, you can delete the dummy
items from the shop stock. In the script, I'll
need to update the node path to the info
node to reflect its new name. We will need references
to the title, the info, and shop nodes, so we can swap them
back and forth, and also the container which
will hold the shop stock, the price label, and
the transaction button. Our inventory script is
getting a bit large. A useful way to
keep longer scripts organized is to create code
regions using comments. With a region comment above
a block of code naming the region and an
end region comment at the bottom of the region. The region can be collapsed and expanded to better
organize the script. Let's add a new
public function which opens the inventory
menu as a shop. We'll change the title to say
shop instead of inventory, set the visible property
of the info view to false and the visibility
of the shop view to true. We can then call our
existing open function to also do the rest
of this other stuff. We'll need to also
create a function to open the menu in inventory mode, resetting the title
back to inventory and reversing the visibility of
the info and shop nodes. The game manager will
open the menu as inventory when the inventory
button is pressed. Using the event
management system we built in the previous course, we can attach an
area three D node to any character in our level
scene and rename it shop. Adding a collision shape
three D as a child, we can give it a size
and shape that is easy for the player to target
with their interact recast. The area three D
will not need to monitor anything
but be monitorable existing on the collision
layer specifically for interactions and
have no collision mask. For more information
on interaction events, see the previous dialogue
and events course. Let's create a new
script for this node, saved in the event
scripts folder. Inheriting from
interaction event. In this script, we can
export an array of items to represent the stock of items that are available for purchase. Like any event script, we'll write a run
event function, accepting the event
manager as a parameter, which contains all
the object references our events might need
to do their job. In this case, the event manager
will need a reference to the player's inventory and the shop if you're using
a separate menu for it. This will also allow the event
manager to add or remove items from the
player's inventory during events or
dialogue if needed. The shop script can then ask the event manager to
open the shop menu. Passing a reference to
itself is an argument, so the inventory
will have access to the stock and any other public
variables you may want. Let's give this a
class name of shop, so the inventory script
can accept a shop as a parameter when opening
the menu as a shop. To use the await command
during the event, our open as shop function also needs to return
a signal to await. Accepting the shop
as a parameter, we can store a reference to it when opening the
inventory menu as a shop. The signal returned will be when the inventory menu is closed. We can emit that signal
anytime the window is closed, even if nobody is
listening for it, when the menu is opened as
the player's inventory. To make our shops more unique, let's also export a line of
dialogue for the vendor as an intro and another
as an outro. Then add these to the
run event function as extra dialogue before and
after the shop is accessed. And tell the game manager
to end the event. We can even export a
value multiplier named markup to give different vendors different prices
for their items. I'll set the default markup
to be ten times the value. Let's populate the
nights shop to include the health
potion and the ax. A as well as an intro and
outro line of dialogue. In our item custom resource, we can give each item a value as an integer with a default
value of one coin. Each item in the game can then be assigned a
different value, which in this case, will be how much the player can
sell an item for. But we'll purchase at
ten times that price. Back in the inventory script, it would also be useful to
display the player's coins. Let's make that a unique
node and grab that as well. We can open the wallet counter when the inventory is opened, and tell it to stay open by
passing true as an argument. Then tell it to close when
the inventory is closed. Unlike with the player's
inventory where we initialize the
many buttons onload, then add or remove buttons as items are added or removed
from the inventory. The shop stock will need to be generated each time the player
interacts with the vendor, and completely cleared out
when they are done shopping. Clearing the stock is
quite straightforward, just iterating through
each of the children of the stock container and queuing them to be freed
from the scene tree. We can also set shop to null here or in the
closed function. We can clear the stock anytime the inventory menu is closed. If the inventory menu was opened as an inventor and
not as a shop, the shop stock contents
will be empty anyway. Generating the stock is
much the same process as generating the player's
initial inventory, iterating through each item
in the shop's stock array, then calling add item button. But we'll need a new argument to specify which container the
button is being added to. Then add the parameter to
the function to match, giving it a default value of
the player's item container. This information of which
container this button is in will also be relevant when displaying the
items information, as well as when the
transaction button is pressed. We can store this information
inside the nodes metadata. Let's add a new entry to the metadata of our
newly created button, with the name is in inventory, with a value of whether
the container it is being added to is equal to the
player's item container. Now every button knows which side of the
shop menu it's on. When displaying an
items information, if there is a selected button, we can get its metadata using the same identifier
is in inventory. If the item is in the
player's inventory, then we can set the price
label to the value of the item and set the text property of the
transaction button to x sell. To keep things simple, I won't allow the player to
sell any items they have equipped and disable the
transaction button in that case. If the item is in
the shop's stock, then the price text will be the items value multiplied
by the shop's markup. And the transaction buttons
text changed to x by. Whether or not the button
is disabled will depend on whether the player's
current coins are less than the price. When checking if the
selected item is equipped, let's make sure that
there is a selected item and that it is also a
piece of equipment. In the input function, if the shop variable
is not null, we can return to prevent the use item or drop item
functions from being checked. If the use item
button is pressed, which we are repurposing here to trigger the buy
or sell action, as long as the transaction
button is not disabled, we can emit its pressed signal. Connecting the
transaction button's pressed signal to the script, I'll move it into
the shop region. If no item is selected or no shop is currently
being interacted with, then we can simply return. If the selected button is
in the player's inventory, then the transaction button will sell the currently
selected item. Increasing the player's coins
by the value of the item, and removing it from
the player's inventory. Otherwise, the selected button must be in the shop's stock, which means the transaction
button is going to buy it, reducing the player's
coins by the value of the item multiplied
by the shop's markup, adding the item to the
player's inventory, and re checking whether or not the buy button
should be disabled. A small correction when
updating the quantity on a button will need to set its
visible property to true. In the progress script. I'll start the player
off with some money. I'll also ignore the narrow
wing conversion warning generated by casting this
float into an integer. Remember that the inventory
menu should start closed. Let's try it out. Speaking with the night,
we are welcomed with a line of dialogue
before the shop opens, which also displays our coins. The transaction button displays sell when selecting our items. But it is disabled when the
selected item is equipped. We can sell items
from our inventory, which gives us more gold. The transaction
button displays buy when selecting the
shop stock items, but is disabled when we do not have enough money
to buy the item. We can purchase items from
the vendor spending our gold. And when we close the shop
nu, the night thanks us. We now have a shop in
our game so the player can buy and sell
items from an NPC.
10. What's Next?: Hello, friends. Before
we get started, you should have a project with a player character that can move around in an environment, and some enemies
for them to fight. You may also want to
have an inventory menu capable of equipping items, including at least
a mala weapon, a arranged weapon, and a shield. Throughout this course, we
will add controls, mechanics, and animations to our project
for locking onto a target, attacking, dodging, blocking,
and shooting projectiles.