A downloadable Engine for Windows

Buy Now$4.99 USD or more

A basic dungeon crawler engine (Game Maker Studio source file) that creates a map of any desired size, populated with rooms that can be 1x1, 2x1 or 2x2 'map cells' big.

  • Rooms are populated with enemies/treasure and decorated by passing selection scripts as arguments to the dungeon generation functions; these can be as simple as a choose() statement or be arbitrarily complex and access data in the dungeon data structure to make their choices.
  • Rooms are categorized as 'big' and 'special' separately, and can be 'special' in one of several ways (treasure room, secret treasure room, boss, player start point, ...). The meta-data is accessible by dungeon room ID and lets you adapt enemy spawn rates and other things as you see fit (such as always spawning zero enemies in the player start room, and using a set dungeon pattern for this room so it always looks the same)
  • Dungeon rooms use separate tilesets for walls, floor and obstacle objects. This enables you to partially combine biomes, such as forest clearings mixing resources for forest and plains rooms.
  • Dungeon room models are created using a sprite-based system; you can add in new room blueprints very quickly and easily.
  • Macros and constants are used to maximize legibility of code. No 'magic numbers' are in the code.
  • Big rooms are created by mirrored copies of a small-room blueprint, making big rooms symmetrical and have access to as many different shapes as smaller rooms do.
  • Smooth scrolling that follows the player character but won't move onto the next room until you cross the boundary. Only objects in view are active, speeding up the game processing and ensuring enemies in other rooms don't do anything stupid while you're not watching.
  • The engine is kept very general; enemies and items are just visual placeholders with very basic behavior. You can just load the resources from this engine into an existing game to get dungeon crawler functionality! (If you do this, make sure you also copy over the macros/constants from the dungeon generator source file).
  • Code is cleanly separated in multiple scripts and grouped by functionality, letting you find what you look for easily should you desire to tweak anything to better suit your needs.
  • Can be used for commercial projects; credit appreciated but not required.
  • Uses Floofpaws' surface sampler scripts, which are released in the public domain.
StatusReleased
CategoryAssets
Rating
Rated 5.0 out of 5 stars
(5 total ratings)
AuthorYal
Made withGameMaker
TagsDungeon Crawler, Procedural Generation

Purchase

Buy Now$4.99 USD or more

In order to download this Engine you must purchase it at or above the minimum price of $4.99 USD. You will get access to the following files:

Source Code (GMS 2.3) (ver. 2) 602 kB
Source Code (GMS1.4) 76 kB

Download demo

Download
Demo Executable 2.2 MB
Download
License Agreement 22 kB

Development log

Comments

Log in with itch.io to leave a comment.

(3 edits)

Hello, I’ve been dabbling with this engine since yesterday and I’m wondering, how do I use sprites/tiles that are lower than 32px. I tried lowering #macro TILESIZE 32 to #macro TILESIZE 16 and it only rendered all the other rooms.

Screenshot for reference image.png

~~I’m using flat colors now since I will be converting the rendering to isometric. if you can point me in the right direction as well when it comes to stripping the engine down to just the dungeon generation, that would be great!~~ (I can’t do strikethroughs XD just scratch this one out)

Cheers!

(1 edit)

I played around a bit and got it working after:

  1. Halving the size of the DUN_GRID_SIZE_X / Y macros
  2. Lowering the view size to 256x160 (I was too lazy to do the maths on what half of 384 is) - this turned out to be necessary because the camera size read from the room viewport settings is used for some calculations and having a room size smaller than this breaks the scrolling code and can cause infinite loops during generation
  3. Stretching the tileset sprites to half their original size and updating the tileset assets to use 16x16 instead of 32x32 as the size setting
  4. Stretching the door sprite to half the original size
  5. Adding a debug message
