ECS Roguelike

(This project is based on the excellent Rust Roguelike Tutorial by TheBracket)

I’ve restarted my roguelike from scratch with a somewhat better understanding of how the pieces should fit together - current progress:

5430195--773437--y1LZf3BvCc.gif

Full source with the MIT license is up at GitHub - sarkahn/dots-roguelike: A "traditional" ascii roguelike built using Unity's ECS/DOTS framework.. Contributions are welcome!

Original Post

Hello. For the past few weeks I’ve been working on a framework for making a roguelike in Unity. Alongside it I’ve been making a tutorial for creating a roguelike using my framework and Unity’s ECS system.

I figure the tutorial should be of interest to anyone who wants to see how you can create an actual working game in ECS. It starts simple and each part covers a new concept, from how to manage the camera and rendering, to how to represent shared map data in ECS.

The readme for each chapter is not a a “step-by-step” tutorial that really explains ECS - I was originally going that route and it turned out to be WAY too much work and ultimately just distracted from the ultimate point. I want to show how to make a game using ECS, not to explain how ECS works.

Instead each chapter will give a brief overview of how the systems work for that example. At the start of chapter 1.1 I provide plenty of resources for documentation and tutorials where you can learn all about how ECS actually works as you follow along.

I’ll keep this thread updated as I add new parts. This is my first time taking a serious crack at something like this, and I’m learning as I go along too, so I’d really appreciate any feedback if people are interested in something like this. Thanks!

Introduction

1.0 - Writing to the Console: A simple example of creating and writing to a console. Nothing really ECS related.

1.1 - ECS: Provides a brief introduction to ECS with plenty of references to learn more. Demonstrates how to use the Conversion System to create some entities, and how to create the console from code and use it to render your entities. It also shows how to handle player input to move the character around.

1.2 - Walking a Map: Generates a map for the player to walk around in via a “Reactive” map generator system. Covers how to handle sharing a simple representation of map data between systems, and goes over how the generator works.

1.3 - A More Interesting Map: Makes some tweaks to the map generator to create the traditional “rooms and tunnels” style map.

1.4 - Field of View, Interfaces, and Reactive Systems: Like the title says it covers how to create a “Field of View” system that remembers parts of the map we’ve already seen, and briefly covers how to use interfaces with jobs and burst and the benefits of “Reactive” systems.

1.5 - Monsters and Refactoring: Refactors the code from the previous chapters so our systems can apply to more than just the player entity, then adds some monsters during map generation.

1.5A - Taking a Turn: Goes over my implementation of a turn-based system in Unity’s ECS, and introduces the beginnings of a combat system.

24 Likes

Reserved

1 Like

Hey, nice work! I also tried to implement roguelilke on ECS, you can check it here: GitHub - Macoron/RoguelikeECS: Example project. Using Unity ECS to create some simple Roguelike game
I ended up using hybrid ECS (easier to render tilemap). Also map represented by a native array of entities. It gives you proper way to attach new components directly on a tile (like wall component or trap component).

Hope you find something interested for you.

2 Likes

Hello! I personally would avoid the entity-per-tile approach. While it might seem intuitive at first I feel it runs counter to how the data will actually be used. Most of the time tiles will be identical to any other tile of the same type, so it makes more sense (in my case) to just store tile types and handle per instance data elsewhere.

It should be fine for smaller maps but it could cause memory and performance issues as the map scales up - especially with the hybrid renderer which is horribly slow right now.

Not to say your approach is wrong though, as always theres a million ways to do it. Thanks for sharing!

1 Like

He said he used it for rendering… So there’s no hybrid renderer involved. And I did the same thing as him a while ago for my copy of ‘They Are Billions’. I used a tilemap to render a 256x256 map which easily scaled. Tilemaps are by far the fastest way to render a huge map of… tiles. I even overlapped a dozen of them and it cost almost nothing either on the gpu or cpu. However, I also did not assign an entity to every single tile, merely only to the tiles that actually had a purpose other than rendering, which in my case, were about 80% of them. So unless you do work over every single one of your tiles all the time, you won’t run into any performance issues or ‘memory’ problems.

