Game Jam Procedural Generation Part I
My latest game jam game still isn't quite ready for release, and unfortunately at this point it will have to wait until after PAX, but I can still share some source code from it that will maybe help people out when it comes to procedurally generating things in their games.
The premise of my jam game is that you play as a space explorer type person who is flying a ship around the galaxy searching for planets with shiny ore on them. When the game starts the entire galaxy is procedurally generated. This is a fancy way in saying that a bunch of random things happen and hopefully it works out.
This is my CreatePlanets() function for the map scene in the game. What this does is just creates 99 planets and places them in the scene at random X and Y coordinates. The map scene has a Width and Height defined earlier in the game (in this case I believe its 5000 x 5000 pixels.)
The scene also has a planetSupply field that is defined earlier. Whenever a planet is generated, its size is subtracted from the planetSupply. If the planetSupply is less or equal to 0, all planets created from that point on will be the smallest possible size. This was to control how many huge planets were created in the scene.
The other check that happens here is planets will check to make sure that they are not too close to any other planet in the scene. Without this check, a planet could spawn on top of another planet. The class for the planets is called Destination because at the start of the jam I had ideas for more destinations than just planets, but as you probably know when at a game jam about 90% of your idea gets tossed by the end.
When a planet is actually created, it randomly will decide the factors that go into creating the level that it contains.
The planet will create a SceneConfig object. This object is just a bunch of properties that are passed along when creating the Scene that the player will go to when selecting this planet. (If you don't know what a Scene is, it's a class in Otter which I used to create this game.)
The planet starts off by using its initial size to determine the width and the height of the scene. It then randomly decides which side to place the shiny ore on: -1 being the left, and 1 being the right. It also decides how far it should offset the initial landing position of the player's ship (the default position is the exact center of the scene.) Then it will decide how far to offset the shiny ore from its default location (which is the very edge of the scene.)
After that the offset to the ground level of the scene is randomly decided. The ground level of the scene is where the ground tiles of the planet begin. The default is the exact vertical center of the scene, but depending on what the SceneConfig decides this could be offset by -10 to +10 tiles.
The next decision is how jagged the surface of the planet is. The higher the jaggedness, the more variation there will be in the height of the surface. Next the decay and island factors are decided. Decay spots are where the ground will be randomly chosen to be eroded, and islands are spots where ground will be randomly chosen to spawn. This can add some weird variation to how a planet looks.
Next the planet decides how many platforms there should be in the scene. The higher the number here, the more floating platforms will appear both above and below the surface of the planet. After that the amount of rooms a planet will have is decided. A room is just a rectangle that will be carved out under the ground. The more rooms a planet has the more empty areas it will have below the surface.
After that the amount of breakable objects a planet will have is decided. The higher number here will spawn more breakable blocks for the player to dig through in the scene.
Last but not least the name of the planet is decided by just combining a bunch of random syllables. After the name the planet will decide if it wants to add a number at the end.
Phew, that's a lot of words. As you can see, so far a lot of what is going on is just tuning randomly generated numbers. What goes into the actual generation of making a planet the player can walk around on though? Well that is a question for Part II of this blog post which I can hopefully get out before PAX madness really gets into gear. Again this isn't even close to any sort of sophisticated procedural generation, but it was good enough for a 48 hour game jam.
The premise of my jam game is that you play as a space explorer type person who is flying a ship around the galaxy searching for planets with shiny ore on them. When the game starts the entire galaxy is procedurally generated. This is a fancy way in saying that a bunch of random things happen and hopefully it works out.
for (var i = 0; i < 99; i++) {
var size = Rand.Int(8, 64);
planetSupply -= size;
if (planetSupply <= 0) {
size = 8;
}
var x = Rand.Float(Width);
var y = Rand.Float(Height);
var d = new Destination(x, y, size);
Add(d);
while (d.CloseToOtherDestination()) {
d.X = Rand.Float(Width);
d.Y = Rand.Float(Height);
}
}
This is my CreatePlanets() function for the map scene in the game. What this does is just creates 99 planets and places them in the scene at random X and Y coordinates. The map scene has a Width and Height defined earlier in the game (in this case I believe its 5000 x 5000 pixels.)
The scene also has a planetSupply field that is defined earlier. Whenever a planet is generated, its size is subtracted from the planetSupply. If the planetSupply is less or equal to 0, all planets created from that point on will be the smallest possible size. This was to control how many huge planets were created in the scene.
The other check that happens here is planets will check to make sure that they are not too close to any other planet in the scene. Without this check, a planet could spawn on top of another planet. The class for the planets is called Destination because at the start of the jam I had ideas for more destinations than just planets, but as you probably know when at a game jam about 90% of your idea gets tossed by the end.
When a planet is actually created, it randomly will decide the factors that go into creating the level that it contains.
var ratioSplit = Rand.Int(size);
var width = ratioSplit;
var height = size - ratioSplit;
SceneConfig.Height = 480 + height * 32 + Rand.Int(height) * 32;
SceneConfig.Width = 640 + width * 32 + Rand.Int(width) * 32;
SceneConfig.TreasureDirection = Rand.Flip ? 1 : -1;
SceneConfig.ShipStartOffset = Rand.Int(-10, 10) * 16;
SceneConfig.TreasureDistanceOffset = Rand.Int(SceneConfig.Width / 4);
SceneConfig.GroundLevelOffset = Rand.Int(-10, 10);
SceneConfig.Jagginess = Rand.Int(0, 10);
SceneConfig.DecaySpots = (int)Util.Min(Rand.Int(0, 20), Rand.Int(0, 20), Rand.Int(0, 20));
SceneConfig.DecayChance = Rand.Int(5, 100);
SceneConfig.IslandSize = (int)Util.Min(Rand.Int(10, 30), Rand.Int(10, 30));
SceneConfig.IslandSpots = (int)Util.Min(Rand.Int(0, 20), Rand.Int(0, 20), Rand.Int(0, 20));
SceneConfig.Platforms = (int)Util.Min(Rand.Int(5, 95), Rand.Int(5, 95), Rand.Int(5, 95));
SceneConfig.Rooms = Rand.Int(3, size / 2);
SceneConfig.BreakableChance = (int)Util.Min(Rand.Int(5, 95), Rand.Int(5, 95), Rand.Int(5, 95));
var syllableCount = Rand.Int(1, 4);
string name = "";
for (var i = 0; i < syllableCount; i++ ) {
name += Rand.ChooseElement(syllables);
}
if (Rand.Flip) {
name += " " + Rand.ChooseElement(numbers);
}
SceneConfig.Name = name;
The planet will create a SceneConfig object. This object is just a bunch of properties that are passed along when creating the Scene that the player will go to when selecting this planet. (If you don't know what a Scene is, it's a class in Otter which I used to create this game.)
The planet starts off by using its initial size to determine the width and the height of the scene. It then randomly decides which side to place the shiny ore on: -1 being the left, and 1 being the right. It also decides how far it should offset the initial landing position of the player's ship (the default position is the exact center of the scene.) Then it will decide how far to offset the shiny ore from its default location (which is the very edge of the scene.)
After that the offset to the ground level of the scene is randomly decided. The ground level of the scene is where the ground tiles of the planet begin. The default is the exact vertical center of the scene, but depending on what the SceneConfig decides this could be offset by -10 to +10 tiles.
The next decision is how jagged the surface of the planet is. The higher the jaggedness, the more variation there will be in the height of the surface. Next the decay and island factors are decided. Decay spots are where the ground will be randomly chosen to be eroded, and islands are spots where ground will be randomly chosen to spawn. This can add some weird variation to how a planet looks.
Next the planet decides how many platforms there should be in the scene. The higher the number here, the more floating platforms will appear both above and below the surface of the planet. After that the amount of rooms a planet will have is decided. A room is just a rectangle that will be carved out under the ground. The more rooms a planet has the more empty areas it will have below the surface.
After that the amount of breakable objects a planet will have is decided. The higher number here will spawn more breakable blocks for the player to dig through in the scene.
Last but not least the name of the planet is decided by just combining a bunch of random syllables. After the name the planet will decide if it wants to add a number at the end.
Phew, that's a lot of words. As you can see, so far a lot of what is going on is just tuning randomly generated numbers. What goes into the actual generation of making a planet the player can walk around on though? Well that is a question for Part II of this blog post which I can hopefully get out before PAX madness really gets into gear. Again this isn't even close to any sort of sophisticated procedural generation, but it was good enough for a 48 hour game jam.
Post your comment!