Weighted Drop Tables

My dream project is an action RPG style game, in the vein of 8- and 16-bit games like The Legend of Zelda, The Faery Tale Adventure, Beyond Oasis, and others.

In such a game, enemies drop items when defeated, and those items generally aren’t fixed. There may be a random chance for an item to drop, or if every enemy has an item, the item may be selected randomly. The enemy may have multiple items. There may be drops common to every enemy, and others specific to one individual or group. And certain items may be exceedingly common, while others are exceptionally rare.

This table always drops exactly one item, but there’s a 50% chance of that item actually being nothing.

In order to deal with this, I decided to prototype up a system where you’d be able to put items on a weighted table. But to make it as flexible as possible, I made it so that each unit could have multiple tables attached, and each would have its own unique rules. Perhaps an enemy would always have exactly one weapon; the game would roll on a table that drops the item with a 100% drop rate.

This table has a 10% chance to drop a coin, and rolls 50 times, dropping one coin for each success.

For coins, it might roll 50 times with a 10% chance each, and for each that succeeds, there’s a chance for it to be copper, silver or gold, with decreasing chances for each. And for random drops, it may do something like rolling three times with a low chance each, and for each success it rolls on a large table with any number of common goods.

The method is relatively simple. Working from the ground up, each item is a standard Game Object, saved as a prefab.

The next step was to create the drop tables. I created a Scriptable Object, and added a struct that holds two items: A Game Object reference to one of those prefabs, and an int for the item’s drop weight. The higher the weight, the more likely it is for that item to drop. By setting the weight to zero, the item remains in the list, but has no chance to drop. Setting the weight to a negative value would also cause the item to be unable to drop, but would also mess with the range of rolls, reducing the chances of things near the end of the list, or maybe even making it impossible to roll that high. In a final version, I would definitely add some sort of safeguard to prevent negative values.

Because the table can have an unpredictable total weight, I created a function that simply iterates through all the items on the table, adds together all the weights, and returns the total. This is where the negative values would potentially cause a problem. The simplest solution would be to add Mathf.Max(0, itemWeight) rather than just adding the weight. That way, negative values are treated the same as zeroes.

The next function selects an item off the list. It generates a random number from 0 to the value returned by the previous function. It then loops through the entire list, checking to see if the rolled number is less than the current item’s weight. If it is, it returns that item. If not, it subtracts the current item’s weight from the roll, and progresses to the next item.

For example, if your table had two items, each with a weight of 50, it would roll a number from 0 to 99. If the roll was less than 50, it would return the first item. If the roll was 50 to 99, it would subtract 50, getting a number between 0 and 49, and then move on to the next one. Since the roll would now be under the second item’s weight of 50, it would return that, and exit the function.

The code should always find a valid result before it finishes the loop. As a safety precaution, after the loop are two lines that push an error message to the console, and return an empty drop result.

The final function on the drop table is Public, and it creates an empty list of Game Objects, and enters a loop that runs a number of times equal to the maximum number of drops allowed. For each loop, if checks if a random number from 0 to 1 is less than the drop chance, and if it is, it runs the item selection function above. If the return value isn’t null (either because of the error check, or because the drop table contains an empty entry), it adds that item to the list, and once it’s finished, it returns the contents of that list.

The units each have a list of Drop Tables, and when clicked, they run a function that calls the public function on each of their Drop Tables, and Instantiates each of the items returned before destroying itself and waiting to be respawned. In this demo, each drop simply spawns in at the location of the unit that created it, and gets launched in a random direction before fading out a few seconds later.

Procedural Maze Generation

I’ve always wanted to make a roguelike game, but one thing I’ve struggled with is procedural map generation; no matter how hard I tried, or how many explanations I read, I could never create a random map without something going wrong.

Earlier today, I found a video by javidx9 which explained how to create a random maze using an algorithm he called the Recursive Back-Tracker, and everything just clicked. I finally understood an algorithm, and I think, with some work, I should be able to modify it to do additional things, like adding rooms, or populating it with random creatures and furnishings.

Yet another Unity prototype.

Another project that I’d like to work on is a deckbuilding game, in the vein of Slay the Spire, and similar games it’s inspired. Working toward that goal, I’ve been learning how create a number of elements that would be needed in such a game, such as a drag-and-drop interface, Scriptable Objects to hold card and enemy data, and a system to damage and heal both players and enemies.

This project must remain firmly in the “undistributed prototype” category. The assets were taken from my copy of RPG Maker MV, and the license for those assets only allows use in RPG Maker engines. Since this was built in Unity, I cannot release the project, even if I progress further with it. It’s unlikely that I’ll go further with this particular build though, as there are several poorly built components; it would be better to rebuild the majority of the components before continuing.

Moar Unity Experimentation

One project I’ve considered working on is some kind of board game. To play a board game, you need dice, of course. It’s quite simple to just generate a random number, and say “You rolled a 6!”, or whatever the result is, or even to use that randomly generated result to play an animation. But it’s more fun to actually roll the die.

Rolling a die is pretty easy, too. Create your 3D model, apply a force and torque to throw and spin it, and wait for it to stop moving. Of course, the computer can’t just look at the face that it lands on and read the result.

There is probably some way to calculate the facing of the die by performing some three-dimensional geometry on the rotation of the object, but I chose a simpler method; I just attached a trigger collider to each face of the die, and when program detected that the die had stopped moving, it sent a Raycast straight up, and checked which of the faces it hit.

Of course, your program needs feedback, and since there’s no game to read and use the results of the die, I instead created a particle emitter that would show the results, allowing me to manually check that what the program thinks the result is matches the actual results.

I’m Not Dead Yet!

I am still alive, and still learning.