Of course the Tilemap has its own limits, which is why I had to do some overlapping in my case. Another problem I had was that on a huge 1kx1k tilemap, the editor was starting to slow down a lot, but only when editing the tilemap using the Unity editor tools. Other than those inconveniences, Tilemap is the way to go if you’re serious about making a performant roguelike.

Since you said you’re learning as you go, you should at least give this method, and probably others that will pop up on this thread, a try instead of dismissing it outright based on inexperienced assumptions. Especially when you’re at the beginning.

@Radu392 Hello!

You’re right, I assumed when they mentioned hybrid ECS with regards to rendering they were talking about the hybrid renderer, that’s my mistake. I can see looking at the code they are using Unity’s tilemap to represent their map and injecting the tiles for use in ECS.

I wasn’t talking about the Tilemap in my post, like I said I had no idea they were using the Tilemap. I do agree the Tilemap is a great solution for many use cases. I personally would never use it for a roguelike since I’m not a fan of the asset creation workflow and I don’t need a map editor. It’s also too restrictive on what data you can pass to the shader - if I remember right you can only pass a single color. While it’s possible to embed a lot of data in one color, it ultimately just doesn’t fit for how I want to represent my map data.

And for the record Tilemaps are most definitely NOT the fastest way to render a huge map of tiles. I guarantee you Graphics.DrawMeshInstancedIndirect will perform vastly better and give you much more control and flexibility with how you represent your tile data. But again, not a knocking Tilemaps in any way, they’re a great tool.

That’s exactly what the hybrid renderer does. And badly. And again, I understand now they aren’t using the hybrid renderer, but that is why I said that in the first place.

I respect your opinion but I disagree. They aren’t compatible with jobs in any way, forcing you to mirror your data as entities. They are wonderful if you need a map editor and you only want to run on the main thread, but in my opinion if you’re “serious” about making a roguelike you’re much better off writing your own rendering solution. Which is exactly what I did.

I was not intending to “dismiss” anything. If my post came off that way, I sincerely apologize. I was simply trying to share my point of view and how I would do things differently - since it was based off a mistaken assumption in the first place perhaps that made it come off even worse.

I appreciate the feedback (seriously), but it seems like you’re making a lot of assumptions of your own. The rendering solution I wrote that is being used in my tutorials was specifically made for rendering ascii in Unity. It works well with the job system, is much simpler to set up than a Tilemap, and is very fast. While I’m always learning, I wouldn’t really describe myself as being “at the beginning”. Either way, again, I appreciate your input.

Apologies to @Macoron for the mistake regarding the Hybrid renderer.

1 Like

Wow, wow, easier guys. Like @Sarkahn_1 said, there a thousand of ways to implement this.

For instance, in my approach there are big drawback in how ECS manage adding/removing components to tile. If I understand correctly, when you add new component to the Entity, Unity reallocate it in new archetype chunk. So if a big part of map changes for some reasons, it will be a lot of copy/pasting and memory will be scatter again.

In buffer approach you just change array value and that’s it.

Another interesting topic is how to implement actions (movement, attack, spell, etc). Because you can’t use inheritance in ECS, you can’t implement Action pattern on a Component level.
I ended up creating a new Entity for each intent like moving. The big advantage is that you can add modifiers to your intent entity (like DoubleSpeedComponent). But it’s add more complexity to your code.

2 Likes

I’ve finished part 1.3 - Makes some tweaks to the map generator to make a simple version of the good old “rooms and tunnels” style map:

At some point I’d like to implement some analogue of the command pattern like that, but yeah it seems like the usual problem of trying to model an oop concept in ecs. It might be just be a better fit to do do the action system it in oop, I’ll have to think about that.

I’ve finished chapter 1.4. It covers how to implement a “Field of View” system that handles remembering tiles of the map we’ve already seen, with a brief aside on using interfaces in Burst and the benefits of “Reactive” systems:

5446704--555591--qb01lAOYLo.gif

11 Likes

Is it possible to use custom sprites instead of ASCII art?

Technically you could just drop in a new texture on the console material and it would use that instead, but it’s not built with that in mind. And you would essentially have to match up all the sprites on the texture to what the renderer expects, which is a typical code page 437 font atlas texture.

Atm it only supports square fonts as well but I do plan to add support for non-square fonts.

