The engine will let you have “loot” in your level, whether it’s just lying around for the picking, like most coins in a Mario game, dropped randomly by enemies like drops in Dead Cells, whether it’s got an instant effect, or stored in an inventory, the possibilities are endless.
The engine comes with a number of ready-to-use classes to have your Character(s) grab objects in your levels and get an effect out of it. Most of them will rely on the PickableItem class, or one of its variants. This class comes with base pick behaviour (collision detection, disable on pick, feedbacks, etc), and is easy to extend to create all sorts of pickable items. Simply extend it, override its Pick method to have it cause the effect you’re after, and you’re done. You’ll find the following components that already do so:
- Coin : an item that triggers a Points event when picked, adding as many points as you specified in its inspector to the GameManager’s points counter
- PickableAction : lets you bind any action you want into its UnityEvent slot, and will trigger it on Pick
- PickableJetpack : will Permit the jetpack ability on pick. On this same model you could easily create other pickable objects that would permit any ability of your choice, effectively offering new powers to your Player at runtime
- PickableOneUp : adds the selected amount of lives to the GameManager’s counter
- Star : sends a Star event on Pick. This is used in the RetroAdventure demos, for persistent collectables.
- Stimpack : gives back the specified amount of Health to the character when picked
- TimeModifier : modifies the time scale on Pick, by the specified speed and for the specified duration
Inventory Engine loot
The pickers above will have an immediate effect. If you’re using the Inventory Engine, you can also have pickers that will store items into your inventories. For that, you can use the InventoryPickableItem component. It’ll let you specify what inventory item it should give, and in what quantity.
You’ll find these in action in many of the demo scenes, in the FeaturesWeapons demo scene for example.
These simple loot options are great, but what if you could offer them to the player in a more random way? Most games distribute random rewards to the player for certain actions, in one form or the other. It’s frequent to have enemies drop loot objects when they die. The Corgi Engine will let you implement random loot systems easily. And it’s generic, which means you could use it to implement all sorts of systems where you need controlled randomness : loot boxes, level generation, NPC stats creation, a Borderlands-like weapon generator, etc. Built around the loot table design pattern, as described in more details by Daniel Cook in 2014 in his blog, this system is simple yet powerful, and should open a vast number of possibilities for your game.
You’ll find this system in action in the RetroForest demo scene. Kill the enemies and crates there to have them drop coins, stimpacks or lives at (controlled) random.
Defining your loot table
The first thing you’ll want to do before actually dropping loot is define a loot definition. A loot definition is a scriptable object (a data file) that will hold the information on what objects constitute your loot table, and what the chances of getting each object are. To create one, in any folder of your project, go right click > create > More Mountains > Loot Definition. Next, define how many objects your loot table should contain, and for each of them, drag prefabs of the objects you’d like spawned when that loot drops, and specify their respective weights. Weights are relative to each other, they don’t have to add up to any specific number. If you have 3 objects A, B and C, and want C to drop 10 times more often than the others, you could indiscriminately have the following weights : A:1,B:1,C:10, or you could have A:2,B:2,C:20.
Adding loot to my game
To have these objects drop in game, you can use the Loot component. It’s designed to work in conjunction with the Health component, letting you trigger the loot drop on death or on damage. Alternatively, it can be used autonomously, all you have to do is call its SpawnLoot public method, and it’ll drop loot.
For example, you could select one of your enemies, and add a Loot component to it. In its inspector you’ll be able to customize its behaviour :
- Loot mode : Unique will let you spawn a single object, Loot Table will let you define a local loot table (only for this specific enemy/object), and Loot Table Scriptable Object will let you bind a loot definition like the one described in the section above.
- Conditions : will let you decide if you want the loot spawn to happen on death or damage (or both). This requires a Health component.
- The Spawn section will let you define a delay (in seconds), an optionally random quantity of objects to spawn, as well as advanced spawn properties.
- Spawn Properties : here you can define the shape of the area in which the spawned object will be instantiated. You’ll want to pay special attention to the NormalToSpawnPlane, which defines the plane in which the objects will be instantiated. Typically for 3D you’ll go with 0,1,0 and for 2D 0,0,1. The Spawn Properties will also let you specify offsets, radius, random rotation and scale.
- Collisions : an option to have the loot system check for collisions before spawning an object (to avoid a weapon picker appearing in the middle of a wall, for example).
- Feedbacks : like most systems in the engine, you can natively plug a MMFeedbacks into it, that will play at the spawn position every time a loot object drops.
- Debug : this section lets you spawn massive quantities of objects at runtime, to check on both their position and if the outcome matches what you had in mind when defining your loot table. An option also lets you draw gizmos showing the drop area.
Going further with the loot system
At the core of this system is the MMLootTable class. It’s a generic class that defines a list of objects to “loot”, or pick, each of them weighted. It’s got two simple methods : ComputeWeights, which you’ll usually only call once at initialization, and GetLoot, that will return one object, picked randomly from the table. If you want more than one object to be picked, you’d just repeat the operation. In the example above we do this for game objects, but this class being generic, you can do it for anything else. The engine comes with ready made implementations with the MMLootTableFloat (for floats) and MMLootTableString (for strings), and you can of course add more to suit your specific needs.
Loot is great but you still have to move to grab it. What if it could just fly to you, effortlessly? With the Engine that’s possible. Simply add a Magnetic component to an empty game object, add a 2D collider to it, and put a picker nested under it (any kind, it actually works on all objects, not just loot, although that’s a common use for it). Via its inspector you’ll be able to define what layers it should be attracted to, its speed, acceleration, etc. You can see examples of such magnetic objects in the MinimalAutoMovement demo scene.
And if you’d rather have your character (or other objects) control how these magnetic objects are collected, you could add a MagneticEnabler component on your character (or other collector object). This will “enable” the magnetic elements in range, based on the options specified in its inspector.