009
We have items, we have lootbags, it’s time to put them together to finish off a big mechanic of the game.
What we’re after is a way to tell an enemy to spawn a lootbag with some items when it dies. Each item should have its own probability of being in the lootbag, eg. common items like potions could have a 100% chance to spawn, while rare items would only be in the loot bag 5% of the time.
Let’s couple the Item and it’s chance of being spawned into a struct, which we’ll call a LootTableEntry
.
[System.Serializable]
public class LootTableEntry
{
public float chance = 1f;
public Item item;
}
While we’re here, let’s also make a custom PropertyDrawer because currently it looks like this:
It’s fine, but it takes up 2 lines making it a foldout, which is unnecessary.
#if UNITY_EDITOR
[CustomPropertyDrawer(typeof(LootTableEntry))]
public class LootTableEntryDrawer : PropertyDrawer
{
public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
{
var chanceProp = property.FindPropertyRelative("chance");
chanceProp.floatValue = EditorGUI.Slider(new Rect(position.x, position.y, position.width / 3f, position.height),
"", chanceProp.floatValue, 0f, 1f);
EditorGUI.ObjectField(new Rect(position.x + position.width / 3f, position.y, position.width - position.width / 3f, position.height),
property.FindPropertyRelative("item"), typeof(Item), GUIContent.none);
}
}
#endif
Here we just put both on one line, and also turned the chance field into a slider. This is what it looks like now:
For this first version, it’s gonna be rather simple. We setup an array and loop over each of the items, get a random value and use it to determine whether to spawn the item or not.
class LootDropOnDeath
{
[SerializeField] List<LootTableEntry> lootTable = new();
// ...
List<Item> GetItems()
{
List<Item> items = new();
foreach (LootTableEntry entry in lootTable)
{
if (Random.value < entry.chance)
{
items.Add(entry.item);
}
}
return items;
}
}
We subscribe to the OnDeath event in Awake()
and spawn the loot bag with the items when its invoked.
void Awake()
{
health.OnDeath += (sender, e) =>
{
ItemContainer lootbag = Instantiate(lootbagPrefab, transform.position, Quaternion.identity).GetComponent<ItemContainer>();
lootbag.TryAddItems(GetItems());
};
}
I added a simple toggle to ItemContainer
, deleteWhenEmpty
. If this is on, everytime the container is updated it checks whether all itemslots are empty and if so, simply destroys itself.
Here’s how it looks in action:
In the future I will probably expand this system with something like groups, essentially excluding the possibility of getting certain items at once.