I’ve completed part 1.5 - Monsters and Refactoring. I found that as I was adding monster entities I needed to refactor some of my old systems - which previously only worked on the player - to instead be more generalized so they could apply to monster entities where it makes sense.

5467725--559023--RXMk7fWlVM.gif

Next up I need to add a turn based system so my entities act in the correct order. I had no idea how you would create a turn based game in ECS so I created a complete separate prototype to test the idea. It turned out well, now I just need to figure out how to integrate that concept in my roguelike.

6 Likes

You are amazing. Will you be fleshing out the documentation for the turn-based prototype? So happy to see a turn-based ECS implementation.

1 Like

Hahah, thanks! Yeah I plan to eventually flesh it out a bit more. Right now I’m still working out how I’m going to handle turns for the next roguelike part. I’m doing it a bit differently than I did in the prototype but I think it’s more fitting for a roguelike.

I’ve set up a typical “energy system” like in Dwarf Fortress where all actor speed gets added to their energy in a loop until someone has enough energy to act. Its promising so far, just need to flesh it out a bit more then simplify it enough to be tutorializable.

1 Like

Oh wow, I’ve come across your RLTK for Unity repository this week and I didn’t realise you were also working through the same tutorial I’ve been doing. I’ve also been following the tutorial for the last few weeks and I’ve made some good progress. I was busy last night adding a better “turn structure” that will also allow my Player & Monsters to be able to attack one another. There’s a lot of code that I’m introducing now that will very likely change the direction I’ll be taking this in, but I’m very excited for what I’ve been able to achieve. Admittedly I am lazy and using a Tilemap for rendering, but it was more a choice supported by wanting to get up and running as soon as possible.

I’m hoping I’ll be able to finish damage tonight, but I also know that I might be delving a little too deep into pointers for some of my solutions. I’m amazed at how alike our two projects still look, but I’m sure we’ll be going in very different directions.I’m aiming to at least get to showing basic UI by the time 7DRL rolls by, but I’m hoping to at least have some more complex map generation done as well. Best of luck with the project! If you’re interested in seeing my horrible attempt at this feel free to browse the link below. It’s not nearly as well documented as yours, but if you want to read code you’re more than welcome to do so!

2 Likes

Hahah, I figured there had to be a few more people out there working on roguelikes in Unity now that we have a better alternative to the game object model.

I’m curious if you think you’ll continue using the tilemap as you move forward? For me the asset creation is just too annoying and I want to be able to switch to project tiny as soon as possible once it has better support for dynamic meshes. Though I imagine they’ll have their own tilemap at some point too.

Also looking forward to seeing how you end up handling turns and combat. It’s an interesting problem since it pretty much defines how every action in your game will be performed.

Good luck with 7DRL!

I’m most likely going to drop using Tilemap in the future. It currently serves the purposes of what I need and performance is decent enough that I won’t consider it the bottleneck just yet. Regarding asset creation I do all of that programmatically. I have a font spritesheet that I’ve sliced up in such a way that using the tilemap requires very little upfront effort. You have mentioned that the shader support is very limited, but the way I got around that was to just have multiple tilemap layers, one for the foreground and one for the background. There’s also a tilemap for the map itself, but I’m considering just removing it and merging everything into one.

If you’re curious my RenderWorldSystem linked below contains all of my rendering code.

https://github.com/LazyGameDevZA/RogueDOTS/blob/master/Packages/za.co.lazygamedev.rogue-dots/LazyGameDevZA.RogueDOTS.Terminal.Rendering/RenderWorldSystem.cs

1 Like

How are things, @Sarkahn_1 ?

Hey, thanks for the interest! Been very busy at my day job the past couple weeks so I haven’t had a lot of time/energy to work on this. I just finished the third (and I think final) iteration of the turn/energy system. Aside from my work it took a while because it turned out to not really fit “nicely” into the typical ECS workflow, so the code is a bit awkward but the results are good. I made a post about it here:

https://discussions.unity.com/t/778247

I was concerned with how backwards the whole thing felt but after getting some feedback from people who are a lot smarter than I am it feels like what I ended up with is more or less on the right track. I just need to clean things up a bit and write the actual tutorial. May be a bit yet but it’s coming.

1 Like