Loot table

This is my version of a loot table that I created and designed using a custom editor script within the Unity engine.

Project details:

code language : C#

Software : Unity engine

Team size : solo

My role : developer, designer

Date : 06 September 2023 - 12 Febuary 2023

NOTE : GitHub and Trello are private

My Contribution

My contribution was to design and creating a flexible and easy to use loot table. I tried to create my own version of a loot table. I created this loot table with a custom editor script within the Unity engine. This is also my first time making such a large tool with a custom editor script.


Design

Before I started coding the loot table I made a design using Adobe XD. For me it was the fastest way to create a good design that I could use later as a reference when I started coding. Whitin the loot table you have two types of drops, the guaranteed drops and the loot drops.

The drop variants inside the guaranteed drops what the name already says always drops no matter what. The loot drops on the other hand work with chance. I wanted to make sure a user exactly knows how much chance there is on a certain list with drop variants from the loot drops. So I decided to give every list his own color and created a chance gradient that represents the total chance you have on a list. The bigger the color is on the chance gradient the more chance a list has.


Chance gradient

Functionality

When I began designing the chance loot system, I thought of a way to make it easier to visualize what the probability of each loot list inside the chance loot. After some time, I came up with an idea to give a unique color to each list and create a gradient that allows for a more visual representation for the chance of each loot list.

Each time you add a new loot list or modify the chance of a loot list, it triggers a function to recalculate the keys within the gradient. When this function is called it gets the key that are stored within the loot list and removes it from the gradient. After that, it recalculate the chance and then adds a new key with the chance value and the color that is associated with the list back into the gradient.

Calculate chance

                
private float CalculateChance(float weight, float totalWeight)
{
    var maxProcent = 100;

    var newProcent = (maxProcent / totalWeight) * weight;

    return newProcent;
}
                              
            

Recalculate keys

                
private void ReCalculateKeysPlace(List<LootDrops> lootDrops,bool calculateChance)
{
    var keyPlace = 0f;
    for (int i = 0; i < lootDrops.Count; i++)
    {
        lootTable.gradient.RemoveKey(lootDrops[i].colorKey);
        if (calculateChance)
        {
            lootDrops[i].chance = CalculateChance(lootDrops[i].weight, lootTable.totalWeight);
        }
        lootDrops[i].colorKey = lootTable.gradient.AddKey(lootDrops[i].myColor, (lootDrops[i].chance / 100) + keyPlace);
        keyPlace += lootDrops[i].chance / 100;
        Repaint();
    }
}
                              
            

Create gradient texture

                
public Texture2D GetTexture(int witdh)
{
    var texture = new Texture2D(witdh, 1);
    var colors = new Color[witdh];
    for (int i = 0; i < witdh; i++)
    {
        colors[i] = Evaluate((float)i / (witdh - 1));
    }
    texture.SetPixels(colors);
    texture.Apply(false);
    return texture;
}
                              
            

Evaluate gradient

                
public Color Evaluate(float time)
{
    if (_keys.Count == 0)
    {
        return Color.white;
    }

    var keyRight = _keys[_keys.Count - 1];

    for (var i = 0; i < _keys.Count; i++)
    {
        if (_keys[i].Time >= time)
        {
            keyRight = _keys[i];
            break;
        }
    }

    return keyRight.Color;
}
                              
            

Draw the gradient

                
public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
{
    var gradient = (LootTableGradient)fieldInfo.GetValue(property.serializedObject.targetObject);
    GUI.DrawTexture(position,gradient.GetTexture((int)position.width));

    for (int i = 0; i < gradient.GetNumberOfKeys(); i++)
    {
        if (i == gradient.GetNumberOfKeys() - 1) break;

        var key = gradient.GetKey(i);
        var keyRect = new Rect(position.x + position.width * key.Time - colorDividerWidth / 2f, position.yMin, 
            colorDividerWidth, position.height);
        EditorGUI.DrawRect(keyRect, Color.black);
    }
}
                              
            

Drop variant options

Drop variant selection

You can add new items to the guaranteed drops or the loot drops. When you add an item, a new button will appear in the list to which you added the item. When you click on this button, you will be presented with a range of options for the drop settings.

