Making a Hack’n’slash #17: Adding a loot system 1/2

Let’s continue our Hack’n’slash and set up a little loot system!

This article is also available on Medium.

In the last episodes, we put together an inventory system so that the players can browse and reorganise their current possessions. We made sure it’s cross-platform, and we worked on various little features to give enough feedback.

But if you pick up a gamepad and move around the inventory for a while, you’ll quickly see that something’s wrong: whenever you use the left joystick to pick a new item, you’ll actually also move the hero in the background! So let’s make sure we properly separate the two modes.

And also, now that we have a place to store our items… let’s see how to make our enemies spawn some as a loot 🙂

Important note: I’ve decided to implement this feature mostly in the InventoryManager because I feel it’s related to this system, and because it re-uses some of its functions. But if you want to extract it into its own LootManager, you can obviously do that too!

Separating the “normal” and “UI” modes

Remember how, when we first talked about Unity’s new input system, we spent a while discussing the hierarchy of objects it relies on? And, in particular, how we started to separate our inputs between “Player” and “UI” in the following tutorials?

You might be thinking that, up til now, we’ve been picking those action maps somewhat arbitrarily, and also you might have been surprised to see that some inputs are mapped to actions in both maps – like the left joystick, that is used to move the hero in the “Player” and to move around the UI in the “UI” map.

This is where the action maps will shine!

The reason we can “overload” inputs like this is because the new input system makes it fairly easy to enable or disable action maps all at once, which allows us to easily define contexts.

For example, here, we’ll want to separate between the normal “Player” context and the “UI” context (that makes use of both the “UI” and the “InGameMenu” maps). So, to begin with, let’s code simple functions to easily toggle our action maps on or off in our InputManager:

Now, we can use these whenever we show or hide our menu to keep just the right maps activated. The naive setup could be:

However, if you do this, you’ll realise after going into the menu and exiting it that… you can’t reopen it again! That’s because, with this code, when we close the menu, we also disable the “InGameMenu” action map which contains the “ToggleMenu” input. So, basically, once you’ve closed the menu, you’ll have disabled the only way to re-open it 😉

To fix this, we can either:

  • leave the “InGameMenu” map be and keep it enabled even when we’re in the normal play mode (out of the menu), since there is currently no conflicting inputs
  • or create another action map that is “on the side”, that we keep active at all times and that holds inputs for transitioning from one mode to another

I feel like the second solution is a bit more flexible and isn’t that much costly to implement, so I’ll add a new action map, call it “Transitions” and copy back my “ToggleMenu” input in it. Then, I’ll remove it from the “InGameMenu” map:

After you save the Input Actions resource, you’ll get a compile error because we’re currently trying to access the “ToggleMenu” input in the “InGameMenu” action map, in our InGameMenuManager – so let’s update it to fix the problem:

While we’re at it, we’ll also make another quick fix. For now, you’ll notice that the light we added next to our character last time for the equipment preview UI element is visible in the scene, and it makes for a pretty unnatural lighting. Similarly, we don’t need the extra camera when we’re in the normal mode.

We’ll insure that we toggle the whole “EquipmentPreviewAnchor” sub-hierarchy when we toggle the menu, as well (as usual, don’t forget to drag the light object to the new reference slot in the Inspector):

Adding the loot system

Creating the UI

If you take a look at the game references we’ve used so far, you’ll see that there are various types of loot systems:

  • Skyrim has a simple listing of the lootable items (even with mods installed):
Image from: https://gamebrott.com/13-mod-skyrim-terbaik-yang-bisa-mengubah-pengalaman-bermainmu
  • World of Warcraft shows the lootable items as a scrollable column of small icons (similar to the inventory slots):
  • Diablo III “pops” a bunch of lootable items on the ground that you can click to add to your inventory:
Image from: https://www.ebaumsworld.com/images/diablo-3-loot/83520065/

There are obviously lots of other possibilities, but these three games can already give us a few ideas 😉

Even if Diablo III‘s loot system has this nice “spawning” effect, overall I find it a bit cumbersome – having to click on each item can be a bit long. However, it does have an interesting feature: you can easily ignore the common and low-value items to avoid overfilling your inventory with useless objects.

World of Warcraft‘s system is cool because you quickly understand which items are available, but it can be somewhat tiring to scroll through the whole list if it’s long…

Skyrim solves this issue by (usually) showing the entire list of lootable items all at once; but let’s be honest, other than that, it’s not the best in terms of navigation or aestethics!

So, how can we mix all of these advantages?

For this game, I’m going to go with the following:

  • we’ll separate the common items from the “special” items (i.e. items with a rarity above “common”)
  • we’ll be able to click on each item stack to loot it (or navigate through the grid and press a given key on a gamepad)
  • and we’ll also have “Take only special” and “Take all” buttons at the bottom to directly pick a filtered list of items

To give you an idea, here is a screenshot of the UI I’ll use, along with its hierarchy:

I’ve also added a semi-transparent dark overlay behind everything so that the loot panel is more readable on the background, but we still “feel” like we’re in the action 😉

The two grids of slots use the “InventoryItemSlot” prefab we prepared a few tutorials ago for the inventory grid, with all their selection-related components:

