Dungeon Generation API
This is a highly commented, thorough dungeon generation API that uses BSP (Binary Space Partitioning)
The generation in the demo is slower than needed, as it tries very hard to create interesting dungeons. Because it is generic, it can be used to create dungeons made of pixels, tiles using pio8 map, or something else.
DEMO
Usage:
genesis(width,height,max_depth,pathfn,renderfn,min_size) -> rooms, tree
Generates a dungeon using the BSP algorithm.
The width and height are arbitrary units that can be used for pixels, the pico8 map, or something of your own creation.
local rooms, tree = genesis( map_width, map_height, depth, on_path_render, on_room_render ) |
max_depth (int)
How deep the BSP tree gets. The greater the number, the more and smaller rooms are generated. For large maps, a higher number is useful, smaller maps, a lower number works better. The program will begin to decrease depth automatically if the process is taking too long. (decreases every second)
pathfn (function)
it is called with (x0,y0,x1,y1) where the coordinates make a line from two points, the line is always vertical, and horizontal. it always goes from center of a container to another center of another container. It is guaranteed to go from left to right, or top to bottom.
function on_path_render (x0,y0,x1,y1) line(x0,y0,x1,y1,6) end |
renderfn (function)
it is called with (x0,y0,x1,y1) where the coordinates make a rectangle called on your own by iterating over rooms and calling room.render() on each used to render tiles to the map, or to pixels.
function on_room_render (x0,y0,x1,y1) rectfill(x0,y0,x1,y1,3) rect(x0,y0,x1,y1,6) end |
min_size (int) (default: 8)
minimum room size before the room is not added to the rooms array, default is 8.
The program will decrease the minimum size automatically if it is taking too long to process, which is usually only the case when the minimum size is too high.
returns
A tuple of rooms and the tree. rooms contains data about each room in the map, and the tree contains traversable tree of containing cells primarily used for calling rendering functions.
Rendering
Assuming you have created something like the on_path_render
and on_room_render
functions above, you then iterate over the rooms and traverse the tree to render the map. In the demo, we use these functions:
function render_rooms() foreach(rooms, function(room) room:render() end) end function render_paths(node) if (nil == node.lchild or nil == node.rchild) return node.lchild.leaf:render_path(node.rchild.leaf) render_paths(node.lchild) render_paths(node.rchild) end |
Full Example
function _init() -- since we are rendering to pixels, -- we use the screen resolution local map_width=127 local map_height=127 -- define how deep our binary trie goes -- the higher, the smaller and more rooms you get -- for smaller maps, you should use a smaller number. local depth=6 -- declare how the paths are rendered function on_path_render (x0,y0,x1,y1) line(x0,y0,x1,y1,6) end -- declare how the rooms are rendered function on_room_render (x0,y0,x1,y1) rectfill(x0,y0,x1,y1,3) rect(x0,y0,x1,y1,6) end -- get our room and tree tables from -- the generator local rooms, tree = genesis( map_width, map_height, depth, on_path_render, on_room_render ) -- now we have our rooms and tree (technically trie) -- but they arent going to render themselves. -- to do this, we need to iterate over the rooms -- and the paths by themselves. -- create a function that will render all of the rooms -- by calling the render function on each of the rooms, -- the rooms themselves will then call the on_room_render function render_rooms() foreach(rooms, function(room) room:render() end) end -- create a function that will recursively walk down the tree -- and render paths between each container cell and -- rooms. which creates our hallways. it will end when -- it reaches the "bottom" of the tree, where a node does not -- have children. function render_paths(node) if (nil == node.lchild or nil == node.rchild) return node.lchild.leaf:render_path(node.rchild.leaf) render_paths(node.lchild) render_paths(node.rchild) end -- with our functions defined, we can now render the dungeon. cls() render_paths(tree) render_rooms() end |
Github
https://github.com/MattMcFarland/dungener
Installation
You can install this into your cartridge by copy and pasting all of the code from index.lua
within the Github Repo.
Use Arrow Keys to change dungeon size
Press Z to make new dungeon
This is a dungeon created by using binary space partitioning (as described on roguebasin here: http://www.roguebasin.com/index.php?title=Basic_BSP_Dungeon_generation)
MIT Licensed
Overview
So the other day I delved deep into trig to pull out the bare minimum of what makes things move in circles.
The problem I've had in the past is that I often looked at games or demos that incorporated a lot more things, so I often would make incorrect assumptions about how to implement these types of things. My hope with this tutorial is to just show you the absolute bare minimum of what you need to get a sprite moving in a counter-clockwise circle, and explain everything line by line as best I can.
Here is what we are making:
To make something move in a circle, we have to use trigonometry. This tutorial is going to explain the trig functions we'll be using so that you can hopefully use them on your own without copy and pasting.
Prerequisites
To get the most out of this tutorial, you just need pico8 installed.
The gist
A sprite moving in a counterclockwise circle
radius = 30 originx = 64 originy = 64 angle = 0 while(true) do angle += 5 if (angle > 360) angle = 0 cls() x=originx + radius * cos(angle/360) y=originy + radius * sin(angle/360) spr(0, x, y) flip() end |
Rendering with pico8
[b]NOTE: Skip this section if you want to get right to the trig! Just go to "Show me the trig!"
Here's a boilerplate I plan on using for prototyping different games.
It consists of a splash screen which transitions to the title screen, upon which the user is encouraged to press the play button. Once the button is pressed, the screen transitions to the "gameplay" state, which is nothing but a text greeting asking the user to press a button, which then results in transitioning to the gameover screen, and finally a press of the button returns the user back to the title screen.
This is a simple implementation of a Finite State Machine
Splash -> Title -> Game -> GameOver -> ...Title
Demo
This is a tiny event system, sometimes called a "message bus"; or "bus" for short, and carries a similar API to the likes of the NodeJS Event Emitter - stripped down to the bare essentials: on
, off
, and emit
Usage:
--create a new emitter with the pubsub() function emitter = pubsub() --listen to an event emitter.on("foo", function (e) print('foo'..e) end ) --fire an event emitter.emit('foo', { a='b' }) --working with handler references: function onFoo() end emitter.on('foo', onFoo) // listen emitter.off('foo', onFoo) // unlisten |