KARST RANDOM CELL MAP
By KarstSkarn#7993
Showcase: https://launch.crayta.com/play/j4p77ep7
Blueprint: Blueprint is called “Karst Random Cell Map Blueprint”
Practical Application Example: https://forum.crayta.com/t/singularity/2884
Karst Random Cell Map is a script capable of generating random rooms and corridors within a given area.
It works based on tiles but what allows its high customization and versatility is that it works with different templates for floors and walls. Allowing a large number of possible combinations.
Virtually all parameters can be easily adjusted and changed by the user from the Crayta panel. In addition it does not work in an intrusive way since it is a script that every time it is launched generates a new map with the given parameters and once it is finished it remains dormant waiting to be launched again. Thus saving many resources to the user who decides to use it and allowing the user to customize the game mode or any script you want to run once the map is spawned.
A lot of time and effort has gone into making the interface as friendly and intuitive as possible and not leaving anything “hard-coded” so that it is simple to use and at the same time can serve any function that the user wants to give it.
In fact the practical example whose link is at the top of this post (The game “Singularity”) uses this same package without having anything programmed specifically for the game. The game uses only the default functions contained in the package.
Common Q&A
-
I will need to code to use it?
Not at all! You can fully use it from the Advanced Crayta Panel! But if you want to do something a bit more complex like deleting a map once a level is completed or stuff like that im afraid that someone will need to code that. -
Can I use the default templates the package comes with?
Of course! Doing things with this package will require mostly building effort! Since when more variety of templates you have more random and diverse will be your maps! -
Can I customize the main script for my use?
Of course you can! You don’t even need to ask! The code is slightly commented but if you have some doubts and you want to ask me something or want me to help you I’ll be more than happy to explain you how it works and assist in your creations. -
How big my maps can be?
The only limit is the Crayta’s entities limit (Which is 8000 spawned using scripts). Depending of how big your templates are it will be bigger or smaller but that is also under the Crayta’s entities limit!. Also keep an eye if you put scripts on your templates because performance can easily drop if you have thousands of entities with scripts! -
Is it truly random or…
The maps it generates are fully random and it is almost impossible to see two maps that are exactly identical. It has nothing “hard-coded” to work in a way or another.
Basic Concepts
The code allows you to add templates to a series of lists and then determine in which groups you want those templates to appear. For example if you have templates with medieval and futuristic aesthetics and you want them to appear in themed rooms according to their aesthetics, they should be in separate groups.
Those groups are:
-
Floors: These are the templates that will be used on the floor. Check the “Hints & Ideas” section of this post for some in-detail about how to use them.
-
Walls: These are the templates that will be used to cover the walls. As like with the floors check the “Hints & Ideas” section for some tips about what you can do with them.
-
Doors: These are a special kind of wall that obviously have a door on them. Those will be placed in between rooms when the generator decides to.
-
Columns: These are used when there’s a junction of walls. These are mainly used for aesthetic reasons and can be set with an invisible entity if you don’t want to use them!
-
Furnitures: These are a special kind of object that will appear in some rooms given the chances the user decided to put them. They will never appear in small rooms or besides a wall.
In addition it will always appear centered and in the floor of a tile. In case you want to change this you can place a locator as parent and then displace the entities or meshes to the position you want relative to that locator.
For example in the “Singularity” game there’s multiple things that are classified as furniture.
Some of them are for example: Chandeliers, Tables, Machinery…
A clear example are Chandeliers which would look odd if they would appear next to a wall or door. So in that case they are set as furniture ensuring they will only appear in tiles that aren’t next to a wall and centered over them. In order to set them into the roof they use a locator.
Before going into detail on its operation, it is necessary to clarify some of
the concepts explained below.
Assets/Templates
The asset/template list is where you, as the user, can select all the templates
you want the generator to be able to use. It is divided into floors, walls, corners
and furniture.
The number of maximum templates is not limited by code but by the maximum size of
the lists that the Crayta editor allows to use which for now is 100.
Style Groups
Style groups are the generator’s way of grouping all the templates you have placed in
the previous category. Each group uses the number it occupies in the list and its value
is a with a specific syntax stated below:
{start-end}
For example if you want a style use the first ten templates it would look like
this:
1-10
The hyphen/dash is really important and can’t be replaced with anything else since
it is what the code actually uses to determine the separation between the first and
the second number.
Also both the start and the end MUST have a number since otherwise the code would
detect it as incomplete.
In case you want to state a single template, you can repeat the same number like this:
1-1
For example:
If you put 7 templates in the list of floor assets and the first 3
templates are medieval floors and the last 4 templates are futuristic floors you
should put them in the following way in the Crayta editor.
Floor Groups
1 1-3
2 4-7
This means that style number “1” will use the floor templates that are set to
numbers 1, 2 and 3. And style number “2” will use the floor templates that are
set to numbers 4, 5, 6 and 7.
This grouping will do two different kinds of rooms which some will have a futuristic
floor and others a medieval one and they won’t mix (Unless you want to!)
There’s no need for the groups to user the first templates first and then go increasing
this is mostly done to be able to visually keep track of the list of templates you are
using. But you can use the order you want.
Also you can use the same templates in different groups without any kind of problem.
Size Conditionals
This is a setting that you can find in advanced options. Using the same syntax as the
style conditionals ( {start-end} ) you can decide at which maximum and minimum room sizes
each style can appear.
If there are several styles that cover the same range they will appear randomly within
that range. This is especially useful if you want for example a style with a vestibule
aesthetic to appear only in large rooms and another style with a warehouse aesthetic
to appear only in small rooms.
Prefixes
The prefixes, although they can be edited in the advanced options, are not complicated at all.
They allow you to more or less determine under which circumstances you want certain
templates to appear. This is useful if you have templates with complex or very bulky
shapes and for example you want to avoid that two of them appear in a corner and therefore
overlap.
The only thing necessary for the prefixes to work is that you put them in the name of the
template in question by simply editing it with the Crayta editor.
In order to favor the location and needs of each user these prefixes can be changed in the
advanced options section in the main script panel.
By default the prefixes are:
"AC_": Acronym for "Avoid Corners". All templates starting with this prefix will
not appear in corners.
"FC_": Acronym for "Force Corners". All templates starting with this prefix can only
appear in corners.
"FB_": Acronym for "Force Borders". All templates starting with this prefix can only
appear adjacent to walls and therefore not in the middle of a room.
Deploying the package in your game
To use the package in your game you simply need to put in the world the template “_KRCMScriptHolderMachineStandalone” (Yes, that’s quite a looooong name!). By default, when you preview your game, a default map will be automatically generated.
This is your starting point; from there you can unleash your creativity and do whatever you want!
Alternatively you can always use and experiment with the showcase blueprint and work on it to make your own games!
By default the package is pre-configured and comes with 3 template styles. 2 of them already assigned and a third one containing a floor, a wall and an unassigned door ready for you to duplicate and make your own creations on them!
Note: If your game is going to use the default characters a size of 500 x 500 (H x W) is recommended. You can always make your own templates from scratch with your own proportion and size!
Hints & Tricks
There are a multitude of tricks you can use with this package to unleash your creativity!
Furniture next to walls
A very useful and recurring trick is that if you want certain furniture and decorations to be attached to a wall (such as shelves, pictures, pipes…) it is best to include them in the wall templates.
This is a wall template where you can see that the table with the dinosaur skull is attached to the wall itself!
Note: The more templates of the same style you have, the more variety there will be in the generated rooms. This is because it uses the templates of the same style in a completely random way. So if all the walls are the same there will be no variety as the script will have no choice!
Ceilings
You may have noticed that there is no list of ceiling assets. Even so there are a thousand ways to make a roof that works well with this package; for example if you want the roof to be always the same you can always use a voxel mesh at the desired height that covers the entire area of the map.
But if instead you want the ceilings to have the same style as your rooms or some kind of variety or specific detail you can put portions of the ceiling in the floor templates. This way each floor of each style brings with it a ceiling.
Both the showcase and the game “Singularity” do use this trick to do the ceilings for every floor tile.
This was done because re-doing the calculation necessary to determine which ceilings to place in each zone would be perhaps too heavy for the games and at the same time that same calculation is already done for the floors. So this is a great alternative that you can use!
Ensure Room Connection
Contrary to logic, putting a maximum of 4 doors or more does not ensure that all rooms will connect to their neighboring rooms as shown in the diagram below.
As you can see room number 2 does not have any door since the maximum number of doors in that diagram is 4 and due to the randomness of the code its adjoining rooms have made some double doors between them. Thus reaching the limit and avoiding that room number 2 can make a door with them.
This does not indicate that it must always happen this way but the problem and virtue of generating random maps is that you have no direct control over how things are going to turn out.
Ideally, it would have turned out like this.
But as mentioned above, this is not always the case. So you can activate in “Advanced Options” the option of “Ensure Room Connection” so that once the map data is generated it will be checked and if a room has no door to its neighbor it will be forced to have a door even if that means ignoring the maximum number of doors and the option of “Closed Areas”.
In blue are shown the doors that the function “Ensure Room Connection” would create to keep the room number 2 connected.
A trick if you want to have very few doors and make sure that all rooms connect to each other is to keep the “Ensure Room Connection” option activated and set the maximum number of doors to 0.
This is equivalent to having the maximum number of doors set to 1 with the assurance that all rooms are always connected.
However, this all depends on what your game is about. For example in a game where the walls are destructible having closed areas would be part of the game.
Unique Entities
The list of “Unique Entities” templates are those templates that only appear once each time the map is generated. And likewise they always appear, they are not likely to not appear.
As a practical example if the game is about collecting coins or some other type of collectible in this list would be the templates of those collectibles.
But this function can be used for many more things! For example in the game “Singularity” the entrance of the level, the lever that opens the exit and the exit itself are all templates in the list of entities.
As you can see after reading this it looks obvious that the exit is a completely different template!
That entails in part doing some more tricks to erase the floor and ceiling that occupy the place where the exit staircase appears. But that is up to the creativity of each game creator!
Changing Parameters between each Launch()
Another great trick I encourage you to do is that if you are going to spawn a map and then delete it when your players do something, keep in mind that you can change almost all the generator parameters from the scripts!.
You can do simple “SendToScripts(…)” to change a lot of things about how the map is going to be generated. You can even disable entirely some style groups or change their size conditions!
A great trick you can do to disable entirely a style group is enable the “Size Conditionals” and set the size condition for that concrete group to 0 ( “0-0” ) so it won’t be able to spawn at all because there’s no room with zero size.
Alternatively if you want a style to be able to spawn everywhere you can do kinda the opposite which would be setting it to from 0 to 999 (“0-999”). Since I really doubt no one is going to make rooms bigger than 999 tiles, any room will be able to take that style.
You can also change the size of the whole map before each time you launch the script again!
As a practical use for example in the game Singularity the first floor templates are just normal floor tiles but the last ones include some traps. By expanding the number of templates each style has it starts to include the templates that have a trap.
You have a complete list of all the functions you can call from another scripts at the bottom of this post.
Using the variable “generationStep” to know in which step the generator is currently at
There is a variable in the main script that determines which part of the process the code is in at any given time.
Besides being useful to guide the code within the process, it can also be useful to visually see where the code is at any given moment and give that as feedback to the user. That is what in the Singularity game it does in the loading menus and in the Showcase it does in the green panel in front of the pressure plates.
Here is a small guide to what each number means in a practical example:
local generationStep = self:GetEntity():FindScriptProperty("generationStep")
if generationStep ~= nil then
local textToDisplay = ""
if generationStep == 0 then
textToDisplay = "Initializing procedural generator..."
end
if generationStep == 1 then
textToDisplay = "Generating Rooms"
end
if generationStep == 2 then
textToDisplay = "Printing Console Map"
end
if generationStep == 3 then
textToDisplay = "Initializing Room Data"
end
if generationStep == 4 then
textToDisplay = "Pre-Initializing Tile Data"
end
if generationStep == 5 then
textToDisplay = "Initializing Tile Data"
end
if generationStep == 6 then
textToDisplay = "Processing Tiles (1/2)"
end
if generationStep == 7 then
textToDisplay = "Processing Tiles (2/2)"
end
if generationStep == 8 then
textToDisplay = "Printing Room Map"
end
if generationStep == 9 then
textToDisplay = "Spawning Map"
end
if generationStep == 10 then
textToDisplay = "Waiting for stabilization"
end
if generationStep == 777 then
textToDisplay = "Waiting to be activated"
end
if generationStep == 999 then
textToDisplay = "Done"
end
if generationStep == 997 then
textToDisplay = "Done"
end
if generationStep == 996 then
textToDisplay = "Cleaning map"
end
if generationStep == -1 then
textToDisplay = "Error on generation! Contact Owain#3593 on Discord!"
end
if self.properties.statusSign ~= nil then
self.properties.statusSign:SendToAllClients("UpdateSignStatus", textToDisplay)
end
end
Alternatively, since many scripts end up linked to the random generator entity, you can use the “ForceStatus(number)” function to change the status number of the generator to communicate additional information not related to the generator to other entities.
This is for example used in the showcase to display the message that the map is being deleted.
But be careful! The first 10 numbers (from 0 to 10) refer to concrete steps of the generator and will make it repeat steps or get bugged. You can put any number from 10 to infinity as those numbers are not used by the random generator script at all.
Crayta’s Advanced Panel Script Parameters
General Options
Execute On Init : Switches between generating the map on “Init()”/Game Start if enabled to only generate the map when is it called from scripts in any moment after the “Init()”.
Verbose: Enables or disables the console out of the script. Is it strongly encouraged that you enable the verbose option meanwhile you are editing the parameters of the script. This will allow that in case you messed something up, you will notice where in the process it did got stuck.
Generation Parameters
Cell Array Size X : Sets the X size of the area where the map will be generated. The unit used to measure this is in the X size of the templates used. (Check below “Template X Size”)
Cell Array Size Y : Sets the Y size of the area where the map will be generated. The unit used to measure this is in the Y size of the templates used. (Check below “Template Y Size”)
Max Room Size : This sets the maximum room size the map will be able to generate. This is measured in number of templates.
Min Room Size : This sets the minimum room size the map will be able to generate. If set to zero it won’t set a minimum size at all. This is also measured in number of templates.
Corridors : This may or may not allow rooms classified as “corridors” to appear.
Max Corridor Lenght : This sets the maximum corridor size the map will be able to generate. This is measured in number of templates.
Corridor Width: This sets the exact width the corridors will have. Also measured in number of templates.
Corridor Chance: The “%” of chances for a room to become a corridor in the map generation process.
Closed Areas: Whether or not to allow rooms that do not have any doors to adjacent rooms.
Max Doors: This sets the maximum doors that each room will have at maximu. Note that this setting can be slightly overriden if you have enabled in the Advanced Options section.
Door Chance: Chances to do a door or not. 1 means that almost always will place doors when able to. 100 won’t place any door at all.
Spawn Parameters
Template X Size: Here you must set the X Size of the templates you are using. Note that in order to work properly ALL templates must have the same proportion ratio and size.
Template Y Size: Here you must set the Y Size of the templates you are using. This will be used when the templates are rotated. Normally this will be the same value as .
Template Width : Here you must set the width of the templates you are using. Note that as the “Template X Size” this must be the same for all templates in order to work properly.
Map X Offset: Sets the absolute position where the map will have its Top-Left corner.
Map Y Offset: Sets the absolute position where the map will have its Top-Left corner.
Map Z Offset: Sets the absolute Z of the map.
Assets / Templates
Floor Assets: This is the list of floor templates you will use.
Wall Assets: This is the list of wall templates you will use.
Door Assets: This is the list of door templates you will use.
Corner Assets: This is the list of corner templates you will use.
Furniture Assets: This is the list of furniture assets you will use.
Unique Entities: Allows to activate/deactivate the single entities. The unique entities are those templates that only appear once each time the map is generated. For example if you want to make the player have to collect 5 coins in each map you can put a coin template 5 times in the list so that 5 coins will appear randomly on the map.
Unique Entities Assets: This is the list that appears if is enabled. Here you must add the templates of the Unique Entities you want to appear.
Style Groups
Floor Groups: This is where you set the groups of floor assets each style will use.
Wall Groups: This is where you set the groups of wall assets each style will use.
Door Groups: This is where you set the groups of door assets each style will use.
Corner Groups: This is where you set the groups of corner assets each style will use.
Furniture Groups: This is where you set the groups of floor assets each style will use.
Furniture Chances: Here you decide what percentage of probability of generating furnitures each style has. This way you can control the desired furniture density.
Advanced Options
Style Conditionals: Here you enable or disable the for the styles.
Size Conditions: This is where you set the {minimum-maximum} size in which each style can appear.
Ensure Room Connection: This will allow an extra step in the generation process that ensures that each room connects with its adjoining rooms. If this option is enabled will override the “Closed Areas” and can slightly override the “Max Doors” options too.
If it’s disabled, despite having disabled “Closed Areas” and having a high number of “Max Doors” there’s some chances that some rooms will not have access.
Avoid Corners Prefix : Here you can change the default prefix for the Avoid Corners condition. By default is it “AC_”.
Force Corners Prefix: Here you can change the default prefix for the Force Corners condition. By default is it “FC_”.
Force Borders Prefix: Here you can change the default prefix for the Force Borders condition. By default is it “FB_”.
Access by Scripts
Practically all the parameters that you can access from the Crayta panel are also
modifiable from other scripts using “entity:SendToScripts”.
For example:
self.properties.entityWithTheScript:SendToScripts("ParamSetMaxRoomSize", 5)
This would change the maximum room size to 5 from the scripts.
All functions that are accessible from scripts are listed below.
Launch() - Launches from zero the script. If you want to generate a level over and over again
every time you want to re-launch it you need to use this function.
ForceStatus(number) - Sets the status number in a forced way.
ParamSetXSize(number) - Sets the X Size of the map.
ParamSetYSize(number) - Sets the Y Size of the map.
ParamSetMaxRoomSize(number) - Sets the maximum room size.
ParamSetMinRoomSize(number) - Sets the minimum room size.
ParamSetCorridors(boolean) - Enables or disables corridors.
ParamSetMaxCorridorLenght(number) - Sets the maximum corridor lenght.
ParamSetCorridorWidth(number) - Sets the corridor width.
ParamSetCorridorChances(number) - Sets the chances for corridors to appear.
ParamSetClosedAreas(boolean) - Enables or disables closed areas.
ParamSetClosedAreasChance(number) - Sets the chances for closed areas to appear.
ParamSetMaxDoors(number) - Sets the maximum number of doors.
ParamSetDoorChance(number) - Sets the chances for doors to appear. 1 is the maximum chance
and 99 is the lowest.
ParamSetMapXOffset(number) - Sets the X absolute position where the map will start.
ParamSetMapYOffset(number) - Sets the Y absolute position where the map will start.
ParamSetMapZOffset(number) - Sets the Z absolute position where the map will start.
ParamSetFloorGroups(string, number) - Changes the templates ( {start-end} string ) the style group
stated with the {number} will use.
Example:
entity:SendToScripts("ParamSetFloorGroups", "1-5", 1)
This will make that the Style number 1 will use the templates
that take from the first to the fifth position on the asset list.
ParamSetWallGroups(string, number) - Works the same way than <ParamSetFloorGroups(string, number)> LocalOnButtonPressed
for wall style groups.
ParamSetDoorGroups(string, number) - Works the same way than <ParamSetFloorGroups(string, number)> LocalOnButtonPressed
for door style groups.
ParamSetCornerGroups(string, number) - Works the same way than <ParamSetFloorGroups(string, number)> LocalOnButtonPressed
for corner style groups.
ParamSetFurnitureGroups(string, number) - Works the same way than <ParamSetFloorGroups(string, number)> LocalOnButtonPressed
for furniture style groups.
ParamSetFurnitureChance(number(group), number(chances)) - Changes the chances of the group with the first {number}
and sets it to the second {number} chances to appear.
ParamSetStyleConditionals(boolean) - Enables or disables completely the style conditionals.
ParamSetSizeConditions(string, number) - Changes the size conditions stated in the {string} for the style group
that is listed with the stated {number}.
ParamSetEnsureRoomConnection(boolean) - Enables or disables the <Ensure Room Connection> feature.
Troubleshooting
By default nothing should fail. Although it is always possible that by modifying the list of templates something can go wrong.
The most common errors that you will find are usually related to some style not having groups in each type of asset (floor, wall, furniture…).
All styles must have a slot in each asset type and those slots must be set to anything!.
If on the contrary the error you find is that LUA has been canceled because it has taken too long to respond this error is usually caused because there is some size in “Size Conditions” that has no style; When the code finds for example a room of size 5 and there is no style that covers that size, the code will not know what to do and will be “stuck” making it take too long to respond and thus being canceled by the server.
However if you want to customize this package for your use feel free to do so! And if you need help I will be more than happy to help!
Likewise if you have any technical problem, doubt or similar don’t hesitate to contact me!