Hey guys, what's up?
Kryzarel again.
Let's continue with our items and inventory implementation.
We have the base for our inventory, but there's currently no way to add items to it other
than dragging them in through the inspector.
That's no good.
We need some methods for Adding and Removing items.
The first thing that comes to mind is checking if the inventory is full before we can add
anything.
So let's make our function return a bool.
To indicate wether or not we were able to add the item.
We'll check if our inventory is full, and if so, return false, otherwise,
add the item to the "items" list, refresh the UI and return true.
How do we know if the inventory is full?
That's easy, just check if the number of items in our list - so items.Count - is bigger than
or equal to the number of item slots - itemSlots.Length.
Now to remove items, let's also return a bool.
If we were able to remove the item from the list, refresh the UI and return true,
otherwise, return false.
Let's move on to the equipment.
We know our armor and weapons are going to need variables for the bonus stats they grant
our characters.
But adding those to the base Item class is a bad idea, we probably don't want every single
item in our game to be equippable.
And we don't want our non-equippable items to have a "Bonus Strength" variable or something,
because it won't do anything there.
We can solve this by making a new class that extends from Item.
There's also some consideration to be made for a component based system.
But in this case I opted for the subclass approach.
We'll call it EquippableItem.
Don't forget the "CreateAssetMenu" attribute.
In the character stats video we used an example with 4 stats, Strength, Agility, Intelligence
and Vitality.
We'll use those same stats here.
There was also an item that granted percentage bonuses instead of "Flat" ones.
So add variables for that too.
The "[Space]" between brackets is a handy Unity attribute that inserts a space between
variables in the inspector.
Makes it much more readable.
The other thing we also need, is to know what type of equipment this is.
Is it a chest armor, helmet, weapon, whatever.
We can define this inside an enum.
Let's call it EquipmentType and add a few values.
And don't forget to add a variable for the EquipmentType in the class, along with the
stat bonuses.
Now, our helmet should really be an EquippableItem, instead of regular one, so let's delete it
and create a new one.
When we're done, just drag it back to the inventory.
OK, we want to be able to equip these items, so we have to set up the equipment panel.
We're gonna need item slots, just like in the inventory,
but these slots need to have an extra field, for the equipment type they can hold.
So once again, make a subclass, this time extending from ItemSlot, and add
an EquipmentType variable.
Let's also use the OnValidate method to name the objects for us using the value of EquipmentType.
But we want to keep the automatic image assignment of the base class, so we need to make its
OnValidate protected and virtual.
We also need to call the base class's OnValidate from this one.
Let's set them up in the UI.
I'll speed this part up so you don't get bored, but I'll show all the values at the end one
by one, and you can pause if you want to copy them.
I'll also organize the hierarchy a bit more by adding a few more objects.
Other than that, we just need to create a new UI Image, add the EquipmentSlot script
to it, and duplicate it a bunch of times.
Set the EquipmentType of each slot to the type of item you want it to hold.
Unfortunately, this free icon pack doesn't have any icon for gloves, leg armor or boots.
So we'll leave a few slots disabled.
We'll also need a class to manage our equipment, so let's make a new script and call it EquipmentPanel.
This will look similar to the inventory, add a Transform - equipmentSlotsParent - and
an array of EquipmentSlots.
This time we won't have a list of Items, or EquippableItems for that matter, just because
separating UI from data is not so easy in this case,
and we want to keep things simple.
Maybe we'll do that in the future, but not for now.
Use the OnValidate method to get all the slots automatically.
Make sure the array gets populated in the editor.
For that to work we need to drag the "equipmentSlotsParent" to the correct variable.
Back in the script, create methods to Add and Remove items.
To add an item, loop through all the slots, when we find a slot that can hold the EquipmentType
of our item, add the item to that slot and return true.
To remove we do almost the same thing, we just look for the Item instead of the EquipmentType,
and we assign null to the slot.
But there's something we're missing in the Add method.
What happens if the slot already has an item?
We can't just assign the new one without doing something to the item that was
already there.
It would be useful to return it, but we still want to return true or false...In that case
let's use an "out" parameter instead.
"out" parameters are just like having extra return values, but instead we pass a variable
as a parameter that will be assigned that value.
So whenever we find the slot that we want, before putting our new item in there, we need
to assign the previous one to this "out" variable.
When "previousItem" is null then we're good, the slot was empty.
If not, then we'll need to take care of it.
Most likely, by putting it back in the inventory.
But that's not the responsibility of this class.
We have both of our main systems already and that's pretty cool, but we need something
that glues them together.
Let's make an InventoryManager class.
This will need references to both the Inventory and the EquipmentPanel.
And here we'll control what happens when we equip or unequip an item.
When equipping, we need to remove the item from the Inventory, add it to the EquipmentPanel,
and if something was in that slot already, return the previous item to the inventory.
If we couldn't equip the item for some reason, just return it to the inventory as well.
When unequipping, we need to make sure to the Inventory isn't full first, then remove
it from the EquipmentPanel, and add to the Inventory.
Now we need to handle the player's input so that we can finally click items in the inventory
to equip them.
We need to know when an ItemSlot is clicked with the right mouse button and figure out
what to do from there.
To detect input from a script, we can add the UnityEngine.EventSystems namespace, and
implement the interfaces that we want.
There's one for detecting clicks, named IPointerClickHandler, which is exactly what we want.
We'll ask visual studio to help us out here with some automatic code.
We can check if the button that was clicked is the right mouse button by accessing the
event data variable.
But now we need to somehow figure out if this item is in the inventory or the character
panel, and move it from one to the other... that seems weird.
Let's think about this.
That doesn't seem like the kind of thing that this class should be taking care of.
Instead of having to deal with all that, we'll let some other class do it.
Let's expose a C# event, of type Action<Item>, that we'll name OnRightClickEvent, and this
will trigger when the item slot is right clicked.
The type just means that this is a generic function that receives an Item as its only
input parameter.
Let's go into the Inventory class and add a similar event as well.
Make an Awake method and here we can add listeners to the slots's events.
To add a listener we just use the "+=" operator and assign a method with the same signature
as the event.
In this case we need a method that takes in an Item, and the Inventory's
own event is precisely that.
And in the InventoryManager class, we'll also add a listener the Inventory's event, finally
calling the EquipItem method.
Unfortunately we can't directly tell it to equip the item, because in order to equip,
we're expecting an EquippableItem, while items in the inventory
can be of some other type.
So we need an extra method to check for that, and only equip the item if it's equippable.
Let's test this out in play mode.
But first we need to add the InventoryManager component to one of our game objects,
and drag the Inventory and EquipmentPanel.
As we can see we can now equip items by right clicking them!
So let's summarize what's actually happening here:
Whenever we right click an ItemSlot, it just goes like "hey somebody right clicked me,
and this is my item".
The Inventory class catches this event and also says "hey somebody right clicked this
item".
In turn, the InventoryManager goes "ok, someone clicked an item in the inventory, let's equip
it then".
This way we avoid a lot of dependencies between our classes, which is good, cause it makes
the code a lot more reusable.
Let's copy and paste this stuff to the EquipmentPanel too, and attach listeners to its event in
the InventoryManager.
This way we can also unequip items.
Once again guys, thank you so much for watching, stay tuned for more, and I'll see you next time.
Bye!
Không có nhận xét nào:
Đăng nhận xét