show_debug_message(string("render room #{2} at {0},{1} ({3} x {4})",argument2,argument3,argument4,argument5,argument6))

 to the start of dungen_render_room and another one that just prints "done" to the end. (Not strictly necessary, but might help debug what's happening, especially if your changes causes infinite loops somewhere)


To convert this to isometric I think something like this would be the easiest way:

  • dungen_render_tile is what places individual tiles, change it so it computes coordinates using an isometric equation instead (easiest is multiplying two diagonal vectors (the two ground-plane directions in your isometric coordinate system) with length 1 "u" and "v" with the x and y coordinates, then adding the resulting x and y components together to convert back from UV coordinates to XY coordinates) and places isotiles there (the exact details depends on how you wanna implement them but the easiest is probably layer_sprite_create)
  • dungen_render_room has a segment at the end that places enemies and items in some random cells in the room, this would also need to convert the incoming XY coordinates to UV coordinates when deciding where to spawn things (you should do this when you set up the xx/yy variables only)
  • dun_room_pos_free is used to check for collisions both in the generation phase and when things move around, depending on how you alter how a room is rendered (e.g. if you move away from using compatibility tiles) this will also need to be changed to check for walls etc accordingly; likewise the code that spawns doors in dungen_render_room also needs to be updated to remove walls the way they're implemented (currently hardcoded to remove compatibility tiles specifically)

Hope this helps, there's probably a bunch of things I could do to separate the generation and rendering steps better...

Hello, I bought this engine yesterday and I have some questions that I don't know if they have an answer but to clear my doubts, 1. What files do I need to pass to my project to have the complete dungeon generation?, 2. What scripts should I put on my character. 3. To add new rooms I must add them to the macros and then assign them in the floor1 files or are they only for the first floor? 4. If I use a lighting system with a surface creating the surface from point 0 to the maximum of the room, would it completely cover it being normal, medium or large? 5. If I want to create different rooms for each floor, can it be created from another terrain sprite so as not to use the same maps as the previous floor or can I only create rooms from that sprite file?.

  Sorry for making you read so much

1) All scripts in the Dungeon Generation folder and the Assist Scripts folder, plus the obj_dungeongenerator object. That should be enough, if you change dungen_render_tile to spawn whatever objects your game uses (instead of the placeholder tiles it uses now).

2) You don't need any special behavior in the player, it's completely separate from the dungeon generation system. (You can always check playerstate_normal and the obj_viewcontrol if you want to see how the camera is controlled)

3) Currently the pattern sprites (the sprites that are used for the floor/wall patterns) are assigned in dungen_rooms_assign_biomes, you can edit this script to e.g. pick different sprites depending on what floor you're on. The tilesets uses for walls, floors etc are set in obj_demosetup, you can set these variables based on current floor / area to control the parameters.

4) You should use a surface the size of the screen, and move it around based on the camera position, right???

5) Yes, dungen_rooms_assign_biomes currently always uses the same chance for every floor but you could edit it to pick different tilesets / pattern sprites depending on which floor you're on.

(+1)

Perfect, thank you very much for your answers. Regarding lighting, my question was because I was new to this subject, and I did it that way.

Please change the colors of this page I can't read the text. 

hello! i get error:

Script: surface_to_sampler at line 13 : wrong number of arguments for function buffer_get_surface

(1 edit) (+1)

I could confirm it, it looks like buffer_get_surface had some arguments removed since the version I was using to export the source code 2 years ago. I've uploaded a new version that's adapted for this.

The only thing that's changed is that on the script surface_to_sampler at line 12, the two last arguments passed to buffer_get_surface was removed. The corrected version of that script should look like this:


(+1)

awesome! thank you!

Hi Yal! Thanks for this great engine. I've used it extensively in my latest project, and modified it and learned a lot along the way. 

Right now I have a question regarding "closing off" certain sides of the rooms - instead of having all my room sprites with spaces for 4 doorways.

For example, if there are only 2 doors on left and right of the room, make it a long corridor and fill the top and bottom areas with "holes" or "rocks" etc (by choosing specific room sprites).

The trouble I see is, after doing a deep-dive I believe the 'room sprite' is selected before the doors are added (or at the same time), so it's doesn't seem possible in the current setup to change the room sprite after we know which walls have doors - is that correct?

If so, can you provide any hints or ideas on how I could modify this to allow unique room layouts like above? Also do you have a Discord? :)

(1 edit)

Had a look in the code, and I think the easiest way to do this would be as follows:

  • After dungen_special_loop finishes, right now it changes state to dungen_rooms_assign_biomes.
  • Instead, you'd want it to change state to a new state, for finding things like corridors and corner rooms.
  • The idea is that you loop through the entire global.grid_roomtype, checking for "rt_NORMAL" rooms. If you find one, check the four surrounding cells as well, and set its type to a corridor / corner based on which neighbors exist. (You'd create one new room type for each new type you'd add, each with a new sprite). There's 4 types of corners, 2 corridors and 4 T-junctions in total. Since you can flip sprites' orientation but not rotate them, you need 1 corner sprite, separate horizontal and vertical corridor sprites, and separate horizontal and vertical T-junction sprites (5 new sprites in total).
  • dungen_render_room currently randomly flips the room ("exdir" variable), this of course won't work for corners. You'd need to check the room type (global.grid_roomtype, check at the cell coordinates in argument2/3) and have different behavior for types that can't be rotated. On the plus side, you could reuse corner sprites this way: use the same roomsprite for all corners, and just always mirror them the same way in this step.
  • (The reason we insert this new state after the special loop is because the special loop can create new rooms, so we could base the room shape on inaccurate info if we do this earlier).
  • (When this new state finishes its loop, you'd continue at dungen_rooms_assign_biomes)

That is amazing, thank you for that! I’ll work my way through it and see if I can pull it off :)