Preparing an object to trigger the loot

Next, let’s see how to create a little “loot bag” on the ground (this idea is taken from yet another game, namely Neverwinter Nights II):

This little “LootBag” prefab consists in a 3D model I made in Blender (check out the Github repo to download it 🚀) and a particle system to get nice little glowy effects on it and catch the player’s eye:

If you’re already familiar with Unity’s particle systems, feel free to dive in and create your own from scratch – but if you prefer to get one done quickly, you can also take a look at the Unity Asset Store and get some free sparky VFX!

For example, I personally like the Cartoon Free FX pack by Jean Moreno that contains 50 nice VFX of various types, plus an editor to customise them easily:

Once you added to your assets and downloaded in Unity via the Package Manager, you’ll just need to import it. If you want to explore the pack and see which effect your like the most for our loot bag, you can go ahead and import the entire thing; however, if you already know which effect you want (in my case, it’s the “CFX4 Aura Bubble C”), then you can toggle everything off but this VFX, and all the required materials or images associated with it:

This avoids bloating your project with hundreds of unused resources, so when you work with those “packs”, definitely try and import selectively when it’s possible 😉

Also, we want to make sure that our bag has a SphereCollider to define the loot radius – this will allow us to check whenever the hero is close enough to loot it. Just like with our previous range check colliders, make sure you turn the “Is Trigger” option on:

And just to give a better feedback to the player, we’ll add a simple “Loot” text that is shown or hidden when the player gets close or moves away (I’ve enabled it for the demo, but make sure it is disabled by default in the prefab):

Finally, let’s add a LootBagManager script on our loot bag prefab to check if the player is currently close enough to grab it. In the Awake(), we’ll rotate the loot text to face the camera, and keep the reference to easily enable or disable it later on. Then, as usual, we’ll take advantage of events to easily tell the InventoryManager the news – but this time, we also need to keep a list of all the bags in range (in case we didn’t loot them right away and more than one stacked on the ground):

Last but not least, we have to create a new input action in our “Player” map (i.e. the one that is active when we are not in the UI) to open the loot panel:

And define the associated callback in our InventoryManager – I’ll simply loop through the loot bags in sight and loot the closest one 🙂

You’ll note that I’m setting up the loot to be outside the UI, because I want the rest of the game to keep playing as usual while you’re in this panel. This means that, for example, if enemies are around you, they will keep coming and attacking! So this prevents the players from just jumping from one loot bag to the other to freeze time 😉

Populating the loot UI when an enemy dies

The only problem is that, for now, our loot bags don’t actually contain anything! We open the loot UI panel we created, sure, but it’s empty! What we want is to get some loot when we kill the enemy, store it in the bag, and then use this data when we open the panel.

So let’s improve our LootBagManager to keep a list of items with the amount of each:

Then, we’ll instantiate the loot bag prefab and set these contents with some arbitrary values when an enemy dies, in the EnemyManager. However, remember that ultimately our enemies will be spawned dynamically, too – so we can’t serialise the prefab reference and assign it in the Inspector!

Rather, we’ll do the same as for the damage popup prefab and add the reference in our AddressablesLoader:

All that’s left to do is use this reference in the EnemyManager and set up the LootBagManager:

And, in the InventoryManager, we’ll access the contents of the bag we picked:

If you try to run the game, now, you’ll get a little loot bag on the ground when you kill the Brute!

For now, I’m using some test items to make it easier to debug but, of course, we’ll remove those fields later on because we don’t want to hardcode the loot 😉

In the meantime, though, we still need to re-use this loot we stored to properly populate the loot panel. The nice thing however is that we can call or slightly improve functions from our InventoryManager to handle this new case of the loot panel.

For example, we can create a new _SetLoot() function that is quite similar to the AddItem() one but sets a whole set of items at once, and acts on the loot UI grid rather than the items grid inside the inventory panel.

Note that this requires us to also adapt the _GetNextSlotIndex(), _SetGridItem() and _UnsetGridItem() methods to differentiate between the “inventory mode” and the “loot mode”. And don’t forget that we prepared two areas in the loot panel, for the “special” and “common” items, so we need to handle both cases in terms of data and UI:

At this point, you should have assigned three new references in the Inspector for the loot UI:

And now, if you run the game again, you’ll see the contents of the loot bag:

Fixing the item selection

However, if you try and move your mouse over the loot panel slots (or move with the gamepad), you’ll see that we don’t have our nice outline glow from before! That’s because, at the moment, when we select a slot, we only assign the outline glow to the _itemsGrid‘s matching child, just by looking at the child’s index…

That was nice for a beginning, but now it’s pretty outdated. Instead, we need to make sure that, when we select a slot, we pass both the index and the transform of the slot’s parent, like this:

Conclusion

In this article, we’ve implemented a good chunk of our loot system, and we’ve made it so that enemies can spawn items, and we can list these items in a dedicated UI panel when we’re close enough to the loot bag.

Next time, we’ll wrap up this feature by adding more randomness to the loots, adding the loot items to the hero’s inventory and taking care of deleting and/or updating the loot bag…

Leave a Reply

Your email address will not be published.