The first thing you can set up is the item itself. You can select any GameObject from your project, and when you choose an item, you will see a Texture2D representation of the selected item. This feature makes it easy to visually identify the item you've chosen.

Selection of the drop variant

                
var prefabPreview = GetAssetPreview(dropVariantDetails[index].dropVariant);

GUILayout.BeginHorizontal();
GUILayout.Space(11);
GUILayout.Label(prefabPreview, GUILayout.Width(80), GUILayout.Height(80));

GUILayout.BeginVertical();
GUILayout.Space(22);

GUILayout.BeginHorizontal();
DrawLabel("Drop variant");
GUILayout.Space(-100);
dropVariantDetails[index].dropVariant = EditorGUILayout.ObjectField(dropVariantDetails[index].dropVariant, 
    typeof(GameObject), false) as GameObject;
                              
            

Drop variant amount

Below the item selection, you can specify how many times this item will drop. You have two options for this:

Single Number: You can choose a specific number. for example you selelct four, the item will be dropped four times whenever it is chosen. Range of Numbers: Alternatively, you can define a range by specifying a minimum (min) and maximum (max) value. The loot table will randomly select a number within this range, and that will determine the number of the item dropped when it's chosen.

This flexible system allows you to finely control the drop rates and number of items in your game, making it easy to create diverse and balanced loot tables.

Drop amount options

                
DrawLabel("Drop amount");

if (dropVariantDetails[index].valueOptions == ValueOptions.oneValue)
{
    EditorGUI.BeginChangeCheck();
    dropVariantDetails[index].amount = EditorGUILayout.IntField(dropVariantDetails[index].amount);
    if (EditorGUI.EndChangeCheck())
    {
        dropVariantDetails[index].amount = CheckValueCap(dropVariantDetails[index].amount);
    }
}
else if (dropVariantDetails[index].valueOptions == ValueOptions.BetweenTwoValues)
{
    DrawLabel("Min", ValueOptionsWidth);
    GUILayout.Space(-30);
    EditorGUI.BeginChangeCheck();
    dropVariantDetails[index].minAmount = EditorGUILayout.IntField(dropVariantDetails[index].minAmount);
    if (EditorGUI.EndChangeCheck())
    {
        dropVariantDetails[index].minAmount = CheckValueCap(dropVariantDetails[index].minAmount);
    }
    GUILayout.Space(-10);
    
    DrawLabel("Max", ValueOptionsWidth);
    GUILayout.Space(-30);
    EditorGUI.BeginChangeCheck();
    dropVariantDetails[index].maxAmount = EditorGUILayout.IntField(dropVariantDetails[index].maxAmount);
    if (EditorGUI.EndChangeCheck())
    {
        dropVariantDetails[index].maxAmount = CheckValueCap(dropVariantDetails[index].maxAmount);
    }
} 
                              
            

Drop simulation

When you have finished setting up the loot table, you may want to test it out. At the bottom of the loot table, there is a button that allows you to simulate a drop. When you press this button, the loot table will calculate the results, and then display them below the button.

You will be able to see what the guaranteed drops consist of, as well as the loot drops. Notably, the loot drops will be color-coded to correspond with the list they originate from.

Simulate roll

                                 
private void DrawSimulatedRoll()
{
    if (_roll == null) return;


    var boldText = new GUIStyle(GUI.skin.label)
    {
        fontStyle = FontStyle.Bold
    };

    var lootColor = new GUIStyle(GUI.skin.label);

    var chanceItems = _roll.chanceItems;
    var guaranteedItems = _roll.guaranteedItems;

    GUILayout.Space(10);

    DrawHorizontalLine();

    GUILayout.Space(5);
    DrawLabel("guaranteedItems :", boldText);
    foreach (var dropVariant in guaranteedItems)
    {
        EditorGUILayout.LabelField(dropVariant.name);
    }

    GUILayout.Space(10);
    DrawLabel("chanceItems :", boldText);
    foreach (var item in chanceItems)
    {
        ChangeTextColor(item, lootColor);

        EditorGUILayout.LabelField(item.name, lootColor);
    }

    DrawHorizontalLine();
}