A common mechanic in action games is for enemies to drop some kind of item or reward upon dying. The character can then collect this loot to gain some advantage. It is a mechanic that is expected in a lot of games, like RPGs, since it gives the player an incentive to get rid of the enemies—as well as a small blast of endorphins when discovering what the immediate reward is for doing so.
In this tutorial, we'll review the inner workings of such a mechanic and see how to implement it, whatever the type of game and the coding tool/language you might be using.
The examples I use to demonstrate this were made using Construct 2, a HTML5 game making tool, but are in no way specific to it. You should be able to implement the same mechanic whatever your coding language or tool is.
The examples were made in r167.2 and can be opened and edited in the free version of the software. You can download the latest version of Construct 2 here (since I started writing this article, at least two newer versions have been released) and mess around with the examples to your liking. The example CAPX source files are attached to this tutorial in the zip file.
The Basic Mechanic

Upon an enemy's death (so, when its HP is less than or equal to zero) a function is called. The role of this function is to determine whether there is a drop or not, and, if so, the kind of drop it should be.
The function can also handle the creation of the visual representation of the drop, spawning it at the former screen coordinates of the enemy.
Consider the following example :
Click the Slay 100 Beasts button. This will execute a batch process that creates 100 random beasts, slays them, and displays the result for each beast (that is, whether the beast drops an item, and, if so, what kind of item). Statistics at the bottom of the screen display how many beasts dropped items, and the how many of each type of item was dropped.
This example is strictly text to show the logic behind the function, and to show that this mechanic can be applied to any type of game, whether it is a platformer on which you stomp on the enemies, or a top-down view shooter, or an RPG.
Let's look at how this demo works. First, the beasts and drops are each contained in arrays. Here's the beast
array:
Index (X) | Name (Y-0) | Drop rate (Y-1) | Item rarity (Y-2) |
0 | Boar | 100 | 100 |
1 | Goblin | 75 | 75 |
2 | Squire | 65 | 55 |
3 | ZogZog | 45 | 100 |
4 | Owl | 15 | 15 |
5 | Mastodon | 35 | 50 |
And here's the drops
array:
Index (X) | Name (Y-0) | Item rarity (Y-1) |
0 | Lollipop | 75 |
1 | Gold | 50 |
2 | Rocks | 95 |
3 | Jewel | 25 |
4 | Incense | 35 |
5 | Equipment | 15 |
The X
value (the Index
column) for the array acts as a unique identifier for the beast or item type. For example, the beast of index 0
is a Boar
. The item of index 3
is a Jewel
.
These arrays act as lookup tables for us, containing the name or type of each beast or item, as well as other values that will allow us to determine the rarity or the drop rate. In the beast array, there are two more columns after the name:
Drop rate
is how likely the beast is to drop an item when slain. For example, the boar will have a 100% chance to drop an item when killed, whereas the owl will have a 15% chance to do the same.
Rarity
defines how uncommon the items that can be dropped by this beast are. For example, a boar will be likely to drop items of a rarity value of 100. Now, if we check the drops
array, we can see that the rocks is the item with the biggest rarity (95). (Despite the rarity value being high, due to the way I programmed the function, the bigger the rarity number is, the more common the item is. It has more chances to drop the rocks than an item with a lower rarity value.)
And that's interesting to us from a game design perspective. For the balance of the game, we don't want the player to get access to too much equipment or too many high-end items too soon—otherwise, the character might get overpowered too early, and the game will be less interesting to play.
These tables and values are just examples, and you can and should play with and adapt them to your own game system and universe. It all depends on the balancing of your system. If you want to learn more on the subject of balancing, I recommend checking out this series of tutorials: Balancing Turn-Based RPGs.
Let's now look over the (pseudo)code for the demo:
CONSTANT BEAST_NAME = 0 CONSTANT BEAST_DROPRATE = 1 CONSTANT BEAST_RARITY = 2 CONSTANT DROP_NAME = 0 CONSTANT DROP_RATE = 1 //Those constants are used for a better readability of the arrays On start of the project, fill the arrays with the correct values array aBeast(6,3) //The array that contains the values for each beast array aDrop(6,2) //The array that contains the values for each item array aTemp(0) //A temporary array that will allow us what item type to drop array aStats(6) //The array that will contain the amount of each item dropped On button clicked Call function "SlainBeast(100)" Function SlainBest (Repetitions) int BeastDrops = 0 //The variable that will keep the count of how many beasts did drop item Text.text = "" aStats().clear //Resets all the values contained in this array to make new statistics for the current batch Repeat Repetitions times int BeastType int DropChance int Rarity BeastType = Random(6) //Since we have 6 beasts in our array Rarity = aBeast(BeastType, BEAST_RARITY) //Get the rarity of items the beast should drop from the aBeast array DropChance = ceil(random(100)) //Picks a number between 0 and 100) Text.text = Text.text & loopindex & " _ " & aBeast(BeastType,BEAST_NAME) & "is slain" If DropChance > aBeast(BeastType,BEAST_DROPRATE) //The DropChance is bigger than the droprate for this beast Text.text = Text.text & "." & newline //We stop here, this beast is considered to not have dropped an item. If DropChance <= aBeast(BeastType,BEAST_DROPRATE) Text.text = Text.Text & " dropping " //We will put some text to display what item was dropped //On the other hand, DropChance is less or equal the droprate for this beast aTemp(0) //We clear/clean the aTemp array in which we will push entries to determine what item type to drop For a = 0 to aDrop.Width //We will loop through every elements of the aDrop array aDrop(a,DROP_RATE) >= Rarity //When the item drop rate is greater or equal the expected Rarity Push aTemp,a //We put the current a index in the temp array. We know that this index is a possible item type to drop int DropType DropType = random(aTemp.width) //The DropType is one of the indexes contained in the temporary array Text.text = Text.text & aDrop(DropType, DROP_NAME) & "." & newline //We display the item name that was dropped //We do some statistics aStats(DropType) = aStats(DropType) + 1 BeastDrops = BeastDrops + 1 TextStats.Text = BeastDrops & " beasts dropped items." & newline For a = 0 to aStats.width //Display each item amount that was dropped and aStats(a) > 0 TextStats.Text = TextStats.Text & aStats(a) & " " & aDrop(a,DROP_NAME) & " "
First, the user action: clicking on the Slay 100 Beasts button. This button calls a function with a parameter of 100
, just because 100 feels like a good number of enemies to slay. In a real game, it's more likely that you will slay beasts one by one, of course.
From this, the function SlainBeast
is called. Its purpose is to display some text to give the user feedback on what happened. First, it cleans up the BeastDrops
variable and aStats
array ,which are used for the statistics. In a real game, it's unlikely you will need those. It cleans the Text
as well, so that a new 100 lines will be displayed to see the results of this batch. In the function itself, three numeric variables are created: BeastType
, DropChance
, and Rarity
.
BeastType
will be the index we use to refer to a specific row in the aBeast
array; it's basically the kind of beast that the player had to face and kill. Rarity
is taken from the aBeast
array as well; it's the rarity of the item this beast should drop, the value of the Item rarity
field in the aBeast
array.
Finally, DropChance
is a number we randomly pick between 0
and 100
. (Most coding languages will have a function to get a random number from a range, or at least to get a random number between 0
and 1
, which you could then simply multiply by 100
.)
At this point, we can display our first bit of information in the Text
object: we already know what kind of beast spawned and was slain. So, we concatenate to the current value of Text.text
the BEAST_NAME
of the current BeastType
we've randomly picked, out of the aBeast
array.
Next, we have to determine whether an item shall be dropped. We do so by comparing the DropChance
value to the BEAST_DROPRATE
value from the aBeast
array. If DropChance
is less than or equal to this value, we drop an item.
(I decided to go for the "less than or equal to" approach, having been influenced by these live role players using the D&D King Arthur: Pendragon set of rules regarding dice rolls, but you could very well code the function the other way around, deciding that drops will only occur when "greater or equal". It's just a matter of numeric values and logic. However, do stay consistent all through your algorithm, and don't change the logic halfway—otherwise, you could end up with issues when trying to debug or maintain it.)
So, two lines determine whether an item is dropped or not. First:
DropChance > aBeast(BeastType,BEAST_DROPRATE)
Here, DropChance
is greater than the DropRate
, and we consider this to mean that no item is dropped. From there on, the only thing displayed is a closing "." (full stop) that ends the sentence, "[BeastType] was slain.", before moving on to the next enemy in our batch.
On the other hand:
DropChance <= aBeast(BeastType,BEAST_DROPRATE)
Here, DropChance
is less than or equal to the DropRate
for the current BeastType
, and so we consider this to mean that an item is dropped. To do so, we will run a comparison between the Rarity
of item that the current BeastType
is "allowed" to drop, and the several rarity values we have set up in the aDrop
table.
We loop through the aDrop
table, checking each index to see whether its DROP_RATE
is greater than or equal to Rarity
. (Remember, counter-intuitively, the higher the Rarity
value is, the more common the item is) For each index that matches the comparison, we push that index into a temporary array, aTemp
.
At the end of the loop, we should have at least one index in the aTemp
array. (If not, we need to redesign our aDrop
and aBeast
tables!). We then make a new numeric variable DropType
that randomly picks one of the indices from the aTemp
array.; this will be the item we drop.
We add the name of the item to our Text object, making the sentence to something like "BeastType
was slain, dropping a DROP_NAME
.". Then, for the sake of this example, we add some numbers to our various statistics (in the aStats
array and in BeastDrops
).
Finally, after the 100 repetitions, we display those statistics, the number of beasts (out of 100) that dropped items, and the number of each item that was dropped.
Another Example: Dropping Items Visually
Let's consider another example:
Press Space to create a fireball that will kill the enemy.
As you can see, a random enemy (from a bestiary of 11) is created. The player character (on the left) can create a projectile attack. When the projectile hit the enemy, the enemy dies.
From there, a similar function to what we've seen in the previous example determines whether the enemy is dropping some item or not, and determine what the item is. This time, it also creates the visual representation of the item dropped, and updates the statistics at the bottom of the screen.
Here is an implementation in pseudocode :
CONSTANT ENEMY_NAME = 0 CONSTANT ENEMY_DROPRATE = 1 CONSTANT ENEMY_RARITY = 2 CONSTANT ENEMY_ANIM = 3 CONSTANT DROP_NAME = 0 CONSTANT DROP_RATE = 1 //Constants for the readability of the arrays int EnemiesSpawned = 0 int EnemiesDrops = 0 array aEnemy(11,4) array aDrop(17,2) array aStats(17) array aTemp(0) On start of the project, we roll the data in aEnemy and aDrop Start Timer "Spawn" for 0.2 second Function "SpawnEnemy" int EnemyType = 0 EnemyType = random(11) //We roll an enemy type out of the 11 available Create object Enemy //We create the visual object Enemy on screen Enemy.Animation = aEnemy(EnemyType, ENEMY_ANIM) EnemiesSpawned = EnemiesSpawned + 1 txtEnemy.text = aEnemy(EnemyType, ENEMY_NAME) & " appeared" Enemy.Name = aEnemy(EnemyType, ENEMY_NAME) Enemy.Type = EnemyType Keyboard Key "Space" pressed Create object Projectile from Char.Position Projectile collides with Enemy Destroy Projectile Enemy start Fade txtEnemy.text = Enemy.Name & " has been vanquished." Enemy Fade finished Start Timer "Spawn" for 2.5 seconds //Once the fade out is finished, we wait 2.5 seconds before spawning a new enemy at a random position on the screen Function "Drop" (Enemy.Type, Enemy.X, Enemy.Y, Enemy.Name) Function Drop (EnemyType, EnemyX, EnemyY, EnemyName) int DropChance = 0 int Rarity = 0 DropChance = ceil(random(100)) Rarity = aEnemy(EnemyType, ENEMY_RARITY) txtEnemy.text = EnemyName & " dropped " If DropChance > aEnemy(EnemyType, ENEMY_DROPRATE) txtEnemy.text = txtEnemy.text & " nothing." //Nothing was dropped If DropChance <= aEnemy(EnemyType, ENEMY_DROPRATE) aTemp.clear/set size to 0 For a = 0 to aDrop.Width and aDrop(a, DROP_RATE) >= Rarity aTemp.Push(a) //We push the current index into the aTemp array as possible drop index int DropType = 0 DropType = Random(aTemp.Width) //We pick what is the drop index amongst the indexes stored in aTemp aStats(DropType) = aStats(DropType) + 1 EnemiesDrops = EnemiesDrops + 1 Create Object Drop at EnemyX, EnemyY Drop.AnimationFrame = DropType txtEnemy.Text = txtEnemy.Text & aDrop.(DropType, DROP_NAME) & "." //We display the name of the drop txtStats.text = EnemiesDrops & " enemies on " & EnemiesSpawned & " dropped items." & newline For a = 0 to aStats.width and aStats(a) > 0 txtStats.text = txtStats.Text & aStats(a) & " " & aDrop(a, DROP_NAME) & " " Timer "Spawn" Call Function "SpawnEnemy"
Take a look at the contents of the aEnemy
and aDrop
tables, respectively:
Index (X) | Name (Y-0) | Drop rate (Y-1) | Item rarity (Y-2) | Animation name (Y-3) |
0 | Healer Female | 100 | 100 | Healer_F |
1 | Healer Male | 75 | 75 | Healer_M |
2 | Mage Female | 65 | 55 | Mage_F |
3 | Mage Male | 45 | 100 | Mage_M |
4 | Ninja Female | 15 | 15 | Ninja_F |
5 | Ninja Male | 35 | 50 | Ninja_M |
6 | Ranger Male | 75 | 80 | Ranger_M |
7 | Townfolk Female | 75 | 15 | Townfolk_F |
8 | Townfolk Male | 95 | 95 | Townfolk_M |
9 | Warrior Female | 70 | 70 | Warrior_F |
10 | Warrior Male | 45 | 55 | Warrior_M |
Index (X) | Name (Y-0) | Item rarity (Y-1) |
0 | Apple | 75 |
1 | Banana | 50 |
2 | Carrot | 95 |
3 | Grape | 85 |
4 | Empty potion | 80 |
5 | Blue potion | 75 |
6 | Red potion | 70 |
7 | Green potion | 60 |
8 | Pink Heart | 65 |
9 | Blue pearl | 15 |
10 | Rock | 100 |
11 | Glove | 25 |
12 | Armor | 30 |
13 | Jewel | 35 |
14 | Mage Hat | 65 |
15 | Wood shield | 85 |
16 | Iron axe | 65 |
Unlike the previous example, the array that contains the enemy data is named aEnemy
and contains one more row of data, ENEMY_ANIM
, which has the name of the enemy's animation. This way, when
spawning the enemy, we can look this up and automate the graphical display.
In
the same vein, aDrop
now contains 16 elements, instead of six, and each index refers to the animation frame of the object—but I could
have had several animation as well, as for the enemies, if the dropped items were to be animated.
This time, there are far more enemies and items than in the previous example. You can see, though, that the data regarding drop rates and rarity values is still there. One notable difference is that we have separated the spawning of the enemies from the function that calculates if there is a drop or not. This is because, in a real game, enemies would likely do more than just wait on screen to be slain!
So now we have a function SpawnEnemy
and another function Drop
. Drop
is pretty similar to how we handled the "dice roll" of our item drops in the previous example, but takes several parameters this time: two of these are the X and Y coordinates of the enemy on screen, since that's the place where we will want to spawn the item when there is a drop; the other parameters are the EnemyType
, so we can look up the name of the enemy in the aEnemy
table, and the name of the character as a string, to make it quicker to write the feedback we want to give to the player.
The logic of the Drop
function is otherwise similar to the previous example; what mostly changes is the way we display feedback. This time, instead of just displaying text, we also spawn an object on screen to give a visual representation to the player.
(Note: To spawn the enemies on several position on screen, I used an invisible object, Spawn
, as reference, which continually moves left and right. Whenever the SpawnEnemy
function is called, it creates the enemy at the current coordinates of the Spawn
object, so that the enemies appear and a variety of horizontal locations.)
One last thing to discuss is when exactly the Drop
function is called. I don't trigger it directly upon an enemy's death, but after the enemy has faded away (the enemy's death animation). You can of course call for the drop when the enemy is still visible on screen, if you prefer; once again, that is really down to your game design.

Conclusion
On a design level, having enemies drop some loot gives an incentive to the player to confront and destroy them. The items dropped allow you to give power-ups, stats, or even goals to the player, whether in a direct or indirect way.
On an implementation level, dropping items is managed through a function that the coder decides when to call. The function does the job of checking the rarity of the items that should be dropped according to the type of enemy killed, and can also determine where to spawn it on screen if and when needed. The data for the items and enemies can be held in data structures like arrays, and looked up by the function.
The function uses random numbers to determine the frequency and type of the drops, and the coder has control over those random rolls, and the data it looks up, to adapt the feel of those drops in the game.
I hope you enjoyed this article and have a better understanding of how to make your monsters drop loots in your game. I'm looking forward to seeing your own games using that mechanic.
References
- Image credit: Gold Treasure Icons by Clint Bellanger.
- Sprite credit: Character sprites by Antifareas.
- Sprite credit: Battle Backgrounds from Trent Gamblin.
- Sprite credit: Pixel Art Icons for RPGs from 7SoulDesign.