Press "Enter" to skip to content

Loot drop table implementation in Unity3D (C#)

0

Many games like Diablo, World of Warcraft and Final Fantasy use loot drop tables for… well… amm… loot drops and stuff ūüėÄ

Currently in Pariah I’m facing random generation. Even though it’s gonna be “randomly” generated world, I still want¬†to have some kind of control over the quantity of spawned items, randomly picked backgrounds, vegetation, resources etc.

I’m not saying that the way I’m doing it, is the best way to implement control over randomness, but I found out that using loot drop table¬†(LDT) can really help me out in¬†multiple situations.

Example of usage

For example we can use loot drop tables in biomes when we want to generate ground. Ground can be made out of different tiles, but we still want to pick the tile with the most grass in it and use it more frequently than other tiles… and that’s where we can harness the power of loot drop tables.

We can create a treasure chest script, that has chances of dropping rare, frequent or maybe legendary loot.

We can use loot drop tables for all sorts of stuff. Before diving into the code and further explanation, I’d really recommend reading this awesome article about loot drop tables on Gamasutra.

The case, tutorial and our goal

I know that i’m gonna use loot drop tables multiple times for different scenarios and that’s the reason why I decided to write two simple generic classes that implement basic loot drop tables and loot drop items (LDI) on which we can extend and build more complex systems.

Goal

In Unity we want to have a¬†“GameObject” or a “ScriptableObject” whose script¬†can contain a loot drop table type property. Loot drop table¬†will give us the ability to add items to it’s list, assign item’s probability¬†and randomly pick them based on their weight.

For example, loot drop item can be an enemy that will be spawned in certain area, a rare or a common sword which the player can acquire from treasure chest, an integer which represents the tile id in our tilemap… or whatever we want it to be.

Also we want to have a control over probability of this item being picked by our loot drop table¬†script and here’s the reason we want to assign a weight to each item separately. Weight in our case is nothing else but an integer. Higher the integer, higher the¬†chances of item being picked.

We’re gonna create a “treasure chest” which will “spawn” different items each time¬†we start the game. We will¬†have the ability to assign which item can spawn and what is it’s¬†probability / weight.

About logic in our loot drop table

Our LDT¬†script combines items to a list, ads up items weights and based on weight’s sum, sets the ranges to which the item belongs. LDT script then picks a random number from “0 – sum of all item weights” and based on that randomly selected number, picks the item.

Example

Total weight of 2 items is 120. Weight of first item is 50 and weight of second item is 70. Based on their weights, LDT¬†assigns¬†that the first item belongs to a range 0-50 and the second item belongs to a range 50-120. Based on these numbers, LDT then¬†picks a random number between 0 and 120. If it picked¬†a number which is for example “76”, it means that the second item was picked. Now that we know which item was picked, we can do whatever we want with it.

Unlimited power - loot drop table

For better understanding what’s happening, check out the video above.

So in the end we want to create 2 abstract classes (genericLootDropItem.cs and genericLootDropTable.cs) which can represent different value types.¬†Then we can inherit¬†different variations of LDTs and LDIs by passing them a type. In our case, we’re gonna create an GameObject variation of LDT and LDI.

Because they can represent different values, I saw a solution in using generics. If you aren’t familiar with generics, you can check out this cool tutorial¬†about generics made by Brackeys.

The code

When writing “nested” generic code, the biggest “problem” I’ve encountered was the inablity to assign the items to a LDT via Unity’s inspector. In my understanding the “problem” is that unity can’t serialize generic properties which are multiple level deep.

One of the¬†consequence is that these properties don’t show up in the inspector and we can’t assign them via inspector even if the code is without an error.

My workaround for this problem is that we create an abstract generic class for LDT and LDI from which we inherit other classes based on the type we want to use.¬†This way we “trick” Unity in a way that it show’s up properties in the inspector and we don’t have to write a lot of code over and over again.

GenericLootDropItem script

Let’s start by writing the most basic element of our system, which is a¬†GenericLootDropItem:

  • It holds information about the item it represents.
  • It has weight.
  • For information purposes it displays item’s chance of being picked (in percents).
  • It holds information about the range it belongs to.

After we know how our item looks like, we can continue with writing GenericLootDropTable script.

GenericLootDropTable script

It’s the brain of our loot drop table system.

  • Contains a list of LDIs.
  • Holds information about total weight of all LDIs.
  • It has a method which validates all of assigned LDIs (set’s their range from, range to, calculates percents).
  • It also has a method which returns the “randomly” picked LDI from it’s¬†list.

You might ask yourself why we’re not inheriting this class¬†from MonoBehaviour and not validating the LDIs in OnValidate method. I’ll explain the reason in the lower section ūüôā

Now that we have base generic classes, we can create LDT and LDI for GameObject type.

GenericLootDropItemGameObject script

GenericLootDropTableGameObject script

TreasureChest script

It’s the script that represents the treasure chest itself. For the tutorial’s purpose we’re gonna try to keep it as simple as possible. It’s main goal is:

  • When the game starts it picks and generates items from it’s LDT property.
  • Validates LDTs and LDIs.

Because this script unlike GenericLootDropTable inherits from MonoBehaviour, It also has a role for¬†validating LDTs and LDIs. Basically it calls LDT’s ValidateTable method in OnValidate method which validates values as soon as we assign them in the inspector.

The main reason for calling LDT’s ValidateTable method inside the TreasureChest script is that if we inherit the GenericLootDropTable from MonoBehaviour we don’t see LDI’s¬†exposed properties.

If LDT inherits from MonoBehaviour
If GenericLootDropTable inherits from MonoBehaviour
If LDT doesn't inherit from MonoBehaviour
If GenericLootDropTable¬†doesn’t inherit from MonoBehaviour

 

 

 

 

 

 

 

 

Now that we have every script we need, we can easily recreate the demo scene from the video.

Summary

Our¬†loot drop table belongs to more “primitive systems” out there ūüėÄ It can be improved and modified in many ways. Here’s the link to my github repo from where you can easily download unity package and test it out yourself.

What I really like about our simple system is that for an example we could really quickly create LDT and LDI of an integer type and modify our treasure chest script in a way that besides dropping items, it would also drop golden coins or something else.

This is my first tutorial and I hope that some of you will find it helpful. If any of you fellow readers have a suggestion, or have an idea on what I could improve, or if you noticed a bug, please let me know in the comments down below.

Till next time, I wish you happy programming!

Filip

    Leave a Reply