Sign in

Dalichrome

@index.dalichro.me.ap.brid.gy
1 followers 0 following 9 posts

Projects and process: indie games, pixel art, figure drawing, and software—documented in fog-tinted pixels 🌉 bridged from dalichro.me on the fediverse by fed.brid.gy

PostsRepliesMedia
Dalichrome @index.dalichro.me.ap.brid.gy · 28/03/2026
This is a blog about how we made a game in a week that won a jam with 1,400+ entries. But it’s more than that, it’s a story of redemption...
dalichro.me
Dead Average to 1st Place in Six Months: How We Won the Brackeys Game Jam
This is a blog about how we made a game in a week that won a jam with 1,400+ entries. But it’s more than that, it’s a story of redemption and recognition. It’s about how a team of four people, none of whom are professional game developers, that went from barely finishing a game that landed dead average in our last jam, GMTK 25, to winning 1st place in the Brackeys game jam. We won in enjoyment, game-play categories and the overall game jam just six months later. So I want to tell you how we failed, learned and quickly turned around to produce our best title ever. ### Background To keep it brief, we are a team of four friends: Jacob Engelbrecht, Ian McBee, Gabriel Soares, and me (aka Dalichrome). This is our third attempt at a game jam. Our first gamejam was GMTK 24, then we did GMTK 25 in August 2025. In the second jam, we did more poorly than our first (you can read about that postmortem here), but to summarize, it was a mess from a leadership and conceptual standpoint. But I learned a lot from that failure, and as the team lead, it made me realize what we really needed to improve on to become more competitive. For our next jam we chose Brackeys jam. Hosted by one of the most popular Unity tutorial YouTubers, it’s generally Itch’s second largest game jam. The itch header for the Brackeys Jam 2026.1 ### The Plan In this jam, I wanted to relentlessly focus on three things: that we would have time to polish the game, that players would enjoy it, and that our team would enjoy making the game. Makes sense right? But it’s easier said than done. ### Time To Polish Specifically, I was thinking how we could limit the scope without the knowledge of the jam’s theme. I tried to think about successful jam games that had limited scope that stayed engaging and fun. The game Flora in GMTK25 came to mind. A game my friend had really liked and showed it to me. With its jazzy music set to cozy puzzles about growing flowers and having moles eat those flowers, I thought now this is a polished jam game. Each level is a stage that introduces new mechanics that naturally guides the player into learning them while having fun. I thought we should replicate this format and come up with concepts that are stage-based on this approach of intuitively tutorial-izing players, limiting the scope, and of course, keeping the game fun. Screenshot of a stage in the GTMK 25 Game Flora ### Making it Fun But every game also needs to be fun. So the second constraint I set for the team was based on how stage-based games always start from a basic mechanic and then build upon it. That the basic mechanic **must** be interesting and fun in isolation. Essentially, if the game is about jumping, the jumping by itself, lacking music, visuals, et cetera **must** be enjoyable. I thought then if we chose a concept that fulfilled those two constraints, no matter how many additional mechanics we had to cut due to time, we wouldn’t sacrifice the soul and enjoyment of the game. With that in mind, I also set a hard timeline that we’d build a playable version of the game halfway through the jam and I’d bring in friends to act as testers so we’d have new feedback every night to ensure that we could identify and fix unintuitive elements. The rough plan we sketched out I hoped pre-planning this would ensure that we would choose a well scoped and fun concept. I was so focused on getting something polished since our last jam entries never really got there. With these constraints, I still felt that we had to chose the concept in accordance to what our team team was good at in order to really accomplish a great jam game. ### Choosing the Concept / Making it Fun to Develop When choosing a concept, I’ve learned the most important thing is to not choose what you think others wont do. It is not to choose what sounds the best or to not choose what will make the best game. But instead to choose something that your team can excel at and will enjoy doing. I heard a lot from both my friends Jacob and Ian that they wished they could code more. They both are career programmers and not game developers, so when we were choosing concepts, I tried to keep this in top of mind. The theme of the jam was _“Strange Places”_ and we did some brainstorming and eventually, three ideas stuck out: * A platformer about environments made of silhouettes that a flashlight reveals are not what you think they would be. * A game about drawing triangles that disappear ships. * A game about playing a board game that has a whole bunch of strange variant rules, where one rule gets added a stage. The platformer, I think, failed the basic mechanic is fun rule. The triangle game seemed like a great idea, but it seemed _code-light_ to me. A lot of it would be physics work and ship AI work which would be a lot interacting with Unity API and or our AI pathing package. The board game idea didn’t really sound fun, but I knew we just had to design it so that the core part was fun and it would meet all my other constraints. I also thought maybe if we based it on chess, Jacob and Ian would be able to spend all their time outside the Unity API making their own chess API and I could hook everything together. This way the concept would maximize what they wanted to do and are good at and met all the constraints I was thinking about. So I basically tried as hard as I could to convince the team that despite the fact that it did not _‘sound sexy’_ that the many chess variants game would be the best for us. Everyone was really hesitant. It wasn’t really like any concept that we had done before (most which used random generation), but everyone was willing to hear me out. With enough explaining, everyone started to feel excited about the concept. But we still needed to decide on the core mechanic. I had imagined that the simplest form of chess would just be the simplest pieces on a small board fighting each other. I thought about four pawns against four pawns on a 4 x 4 where the first pawn across wins. But I was unsure if it was fun or viable to be the base of the game. So we tested it. The basic concept of the game We drew the concept in _paint.net_ and played by just giving each other coordinates and by moving the pictures into the right spot. Even just on the 4x4, we actually had to think a little bit and eventually we decided… yeah this is fun. So with that, we locked in the theme and started working. ### Development We split the roles pretty cleanly, I acted as director/artist/UI developer. Jacob worked on mechanic design and chess back-end. Ian did some UI, some design work and a lot of dialog writing. Gabe did all the music and sound effects. Our primary concern was getting a prototype up and running where someone could play the game against an AI. But since Gabe needed direction for music, we first outlined a quick story. Some world event has led to the creation of ‘pawn chess’ and our main character wants to go and conquer all varieties of the game, become the first world champion of this new game. He learns how to play in a NYC park, then learns about the “ _en passant_ ” in Paris, gets the new _‘poison pawn’_ in the Amazon, gets the 5x5 board in Antarctica, and finally gets the knight or the _‘cow’_ piece in the UFO for stage five. With that rough outline, he set off on the music and we began to code. What I sketched out when I envisioned the map ### UI Design I was in charge of the UI Design and from the get-go, I wanted to have a very tactile and visceral type of chess. Nothing like the clinical and clean interface of ‘chess.com’. I wanted to instead have something grounded and with character. So instead, I wanted a board with perspective and players to have hands and hands to express the character of the opponent. I thought that adding silly hand movements was a quick and easy way for players to figure out the vibe of the game. That they were playing and to not just get hooked into the game-play, but also the world of the game. I started with referencing my own hands for the player and scratched up some simple picking up and idle hand frames. The Paris stage in the prototype version For removing pieces, I had the idea that the player would just chuck the piece off the board which I think captured exactly the vibe I was looking for. After I had the player hand set up, I transitioned to getting the map working. For the map, I really just wanted something incredibly intuitive that made people feel like they were visiting different places. So a world map was chosen with little google maps style pins and numbers. It was cheap and easy, but we wanted to have a little line renderer create a line between pins like an Indiana Jones movie, but alas time constraints again. Essentially I just wanted everything to be as easily understood as possible, so we could reduce tutorial-izing as much as possible. That included the hands, map and of course the chess itself and the design of the pieces. ### Game Design The game within the game, _‘pawn chess’_ , as we started calling it, really focuses on constrained movement. Pawns themselves are very restricted pieces and the joy of the game we believed really came from this locked down and limited option game-play. So when we designed new pieces, we wanted to add new pieces that were similarly constrained. The poison pawn, introduced on level three, destroys any piece that takes it and moves like a normal pawn. The ‘cow’ piece introduced in level five was just a knight, but it could not win the game. Jacob was coding the AI and doing the stage design and was able to get these and more coded in before our first build. The UFO stage with the ‘cow’ and ‘poison pawn’ pieces in the prototype ### The Prototype So with the UI done and the back-end done, it was all about wiring them together. Jacob and I did quite a bit of coordinating in tying the back-end to the UI. Then I quickly wired all the new stages he had made to the map and with that we had a minimally viable game done on Wednesday just before our testing deadline. So after work on Thursday (yes we all had day jobs during this), we built the game, put it up on a private itch page and waited for my friend and first tester, Jack. Some game-play of the first stage in the prototype ### Initial Testing The choice of who you have testing your game is really important. You want someone who is honest, likes the genre you’re shooting for, gives good advice and won’t forget to show up. Jack is all those things and a great guy so I was very happy that he was our first tester. We just barely had a game, no AI hand, no dialog yet and just five stages. But he really enjoyed it. He blasted through it with little complaints and navigated the map and UI with zero problems. His main criticisms were that he found that the AI moving instantly (since there was no AI hand yet) was confused and that it could use some story or dialog to draw it together. As these were things we were already aware of and working on, I was incredibly surprised. This was the moment I realized this was a good game and our direction was right on the money. ### Realization Oftentimes in jams prior, I found myself delusional about the state and status of my game. I would think only about how my random generation was so cool and impressive when the combat was spammy and goal was unclear. I would think that the second stage is the best when no one could figure out how to get to the second stage. As a developer, you often think about how you want the game to come across, and its incredibly hard to figure out how it really comes across, as its not like you can do a blind play-through. Because of that, I was fully prepared for him to tell me it wasn’t fun, or that the chess felt confusing. But that never happened and instead he said he just wanted it to be longer. I thought, _wow_ we’ve not only accomplished something that is genuinely intuitive and fun, but also something that’s going according to plan. Now if we can polish the hell out of it, then we will have truly made a really good game. ### Second Half of the Game With that realization in mind, I began to double my efforts. This year, I’ve been extremely busy between work, starting marathon training, doing development projects, art practice, and more. I was incredibly burnt out going into this jam and just wanted to simplify the design process as much as possible. So this was the moment I really began to lock in. We spent the next couple days working around the clock, adding the AI Hand, adding the dialog, adding the story, and designing those last five stages as well as having enough time to add five bonus stages. The final map of the jam version of the game We were feeling really proud in what we had made and were ready to submit around 1AM, several hours before the deadline. But I remembered that Gabe had given me some ambience tracks to back the music and I just didn’t have the mental wavelength to figure out how to get the ambience tracks looping and persistent between scene loads. This is when Gabe called in as he just finished a Metal show (_yes Gabe is in a metal band_) and he said as soon as he got home that he would re-render all the music in the game with the ambience in it and I was kinda surprised but also game and said lets do it. ### Last Minutes So as Gabe drove home, I added more hand variants, fixed up some of the ‘table’ graphics (the stump and starting table) and just gave the whole game one more ‘art’ pass. We found a bug that broke the visibility of the clock in WebGL builds (had some async code, that doesn’t work in WebGL) and when Gabe finally came back and rendered the tracks, we quickly put them in the game did a short test, submitted at around 4AM and then we crashed. ### Reception I woke up on Sunday after about 5 hours of sleep and saw all these amazing reviews. People saying they hope we win and that they thought it was ‘full release’ even. A type of positivity we’ve never received before. The whole team was abuzz and frankly in shock that we finally had made a good game in a jam, we were even beginning to believe maybe we could be in the top ten! The next two week were frankly, an anxious waiting game, but it passed surprisingly quickly and before we knew it, results day was close. One of the first amazing comments we received! ### Winning I’m American so the results for the Jam came out on Sunday at 7am EST on the day that Daylight Savings time ‘ _springed back_ ’. Not only that but, I had had laser eye surgery two days before. So I was wearing goggles to prevent rubbing my eyes in my sleep and woke up what I felt like was the middle of the night, checked my phone, which was super bright and blurry still and saw it was around 7:20 AM. I quickly checked the results and learned that we won first in overall, first in gameplay, and first in enjoyment. The feeling was a mixture of shock, totally overwhelmed, and brimming with joy. I’ve been developing games for more than seven years now and to have such recognition felt unreal and so good, like I finally knew that I was good at this. I then thought immediately I have to let everyone else know and called Gabe as he messaged back first. He was lying in bed with the flu, voice gravely as he hadn’t had a wink of sleep all night. Then Ian called in, turned on his camera, he was on stage about to perform music for his church. And we were all just kind of in this weird state of shock and excitement, trying to figure out, what does this mean? The specific results of the jam ### What’s Next? Well, its been a couple weeks, we decided that it means, that people like our game! So, we want to keep making ‘ _House Rules_ ’. In fact, we’ve already renamed the game to ‘Rulehouse’ and made a second patch on itch, with mobile compatibility, pre-moving, and hard-mode. But I want to say how grateful we are to everyone that voted for us in the jam and gave us the opportunity to even think about making a commercial title. We’re still shocked and trying to figure out, how in the hell do you market a game? A gif thumbnail for the post-jam version of the game, now live on itch! Well for a first step, we made a steam page now for Rulehouse and it’d be great if you could wishlist the game, so we can notify you when it comes out. You can also play the original jam version on itch here if you want to see what it was like when it won. Anyways thanks for reading and I hope that our story can help others who are looking to perform in jams and wish us luck in trying to convert this success into a commercial one. Thanks!
021
Dalichrome @index.dalichro.me.ap.brid.gy · 01/03/2026
Brackeys 2026.1 Game Jam Submission
dalichro.me
House Rules
I made a game for the Brackeys 2026.1 Game Jam! If you haven't played it yet, check it out below! It's the best thing the team and I have made yet. House Rules by Dalichrome
000
Dalichrome @index.dalichro.me.ap.brid.gy · 05/02/2026
I've written several Hytale mods that have a couple thousand downloads as of posting this, feel free to check them out. Link to my CurseForge
dalichro.me
Hytale Mods
<p>I've written several Hytale mods that have a couple thousand downloads as of posting this, feel free to check them out.<br /><br /><a href="https://www.curseforge.com/members/dalichrome/projects" rel="noreferrer">Link to my CurseForge</a></p>
000
Dalichrome @index.dalichro.me.ap.brid.gy · 05/02/2026
An avatar I made for a friend: him as a dark magician.
dalichro.me
Dark Magician
<p>An avatar I made for a friend: him as a dark magician.</p>
000
Dalichrome @index.dalichro.me.ap.brid.gy · 19/01/2026
dalichro.me
Internet Manager v1.3.0 - Retrovoucher Update
Internet Manager gets a major new update that adds 'retrovouchers' and much more
000
Dalichrome @index.dalichro.me.ap.brid.gy · 05/10/2025
dalichro.me
The Best Modular Character Workflow With Aseprite
Hi everyone, this blog is a little different from the rest, this is a **tutorial** post. Meaning that if you have no interest in implementing a modular character system using Aseprite, then you can skip this one, I won't judge you. ### What is a Modular Character System? It is a system in games where a character does not exist as a single graphic, but as many separate graphics or modules. In this blog, we'll specifically be using a humanoid character and look at how we can visually 'equip' different clothing, glasses, and even hair styles using Unity and Aseprite. By the end of this tutorial, you'll be able to swap helmets, hairstyles, and clothing on a single character rig with minimal code. And of course there are many many other use cases for this workflow, it doesn't need to be a character you use. **Modular** is the key word here, not character. With that said let me lay down the Aseprite basics. ### How to setup Aseprite In aseprite, you need to make sure every animation is a different tag and each 'module' that you want to trigger on and off is a layer. Take a look at this image. The reason some of the layers are missing frames on the right side is that I ended up flipping the player in this game instead of using those animations This is a screen capture of the aseprite file I made for the character in my most recent game jam submission. You can see each layer is either the character itself (Main) or some graphic that you'd want equipped. This is exactly how you want to make your aseprite file so that this workflow will actually work. Also it is important to keep those layer names nice and legible as you'll see, we need to use those later. ### In Unity First of all you need to go into your package manager window and make sure that you have Aseprite Importer installed, don't worry this is a package made and distributed by Unity themselves. After that, just drag your '.aseprite' file into a Unity folder and check it's import settings. So this is not the default unity view, but you can see I have clicked my _PlayerFrames.aseprite_ in the project view and now in the inspector I can see its import settings. What's important for us here is that we have '_Individual Layers_ ' enabled and '_Animation Clips_ ' enabled, then press that '_Export Animation Assets_ ' button and export the controller somewhere in your project. That controller is independent of the aseprite file, so if you delete the _whatever.aseprite_ file, the controller will stay around. The animation clips are a different story. If you export them, they will not change with the aseprite file anymore, so I recommend not doing that and using the ones the importer automatically creates (like Attack-Right in the project view of the above image). Also, this tutorial is not about setting up an animation controller, but you will need to have one working and setup for use in this tutorial, so make sure you set up the one you just exported. With that done you can now click on one of those clips and you should see something like this in the Animation tab: This can be a little confusing if you've never worked too in depth with the Unity Animation tab before, but Unity is now expecting different sprite renderers for each layer/module in the aseprite file. Specifically, it wants a sprite renderer named the same as the layer childed to object that has the animator component. So the 'TraumaCharacterModel' has no sprite renderer, but its childed objects 'Main', 'Torso', 'Hair', 'Beard', and 'Glasses' all do. Now with this setup you have a zero code solution for modular characters in Unity, but there remains one big issue. Scalability, if you have 100 different modules, you need 100 different childed game objects, which feels obscene, because it is. So we need a little code. ### The Coding The solution to the scalability problem is simple. Rename a childed object to the layer you want to show and refresh the animator. If you do so you can reuse the same generic object for many different layers. Here is the simplest way I can express that in code: layerGameObject.name = layer.name; layerGameObject.SetActive(true); GetComponent<Animator>().Rebind(); Of course, that only applies to one layer and one object, which creates another problem. What if you want to show modules concurrently like a separate object for helmet and chest-piece? In order to do that we need multiple generic objects. First let's call these objects '_slots_ '. Each layer needs to be associated with a slot. We can make two simple classes and an enum for the data we need to assign in the inspector. public enum CosmeticSlotType { NA, Torso, Pants, Head, Eye, Chin, Feet } [Serializable] public class CosmeticSlot { public CosmeticSlotType slot; public SpriteRenderer spriteRenderer; } [Serializable] public class Cosmetic { public string name; public CosmeticSlotType slot; } Then we can make a _MonoBehaviour_ to attach to the player. public class PlayerCosmetics : MonoBehaviour { [SerializeField] private List<Cosmetic> cosmetics; [SerializeField] private List<CosmeticSlot> cosmeticSlots; ... Go back into Unity, attach the script, and then type each layer name from Aseprite carefully into the inspector. Once you've done that head back into the code and we can add some data structures. public class PlayerCosmetics : MonoBehaviour { [SerializeField] private List<Cosmetic> cosmetics; [SerializeField] private List<CosmeticSlot> cosmeticSlots; private Dictionary<CosmeticSlotType, CosmeticSlotInfo> slotInfoDict = new(); private Animator animator; private class CosmeticSlotInfo { public CosmeticSlotType slot; public List<Cosmetic> cosmetics; public SpriteRenderer renderer; public GameObject GameObject { get { return renderer.gameObject; } } public CosmeticSlotInfo(CosmeticSlotType slot) { this.slot = slot; cosmetics = new (); renderer = null; } public void AddCosmetic(Cosmetic cosmetic) { cosmetics.Add(cosmetic); } } private void Start() { animator = GetComponent<Animator>(); CreateCosmeticSlots(); } private void CreateCosmeticSlots() { slotInfoDict = new(); foreach (CosmeticSlotType slot in Enum.GetValues(typeof(CosmeticSlotType))) { slotInfoDict.Add(slot, new(slot)); } foreach (Cosmetic cosmetic in cosmetics) { if(slotInfoDict.ContainsKey(cosmetic.slot)) slotInfoDict[cosmetic.slot].AddCosmetic(cosmetic); } foreach (CosmeticSlot slot in cosmeticSlots) { CosmeticSlotInfo info = slotInfoDict[slot.slot]; info.renderer = slot.spriteRenderer; slot.spriteRenderer.sprite = null; } } ... Now we have a dictionary where we can put in the cosmetic slot type and get back the actual slot class. With that working, all we need to do is to receive a cosmetic class and... public void SetCosmetic(Cosmetic cosmetic) { CosmeticSlotInfo slot = slotInfoDict[cosmetic.slot]; slot.GameObject.name = cosmetic.name; animator.Rebind(); } Now we can easily swap between different layers in one slot! We can also define a lot of slots and have hundreds of animations per slot, but still only have a limited number of game objects! If you want to test it, try adding something like this to '_Awake_ ': private void Awake() { Cosmetic newCosmetic = new(); newCosmetic.name = "AsepriteLayerName"; newCosmetic.slot = CosmeticSlotType.Head; SetCosmetic(newCosmetic); } ## Conclusion I was really excited when I implemented this during a jam as I had no clue that modular character animation could be this easy! The code I've provided is way less than 100 lines and it gives you all you need for the simplest version of a working system. Of course there are more professional implementations than enums and dictionaries, like having your own scriptable object item class in your resources folder that you hook into instead. But, I'll leave that to you to figure out. So let me know if you guys end up using this in your games and sign up to my website if you want more of this stuff or look at some of my other blogs. Thanks! ## Sign up for Dalichrome Projects and process: indie games, pixel art, figure drawing, and software—documented in fog-tinted pixels join the cool crew Email sent! Check your inbox to complete your signup. You can unsubscribe, but don't
000
Dalichrome @index.dalichro.me.ap.brid.gy · 04/10/2025
dalichro.me
Making A Graph Based Random Generator
### Preface So recently I participated in the 2025 Game Maker's Toolkit Game Jam (link to the blog on that) and in preparation for that event, and for use in my in-progress game, I made some significant changes to my random generator package. Now as it stands I view this random generator as my technical _mangum opus_ , it's a complicated project and the architecture is the best I've ever designed. Although I was proud of it, prior to the changes I'm about to layout, it lacked several key features that would make it impossible to use in production. But before I get into why that is, since this is going to be a more technical post, I've decided to break it up section by section more cleanly so that readers can skip to different parts easily. So skip it if you want, I'll start with describing terminology you should know so that we can all be on the same page. ### Terms So first, let's go over the terms I use for the data structures that store the generated data. #### Data Storage Terms **Tile** - The smallest unit in a tile grid, it contains an x and y coordinate and a tile type for each tile layer. Think of it as representing a small square of terrain that can define anything from a square foot of sea to a square foot of mossy castle walls. **Tile Grid** - The actual grid that contains all the tiles. The grids in my generator can have variable width and height, but cannot stretch infinitely. **Map** - Is just another term for tile grid. **Generation** - Another name for a map or tile grid, something output by a generator. **Tile Type** - The actual data that defines what's in a tile. A tile type could be grass, or tree, or anything that can generate really. Each tile type is restricted to only being placed in a specific tile layer. **Tile Layer** - Inside each tile there are layers, such as ground, wall, object, and debug (these are the current layers I have). Imagine each layer like a 2D plane, each layer renders on top of the previous. Each tile only contains one tile type for each layer. #### Generation Operations Okay now that we have an idea of my implementation of the tile grid, let's talk about how we create them. **Generator** - The algorithm used to place or modify tiles on an existing tile grid. An example would a noise generator, which the user gives a tile i.e. grass, and it places it at a random amount, outputting something visually similar to a grassy TV static. **Grid Operation** - An algorithm used to do something with a tile grid. A generator is a grid operation, but as you will see later there are many types of grid operations that are not generators. What's important is to realize this is the umbrella term. **Tile Masking** - The process of including or excluding certain tiles types from being modified by generators. **Generation Config Stack** - In the old linear model of generation, this would be the information that told the random generator exactly what to generate. **Operation Config** - The set of variables that tweak specific parts of a grid operation. They are serializable and are used to save specific generation information. **Random Generator -** This is the short-hand version of 2D Random Terrain Generator, it refers to the entire program that makes random terrain. ## **Recap** So, as I said this blog is about an update to an existing package. That's right, in fact I've already used it in the same jam last year! So, I'm not making a new random generator, but I'm modify the same one. It's important to know that the random generator at this point existed in two different parts. One was the package that housed all the actual generation logic that existed to be added to different unity packages via the package manager. The second was a separate Unity UI project that had its own front end that allowed users to see, test, and modify their generations. Picture of the old UI, notice the 'stack' of configs on the left The UI application could also export certain generation data as json, which then you could load into other unity projects and just import config stacks that you had tested fairly easily. But this blog post is only about updating the package side of the project. ### **Problem** So that sounds pretty good, so what's the issue? The answer is pretty straightforward, it could not generate 'regions'. I didn't realize it when I was initially making the random generator, but regions are _insanely_ important. Just think about how regional terrain is in real-life, seas, hills, mountains, beaches, cliffs, forests, jungles, swamps, etc. Imagine you just had only one of those, and that was basically what my generator could handle. You could make any one of those 'biomes,' but only one. So, I don't think its an overstatement to say that a random generator without regions is missing the core feature that makes maps interesting or engaging the first place. Now the technical reason why I couldn't create regions was I was using a stacked based config system, that had each generator resolved sequentially in order. This meant there was no way to split off a section of the map and run different generators on that section. So in order to make a good random generator that could make interesting maps, I had to design a new system that could do this. ### **Design** So it might sound complicated, but the high-level design changes are actually really simple. I wanted to take the linear stack based config system and transition to a graph based config system. This is would allow for multiple branching paths of generation and enable 'regionality' in generation most importantly. But as the generator stood then, the only type of grid operations were generators, which take in a tile grid and spit out a different tile grid. So I needed several new grid operations in order to enable this _regional_ generation workflow. Firstly we need an operation called 'splitters' that split the tile grid into different regions. A good example of a splitter algorithm is the Voronoi algorithm, which without going into specifics creates very geometric cellular regions. A Voronoi diagram, given the set of black points, it can create these geometric regions Then we needed 'filters' these are a bit unique as they don't change tile grid directly, but instead take all the different 'splits' or different regions from the splitter then select a subset of those then apply them to the tile grid. This makes it so that the next generators can only modify the regions that the filter returned. Then finally are the 'joiners' that take different 'lines' of generators then join them together, think of like a 'git merge'. These lines of generators are the different generators that run off of a filtered region after a splitter then a filter, you can think of them as the region specific generators. But I needed more than just these new operations, I needed to define a graph structure as well to put these operations in and then have some sort of UI that allowed me to make and modify them easily. # **Execution** I thought the UI was the likeliest point of failure, so I started from there, thinking I could back off if I needed to. I knew coding a UI from scratch was out of the question, so I knew that I needed to find a perfect package that met all my needs. ## XNode Sometimes the solution just exists. The package I found, XNode, was the perfect fit. It's a open source package that allows you to extend their node and graph classes to leverage their built-in UI. After I found it, I started with verifying that I could actually use it, by making the class GeneratorNode that extended their node class and GeneratorGraph that extended their graph class. Not only did it work, but it worked much better than I expected, within a couple weeks I was able to define a graph where each node was a config for a grid operation in the XNode UI. I was even able to write abstract classes that handled most of the logic for every node tied to a grid operation, meaning that I only needed one class per operation type not for each implementation. The abstraction worked so well that any new operation, when its added, automatically gets full UI support - no code needed. [Picture of the XNode UI] It was a slim and fast solution and I couldn't have been more surprised. Now that the UI was complete, I need to actually implement the traversal of the graph I had just made. ## Traversal To actually generate a map, the random generator needs to move through generator graph and each operation must run in the correct order _(Not doing parallel generation yet)_. Well, what is the correct order? The most obvious answer is we need to move from the start of the graph to the end. So I added some start and end nodes to the graph. A linear graph would just be a line of generators connecting start to finish. So the most simple traversal is run each node in order from start to finish. But we want to really leverage branching paths, that's the whole point of this feature and this blog. So let's add a branching path to that. So take a moment and think logically, how we want to move through this? We have two branching paths, so the biggest difference now is we can't run left to right anymore. The nodes after the joiner have to run after all the generators to left of it run. The way I handled this was by thinking of joiners like sort of a video game water puzzle. So each time you get to the joiner, you bring a bucket of water and fill up the pit a little bit more. When the pit is full, you swim across easily. But where does the water come from? Well I like to think of splitters as something a little bit more complicated, imagine a ledge you can get down, but not back up that also has a well at the bottom. A splitter is just like that, the first time you get to one, you can cross no problem, nothing needed. Then you take a bucket of water from the well, so when you find a water puzzle / joiner you can fill it up. Of course you don't have enough water on one go to fill the joiner, but look at the joiner diagram again, you can grab a box from the joiner then come back and place the box, get more water. This exchange is the basic back and forth of the traversal logic. Going forward with buckets and going backwards with boxes. Eventually you'll be able to cross the joiner and all will be well. But this doesn't explain why you'd want to bring boxes to go back past a splitter in the first place. The reason you need to be able to do that is if you have multiple layers of splitters! Generation Graph on the left with multiple splitters, a resulting map on the right Yes, as you can see graphs can have more than one level of regions. You can split a region into smaller sub regions, so our traversal logic requires us to be able to handle any amount of splitters or joiners and any amount of sub regions. Now knowing that I think its pretty clear how you have to make your way back over a splitter at some point, to run a splitter further up. I also understand that these images aren't a stand in for code which is much more precise. So I'll clarify that each splitter needs _n_ boxes, _n_ being the number of filters that branch off of it. Also there is only _n_ buckets of water at each 'well' as well (_har har_). Now the algorithm I described is also basically just a different form of DFS (Depth First Search), so I could've described it that way as well, but I personally found the imagery resonated with me more. Also it's a bit more fun that way. ## Result Simply put, the introduction of XNode and graphs in conjunction with new node operations such as splitters, filters and joiners worked to enable the generation of maps with regions. To come full circle, the real world application of this is readily playable on my itch in the form of the recent GMTK 25 Jam Submission my team and I made. The graph that I ended up making for that game jam had over 70 different nodes and was 384 x 384 tiles large, a total of 147,456 per layer and with four layers per tile, a total of 589,824 different tile types generated in less than three seconds in a browser- proving the system could handle production-scale complexity under jam constraints. Having just looked at some other graphs in the traversal section, look at the behemoth we ended up using. So I was incredibly proud of not only getting this working, but having it work under such pressure. But as they say now is the youngest you'll ever be and now is the worst my random generator will ever be, as I also cannot wait to continue to push the random generator further and make even cooler maps. ### Future Plans So what's next? The first thing I am going to tackle is how layers work. I want users to be able to add their own layers, which I think requires, in a weird way, to make the generator 3D? As there if there is no set number of layers the z axis could stretch infinitely, so I think at that point the generator will be a 3D generator that's meant to be rendered in 2D. The main reason for this change is I need to add two new layers at least and I feel like its a good opportunity to look into the system and future proof it. Also if I do it right, I might be able to expand its use cases. After that I hope to add meta-data and structures to the generator. I'm sure one of those two will warrant another blog post, whenever I get to it. But anyways thank you so much for getting this far! This has probably been one of the largest pieces of writing I've ever done on one of the most complicated features I've ever implemented. I tried to keep it technical but mainly architectural, so let me know how I did in the comments. I hope you enjoyed it and see you next time!
000
Dalichrome @index.dalichro.me.ap.brid.gy · 28/09/2025
dalichro.me
Dalichrome's First Anniversary
So it's almost been one year since I launched this site in October of 2024 and to commemorate the occasion, I have been building up a lot of cool stuff to share all at the same time. ### Website Overhaul! The website has undergone a complete visual overhaul. I've done a whole bunch of art as well as spent many hours scrolling through 'inspect element' to bring you an experience that is hopefully unique and interesting, instead of looking like a rushed student project. I recreated the main menu screen and changed the navigation in mobile as well. The layout is similar other than those, but visually it's completely different. A lot more chains and roses than before, so please do check it out! ### The Newsletter Works Again Yes! I fixed the newsletter stuff, it had to do with some miscommunication between my bulk email sending service and digital ocean, where I host the site. That is all cleared up so hopefully you're reading this in your inbox! ### New Pixel Art Another part of the website changes was the pixel art page has had a big makeover. Now the pixel art is actually the right resolution, so instead of looking like shit, the art I put up there looks as intended. In celebration of this, I've redone a couple pieces in my newer style, specifically the lion and falcon pieces. Also now you can choose specific zoom values for the art so that you can get closer without compromising the aspect ratio. Check it out! ### New Figures I've been doing a lot of figure art over the past year, but I just hadn't been uploading them. So I took some time and uploaded my backlog, so now there's about seven new pieces of figure art. So if you like those, go check them out! ### New Blogs I've written two new blogs as well, not including this one. The first is about my experience with the Game Makers Toolkit game jam this year and what mistakes I made and what I learned as a team lead. That is already out, so go and read it. The second one is finished, but not uploaded yet. I'm uploading it in a week to drip feed the content a bit, so not to overwhelm people. That one is about the upgrading of my random generator to work with graphs, so that I can do _regional_ generation. I'm quite proud of both blogs and I've been trying to get better at making my technical writing approachable and not dry. So, even if you don't think you're the target demographic for this kind of writing, you might like it more than you think. ### New Game?! Well kind of? I submitted a game to the GMTK 25 game jam this year, so if you're into game jam games, go check it out. It didn't get the greatest reviews, but I patched some issues after the fact, so I think its at least a good way to spend 5-10 minutes. At the very least it'll put the newest blog into context. Here's the link. ### The Future So as you can imagine, it's taken me a while to make all of these changes and to put up all this new content to the site, but I'm not done. I have one more blog idea, I haven't yet written, about how to use Aseprite to create a really easy modular animation system workflow in Unity. Think of game characters that can equip and swap armor then the new armor is animated on top of the character animations, that's what I mean by modular animation system. It's a super common scenario, right? But during the jam, I created this really clean workflow that I haven't seen elsewhere on line, so I've been thinking about putting it on Medium then linking back to here to increase traffic. It'll be probably the most tutorial-esque of any of my posts so far, so it should be an interesting style change. Also I'm continuing my work on my long term game project a more fleshed out version of the game we submitted to GMTK last year. I'm sure that work will give me plenty more to write about. But in short, I'm just going to continue to make more art, code more cool things, then put it all up here on this website. So if you have any interest in anything that I do at all. Please sign up for the mailing list, even if just to boost my ego. Thanks! ## Sign up for Dalichrome Please I beg of you Sign Up Email sent! Check your inbox to complete your signup. No spam. Unsubscribe maybe. There's an unsubscribe button, but don't press it
000
Dalichrome @index.dalichro.me.ap.brid.gy · 19/09/2025
dalichro.me
GMTK 2025 Experience
For the second year running some of my close friends and I decided to come back and try our hand at the Game Maker's Toolkit Game Jam. This is the story of that experience and what we learned in retrospect.
000