Earlier this year, Udemy had a sale, and several Unity courses, including some officially endorsed ones,  were very cheap, so I picked a few up. I figure learning anything is better than stagnating; any practices I pick up in one language are applicable to others.

I haven’t finished any of those courses yet, but I feel like I’ve learned enough that I set myself a challenge: I was going to build a game, game jam style. That is, a full game, start to finish, in 48 hours or less. Since it isn’t an actual game jam, I loosened up some of the typical rules: No theme, some advance planning was done, the only thing I strictly adhered to was a 48 hour limit once I started coding.

It isn’t 100% bug free, and it’s lacking a lot of polish. There are a couple things I wanted to include that I didn’t have time to complete.

However, it is a complete project. If you want to try it, it can be found at: http://arkanoid.chronocidegames.com/

Change of Direction

Game Maker: Studio is great. Don’t get me wrong, it’s an amazing dev tool, and I love it. But the end of life has been announced for 1.4, and I don’t have the money to buy the Mobile version of 2.0, and I’m concerned that, if I stick with 1.4, I could run into a situation where an app released on the Play Store might get blocked untul certain updates to the IDE happen. It’s happened before, and if that happens after 1.4 support ends, my app is dead, unless I can find the time and money to update to 2.0.

So, I’ve been looking for an alternate option for mobile development. My criteria are pretty simple: Cheap enough I can afford it, as powerful as GM:S, and something I can learn fairly easily. Two programs instantly came to mind: Clickteam Fusion and Godot.

I already own a copy of Fusion, thanks to Humble Bundle, and it’s got integrated support for Spriter, which I really like, so I gave it a go first. I still can’t wrap my head around it though; its workflow and mine just don’t mesh.

So next, I tried Godot. I’ve seen some pretty good reviews of it, and it’s free and open source, so nothing to lose, right? Turns out, I love it. It suits my learning and programming styles to a T, and there are even things that I had to force Game Maker to do the way I wanted that Godot does that way by default.

So, I’m doing Godot tutorials by the dozen in every spare minute I’ve got, and I expect to be using it for at least one project I’m planning. Will I continue to use GM:S? I hope so. But it’s no longer going to be my sole language.

And maybe one if these days I’ll get around to figuring out Fusion.

Logo!

A few days ago, I had the idea to put together a copy of a program I used to use way back in elementary school – Logo. I know enough about how to do all the basic tasks – moving an object, manipulating data structures and collecting and parsing user input – that I thought I’d be able to slap together a rudimentary version fairly quickly. And I was right – mostly.

Almost immediately, I ran into a problem. Drawing 640,000 pixels individually from my data structure 60 times per second just didn’t work. I was lucky enough to occasionally push it up to two. That just wasn’t acceptable. Fortunately, I knew of a system in Game Maker – Surfaces – that would solve my problem. Unfortunately, I knew absolutely nothing about using them. It turned out that they’re relatively simple to use; in a bit over an hour, I had the system drawing to the surface, and vastly improving my Frames Per Second.

After I got that working, I started in on my text input. Like the graphics portion of the screen, I know that it was not the most efficient method, but it works, and I don’t currently have a reason to make massive changes to it.

Next was the text parser. Not that I could get user input, I needed to convert it into commands that the system could use. Again, I feel that I’m not using the best methods, but it (mostly) works, so again, I’m sticking with what I’ve got.

At this point, I noticed a flaw in my graphics system. My surface was redrawing itself every time it changed, and when the turtle started moving, the surface was once again drawing all 640,000 pixels multiple times per second. So, once again, I had to rewrite the graphics system, making it draw directly to the surface, and only recalculating the full contents when absolutely necessary.

If you’re familiar with Logo, this version will come as a disappointment to you. It only has a handful of basic commands implemented. But since it’s the fruit of only about 10-12 hours labor, including learning some new tools, I’m willing to consider it a good start.

Commands

FW or FORWARD # – Move the turtle forward # pixels.

BK or BACK # – Move the turtle backwards # pixels.

RT or RIGHT # – Turn the turtle right # degrees (360 degrees in a circle).

LT or LEFT # – Turn the turtle left # degrees.

HOME – Return the turtle to the center of the drawing area.

CG or CLEAN – Remove everything from the drawing area.

PU or PENUP – Lift the pen, making it so that the turtle doesn’t draw when it moves.

PD or PENDOWN – Lower the pen, making the turtle draw as it moves.

PC or PENCOLOR # # # – Changes the color of the trail the turtle leaves. The three numbers are Red, Green and Blue levels, each from 0 to 255.

RP or REPEAT # [command list] – Repeat all the commands contained in the [square brackets] # times.

Commands are not case sensitive. You can enter multiple commands in a single line, up to a maximum of 60 characters.

Known Issues

PENCOLOR does not properly clear its parameters from the input buffer. As a result, any commands on the same line will not execute properly, if at all. This does not affect commands typed on a new line following a PC command.

REPEAT does not properly parse nested loops. In some situations they may perform as expected. At other times, they will act unpredictably.

HT, HIDETURTLE, ST and SHOWTURTLE commands are implemented in a manner that is not compatible with HTML5. While you can use these commands, they currently do nothing.

The application window is too large to properly fit in a blog post, so follow the link to try it out HERE.

Welcome to Chronocide Games!

My name is Kurtis, and I’m the lead (and sole) dev behind Chronocide Games, a studio working on 2D game development using YoYoGames’ Game Maker: Studio. I’ll be posting updates here on projects that I’m working on.

The current project is currently at an “under-the-hood” stage, and wouldn’t show much more than a plain black screen, so in the meantime, here’s a quick look at a simple project that I worked on in the past.

This particular game engine has been set aside; it had some elements that needed to be reworked, and doing so would have too much of an impact on other parts of the project, so a new project has been started to improve on this one.