(+1)

Oh wow. I did it. Thank you for your help and really clear guide! Here's a preview of a single door room! 
 

Deleted 2 years ago
(1 edit)

Hey, sorry for bugging you again.. figured you might know this better!

Been having trouble getting surfaces showing up in the engine. Used the demo project without any other changes than a shadow surface and it didn't work there either, so it's purely the engine causing it. Made an instance that creates a shadow surface at depth 1999 (floors are 2000, walls are 1998, creates more depth), this is the size of the view and is drawn at the camera using camera_get_view_x(view_camera[0]), camera_get_view_y(view_camera[0]).. if I put this in a new empty room, it works, but not in the main game room, just shows up as transparent in the debugger and no shadows anywhere :( Is there anything you can think of in the code that'd cause this? I can't seem to get any extra surfaces working.

The only explanation I can think of is "something goes wrong when trying to draw to the surface", I know alpha + surfaces is a bit of a wonky mix. I did a bunch of searches through the codebase to see if there's any unresolved alpha settings that might carry over, but I couldn't find anything suspicious. So... derp, back to square one.

Some things to investigate...

  • The size of the view might be zero at the moment the surface is created, perhaps? Try printing out the surface's size with a debug message after creating it.
  • If you use draw_clear_alpha to fill the surface with both color (preferably something like c_fuchsia so it's possible to tell the black background from a black surface) and 100% alpha at once, does it show up properly?
  • If the object that draws the surface draws something else at the same time (e.g. a gradient circle with draw_circle_ext, large enough to be visible even if the thing is offscreen a bit), does that work or is it also invisible? Depth sorting, accidentally turning off visible etc all could explain it being gone.
  • This goes without saying, but do you re-create the surface if it gets destroyed? It doesn't happen all the time on a desktop target, but it's definitely going to get destroyed eventually.
  • I noticed the view/HUD control object has a depth outside the 16,000 range, perhaps that could lead to oddities in the depth sorting? (I had similar issues when doing the DDG2 port job, changing depths had absolutely no effect when objects were outside the valid range). Changing the object's depth to be in front of everything else (e.g. -4000) could help shedding light on this; if it draws at the new depth the depth sorting is the reason why the surface is invisible.
(1 edit)

1. Surface size is correct, according to the debug message.
2. If I do c_fuchsia on the draw_clear_alpha, the screen does turn that colour.
3. The circle appears, on top of everything..
4. Always recreated!
5. Changed the view controller depth, no noticeable difference.

Getting a little bit of hope with the circle showing up, I'll mess around with depths and see what I find!

2. If I do c_fuchsia on the draw_clear_alpha, the screen does turn that colour.

Are you 100% sure you draw to the surface when doing this? It sounds like the symptom of not surface_set_target-ing it first (clearing the entire screen instead of the surface). And not surface_set_target-ing it would also explain why nothing is drawn to it.

Oh, correction, the entire screen doesn't turn that colour - only the floor tiles. The walls are all 'above' the clear_set_alpha as are the instances.

This is the code I'm using for the surface

(1 edit)

I think I've spotted the issue - when you set the drawing target to a surface, 0,0 is the top left corner of the surface. However, you don't always draw the surface at the 0,0 room coordinate, so drawing sprites at x,y will displace them with whatever the view top-left corner currently is. Draw the shadow sprites at x-cx, y-cy instead and it should work. (It probably worked when you did it in a separate room since you didn't move the view away from the top-left corner)

(1 edit)

Hey! I just bought this, trying to manually merge it into the game I'm working on (so I can learn what every script does).. I noticed this uses legacy tile code, whereas my game has tile layers for the walls, floor and foreground (so if I move to the bottom, my character is slightly covered by the ceiling to give the illusion of depth)


I'm a little confused as to how I'd modify your code to work with tile layers and the new tilemap_set functions - if I change "dungen_render_tile" to be tilemap_set, the game just straight up doesn't load - no errors, just hangs on 'biomes & metadata assigned~'. I also used tilemap_get_at_pixel within dungen_render_room to try to get the correct cell IDs for the tilemap_set, but it still hangs the same way.. any help would be appreciated :(

(1 edit)

I'm pretty sure the game hangs because dun_room_pos_free only returns true if there's a tile of the wtd_FLOOR type on the position (which also uses the legacy tile function), and there's a number of loops to place enemies/treasure that will loop indefinitely checking random positions until they find a free spot. (Completely blank spots are treated as collisions to stop points outside of the dungeon to count as empty) If no spot is free since the collision-checking function is broken, it will loop forever, and since you now don't spawn the legacy floor tiles anymore, it will never find a free spot.

To fully fix this, you'd need to replace all the legacy tile functions in the engine with something adapted for your multi-layer solution:


The first 3 matches delete wall tiles and overwrites them with floor tiles at positions that should have a door, the next 3 matches are the tile rendering you've already replaced, the four subsequent ones are in the collision-checking function that's currently causing the crash, and the final match is just an effect object that you don't need to worry about at the moment.

My idea for the fix to dun_room_pos_free is to replace the wall/floor checks with checks to see if the tilemap at the wall / floor layers has a nonzero tile, since walls are checked before floors it should work without changing the order/priorities.

Thanks so much for the response! I'll study this when I get back into it, though there was another thing that was worrying me - I have two GMS2 windows open, one that's just this imported project, and my game which I've imported all the scripts/objects to. I rearranged my tilesets to look like the ones in the demo (6-wide floor, then 6-wide wall below it) just to see if it'd work.. but it's black. My character renders, doors appear, but the background is black.

Not sure if there's something in my code that's causing this, but I can't figure out for the life of me why none of the tiles are rendering with no changes whatsoever lol. The same code works fine in the actual demo project.

If I can figure this out.. and get it to work with the legacy, it'd be a lot easier to start moving it over to the new layered system because I'd be able to see it working..

I pretty much changed all of the compatibility scripts for the layer (the created tile_add etc.) to use the new functions.. and the game loads, no hanging! I still have the invisible problem though.. 

How does your new version of dungen_render_tile look? New-style tiles changed from coordinates to a 0-N index so if you pipe in the same coordinates as before it would probably create tiles outside the range of the tileset. Also, the example tileset is organized in a special way and this is used by _render_tile (first row has 6 floor tiles, second row has 6 wall tiles, third has 6 obstacle tiles, so it picks an x coordinate between 0 and 5*TILESIZE and an y coordinate based on the tile type), if your tileset is arranged in a different way you'd need to adjust how the coordinates are picked to suit this. Okay I didn't see your first message, seems like you already tested the tileset arrangement issue.

As an intermediate debug step, try creating objects rather than tiles (with instance_create_depth) and make bright-colored squares for the placeholder walls and floors, this should at least tell you if the spawning is done correctly. (I would wager it's done correctly if the doors show up in the right spots, but it's best to make absolute sure).

If you still don't see anything when spawning placeholder objecst, you could try adding a show_debug_message() to their create event where they print their x/y coordinates (if those messages show up, but you can't see the objects, just compare with the player's spawn point to figure out where they spawn; if the messages aren't printed, the objects aren't spawned at all)

(+1)

Thanks for all the help! I got it working, just trying to figure out bitmasking for the wall tiles. This is what I got so far, if you're interested in how I'm destroying your code:

The numbers correspond to the bitmask, so I can debug it easily and place walls properly. The red square is a solid placed through code on the walls. Once you get into it it's very easy to modify this engine, great work on it <3

Hi Yal!

I was wondering if it's possible to manipulate the code in some way so that pre-made rooms could be randomly chosen, rather than the room sprites? I'm still rather inexperienced with programming, but this engine will help me learn a lot more!

Thank you~

(1 edit)

This is a wall of text, sorry about that x3


The rooms aren't GameMaker rooms, everything is created in a single room using the data in the room-sprites. It shouldn't be impossible to get premade rooms working, though... the two main scripts you should look into are dungen_render_room() and dungen_spawn_contents() for actually creating room contents. (Specifically the code block with the "If inside the room, we need to actually look up what tile type to spawn." comment... remove that, and then add code after the loop to create the stuff from the premade room.)

This is assuming you want to replace the sprite-sampler system with your own premade room system... if it's for what's commonly called "vaults" (occasional handmade rooms to spice up the randomness) you could add them to dungen_init_specials_list (although that still uses the sprite-based approach) and adding a new sprite to dungen_rooms_assign_biomes for it.

Another thing you could check out for a place to insert your code is dungen_rooms_spawn, once you read rrr you could check global.dungeon_room_type[rrr] to see if you've got a room of a special type, and then spawn it in a different way. (Don't forget to add walls and doors, since _render_room does that too).

I've not thought too much about HOW to make those premade rooms, but there's two easy options. In Studio 1 rooms are XML files, so you could make the rooms in the room editor, copy the files to included files, and then just parse the XML from them.


In GMS2 they're JSON files that don't contain the object names, so that's a bit trickier. I'd go with a bit more involved approach: get into the room, have the creation code of an "room exporter" object run something like

var s = "";
var nl = "
";
with(all){
  if(id != other.id){
    s += instance_create(string(x) + " + xxxx," + string(y) + " + yyyy," + object_get_name(object_index)) + ";" + nl;
  }
};
clipboard_set_text(s);
show_message("Level copied to clipboard!")

Bam, now you have your clipboard filled with a GML script that creates all those instances. Paste it in a new script in your game, and then when you wanna create that room in a dungeon, call it. Just set the variables "xxxx" and "yyyy" to the room's top-left corner coordinates in the new dungeon.

Oh, side note... just remembered instance_create() has been removed in GMS2 and it's now instance_create_layer() or something like that... you'll need to modify the code snippet to generate the correct functon name (and add a layer name) if you're going to use it in GMS2, but hopefully that's easy enough. (I don't use GMS2 a lot myself, I mostly import projects originally made in GMS1 and then export them without doing any actual editing)

(+1)

Woah, so much information! Thank you for the amazing reply Yal.  *Time to study*

hello, i am searching a very special dungeon generator... with "stick rooms" functions... could we talk about that?

Sure. First of all, what exactly is a "stick room"? Is it related to tree data structures in some way?

Predefined rooms can be accidentally glued together. These predefined rooms are stored somewhere and used by the generator when generating.

This engine uses predefined room layouts, room maps are stored in bitmaps so you can quickly draw new rooms using Game Maker's sprite editor. There's also separate sprites for normal rooms, boss rooms, player start rooms and treasure rooms, so you can easily have some layouts only be used in some contexts, and this system probably wouldn't be too hard to extend either (e.g. you could randomly change some rooms' layouts to layouts from your "SPECIAL STICK ROOM" if you want 2-3 stick rooms in the middle of all the normal ones).

i need one big room, and this room is generated of any room "parts" and stick together...

Everything is still placed in a single GM room... the view scrolling effect is just there because the generator is inspired by Binding of Isaac, if you remove it and have the view follow the player normally it becomes a single cohesive room.

___________________________________________
############################################################################################
FATAL ERROR in Vertex Shader compilation
ShaderName: shader_hurtflash
D3DXCompile failed - result
at gml_Object_parent_enemy_Draw_0
############################################################################################

If this error is displayed, it means your computer doesn't support shaders. One possibility could be that you don't have the DirectX 9 runtime installed, you should be able to get it from here: https://www.microsoft.com/en-us/download/details.aspx?displaylang=en&id=35

If that fails, the problem is probably that your GPU doesn't support OpenGL, and you'll need to remove the shader from the game (and remove the code in parent_enemy that sets and resets the shader before drawing, so you don't have a reference to a nonexistent resource).

Sorry about this, but I am extremely dumb and I'm having trouble. I purchased this engine mostly to figure out how to generate room layouts based on predefined blueprints but I can't for the life of me figure out how it works in this engine. I've been scouring all the scripts to figure out where the blueprints are defined or how to make new ones and it's driving me crazy.

(+2)

The rooms are actually defined using four sprites (one pixel corresponds to one tile in the room, each color means a different type of tile) - Sprites-->Room Maps will give you those, and their names should explain the purpose of each map. Just add more subimages to a sprite and they should automatically get added to the random selection.

You'll also need dungen_render_room() and dungen_render_tile() to actually create a room from the blueprints, though. (And also all the scripts in the folder Scripts-->Assist Scripts--->Floofpaws' Sampler Extension). I think they're pretty much standalone enough that they can be used on their own without the rest of the dungeon generation structure around them with just some small changes to dungen_render_room():

  • Remove the part that spawns enemies and treasures.
  • Remove the lookup of the "doorflags" variable and pass that as an argument instead.

In either case, the important bit is how dungen_render_room() uses a sampler to read the sprite data. The colors that are used to represent different types of tiles are defined in the list of macros, under __VaultMapData__, and these constants is what dungen_render_tile() is checking for.


If there's anything more that's unclear, feel free to ask :3

(+1)

I thought it worked something like that, and now I know what my problem was:

when you import another project, the sprites show up as black squares in the resource menu until you view or edit them. so my eyes just sort of passed over these seemingly unhelpful square sprites. Thanks for replying!

10/10