Tilengine Mapping

I’ve recently been working on a few tutorials related to the Tilengine rendering library. My primary engine of choice is Unity 3D. But I was pleased with the results I was getting, so I decided to install Monogame, and give that a go as well. Over the last weekend, I was able to successfully port the tutorial I was working on to Monogame.

I’ll be posting the tutorial in a few days, along with zip of the sample project. It’s going to be extremely bare-bones, but will provide a working example of Tilengine embedded into Monogame.

At first, I was hesitant to even bother porting it over, as Monogame already has a fairly decent 2D drawing system. A lot of modern engines don’t bother with 2D pixel graphics, preferring instead to rely on a 3D rendering back-end. But the Sprite Manager in XNA and now in Monogame is relatively capable when it comes to supporting and scaling pure 2D pixel graphics. I originally sought to embed Tilengine in Unity because Unity’s 2D pixel support is considerably more lacking.

But when I was poking through these boards I noticed that there does seem to be some desire for a decent color palette solution for 2D low-res sprite-based games. Monogame’s Sprite Manager is solid for pixel-focused drawing, but it still requires custom shaders in order to support color palettes. (and even then not well) Being able to control and render dynamic color palettes is one of the big strengths of Tilengine.

I’m not sure how much actual interest there will be, but I’m planning on maintaining a version of my Tilengine C# mapper targeted at Monogame.

Hi @Will_Hawthorne, in the XNA/MonoGame world, a lot of 2D games leverage SpriteBatch. It’s rendering API which is a (priority) queue of quadrilaterals which gets filled every frame and then discarded. That’s it, nothing fancy, but it seems to be that the idea of the painter’s algorithm is intuitive and straightforward for developers to get behind. However, in the case of drawing large amount of sprites such as a Tiled map, there are some flaws with the beloved SpriteBatch. You might be interested in MonoGame.Extended’s implementation of rendering tiled maps for inspiration. There is still room for improvement as a lot of developers wish to modify the Tiled map in-game while keeping the performance boost of rendering the map as a 3D planar “mesh”. Also, there is no census on what’s the best technique to “sandwich” game object rendering between Tiled planar layers for a game. Again, most developers seem to grasp the painter’s algorithm again here, but there are other techniques out there.

Tilengine is a potential alternative to the traditional SpriteBatch rendering approach. Tilengine is a low-level C library that uses a very, VERY old-school approach to rendering. It is essentially a scan-line renderer that uses techniques that are similar to how classic consoles processed graphics. Because of this, it shares many of the same limitations as these older systems, but also provides some similar strengths and unique techniques.

Tilengine does not use the GPU, as far as I know. It only uses the CPU, and features no 3D rendering. This obviously limits some of its rendering power. But it also makes sense, given what it does. (all pixels and integers, no polygons or floats) Because it uses legacy rendering techniques and extremely low-level processing, it is able to get some impressive performance on modern systems despite being limited to the CPU. But it is definitely more limited than most other modern rendering engines.

The advantage of using Tilengine is that it plays really, REALLY well with the Tiled map editor. It can load resources from that editor natively, and can display maps created in Tiled with no issues. Tilengine’s approach to rendering also gives the user access to legacy rendering techniques that are no longer common or compatible with modern hardware. Most prominent among these is its support for color palettes, both assignment and full-on animation.

Part of my desire for adapting Tilengine for C# and embedding it into other engines is to combine its advantages with those of more modern game development platforms. For low-resolution sprite-based games, a rendering system like Tilengine is quite keen. But it would be even better to be able to combine such a system with the power and flexibility of modern rendering. The example I created essentially generates a standard Monogame Texture2D object containing the pixel data that Tilengine creates. Once you have that Texture2D, you can do whatever you want with it. Use the SpriteBatch to render it to the screen in 2D. Stretch it across a 3D model. Overlay a high-definition user interface on top of it. I want to be able to provide the option, and do so in a way that does not limit the user.

The advantage of using Tilengine is that it plays really, REALLY well with the Tiled map editor. It can load resources from that editor natively, and can display maps created in Tiled with no issues.

We already have other renderers that work well with Tiled with no or limited problems.

Most prominent among these is its support for color palettes, both assignment and full-on animation.

For modern hardware, palette swapping can be achieved using fragment shaders.

For low-resolution sprite-based games, a rendering system like Tilengine is quite keen.

You can easily do “pixel” type effect with textures if you use point sampling.

The example I created essentially generates a standard Monogame Texture2D object containing the pixel data that Tilengine creates.

Texture2D is a GPU resource. Unfortatunely there just isn’t that much memory available to keep layers of a Tiled map in memory as a Texture2D for large maps. Also, if developers want to change the map at runtime, they will have to invalidate the Texture2D. Since you say your technique uses the CPU to generate the bitmap and then upload it to the GPU, I suspect there will be performance bottleneck with memory transfer. But who knows, some hardware share memory between CPU and GPU these days; it would need to be profiled. Also, GPUs are used today to munch on raw data in parallel and have become practical beyond just rendering; so why not use them?

The project sounds fun for academic purposes but I’m skeptic it will be practical for people to use. Maybe that’s not your goal? I don’t know. Anyways I would be interested to see your benchmarks compared to other tried techniques.

Tilengine

It’s not “my” technique. Tilengine is an open-source rendering library that was programmed by someone else. I stumbled across it on-line about a year ago when it was first released under an open-source license. It had Python binding, and could be compiled for the Raspberry Pi, so I downloaded it and tried it out a little bit. I had been interested in finding a game engine that could run natively on a Pi, and there were precious few options. (the Pi’s older GPU makes it incompatible with most modern engines)

I decided not to adopt Tilengine, as it was focused almost entirely on rendering had little or no support for other features, like audio and GUI. But I did rather like how it handled graphics. And the restrictions it puts in place actively force a “retro” approach. Then I started poking around its C# binding project, and realized that it might be possible to embed it in other game engines, as an alternative rendering option.

I think you might not be understanding how Tilengine works. Yes, Texture2D is a GPU resource. But with Tilengine, you don’t have to render Tilemaps to a Texture2D. You render the visible screen, to a single Texture2D, that is whatever pixel resolution you chose. The contents of the scene, such as multiple large tilemaps, never get converted to Texture2D objects. They can be as big or as complex as you want, but all that actually gets “rendered” is whatever portion of them is currently in the defined viewport. And because the rendering is pixel/integer based, it happens quickly, and only for the dimensions of the defined instance of Tilengine. You only technically need a single Texture2D, with a single resolution. I’ve been targeting 320 x 180, to make it nicely scale-able for modern displays. (although 640 x 360 also works well)

It’s also worth pointing out again that I have no intention of completely supplanting standard Monogame rendering. As I pointed out, this approach isn’t for everyone, and is even better when paired and used alongside more modern techniques. There are certain functions that this legacy approach doesn’t excel at, and where modern rendering would be preferable. Text rendering is one area in particular where it would help to overlay with a more modern approach.

Actually im not sure pal is even supported on any modern cards everything is col so there is really no benifit to it other then for storing it for some custom compression scheme so you are just fakeing it but for no real benifit.

I still have some were on my hard drive a 90’s styled ps1 .Shp compression decompression algorithm (pal based) i wrote and a dat that has over 10,000 images in a 100 mb or kb file cant remember.

Ah, nice! I apologize, I thought you were introducing this as your own work.

with Tilengine, you don’t have to render Tilemaps to a Texture2D. You render the visible screen, to a single Texture2D, that is whatever pixel resolution you chose

That’s what RenderTarget2D is for. You can render whatever you want to an immediate GPU memory buffer instead of the framebuffer. In MonoGame/XNA a RenderTarget2D is a Texture2D. Most 2D games I have seen use render targets to scale up the rendering for UI purposes. This technique works especially well if you want that pixelated look by using point clamp filtering as mentioned earlier.

all that actually gets “rendered” is whatever portion of them is currently in the defined viewport.

In essence that’s exactly how people use SpriteBatch to render large Tiled maps today. It’s still somewhat slow though as the CPU has to churn through the right portion of the tile data (cache coherency nightmare) and then create the quadaretals and then sync that data to the GPU every frame. We all know that games have very tight time budgets for rendering frames. That’s why I wrote the alternative method of generating all the geometry for the tilemap and saving that to a GPU buffer (not every frame) to not waste CPU cycles. Then the CPU just tells the GPU to render a specific buffer of geometry in VRAM for the tilemap every frame.

And because the rendering is pixel/integer based, it happens quickly

Technically anyone can change the data types being used for the render geometry from float to integers. That actually might be a good idea for optimizing the technique I wrote early but I didn’t really care about it since using 32-bit or 16-bit floats are pretty fast already especially when writing 2D games.

It’s also worth pointing out again that I have no intention of completely supplanting standard Monogame rendering. As I pointed out, this approach isn’t for everyone, and is even better when paired and used alongside more modern techniques. There are certain functions that this legacy approach doesn’t excel at, and where modern rendering would be preferable. Text rendering is one area in particular where it would help to overlay with a more modern approach.

Well fair enough. However, I wouldn’t recommend it since all the features or gains can be done using an alternative more modern technique. SpriteBatch is an awesome choice by the way for rendering text glyphs. I spent quite a bit of effort with getting BitmapFont not to generate garbage when rendering/measuring.

No, as I linked to, this is an existing project. I have no interest in altering the original source code. I’m an experienced C# programmer, but have very little experience with C. I believe that I would be able to compile the Tilengine source code for different platforms, but there’s no chance of me making significant alterations.

What I’m interested in is making a more fleshed-out C# binding, that makes it easier to embed and use Tilengine in modern engines, and approach development in Tilengine in a more object-oriented approach, consistent with typical development in C#. The C# binding created by the original developer seems to be a little on the automated side. And all it really does is translate the original code into C# structs. In order to use that binding, you basically have to code the same way you did in C, only in C# instead. That C# binding also treats the engine instance as a static singleton, preventing you from using multiple instances in the same application.

Trying to cook up a “true” retro 2D solution can be a challenge in modern systems. You have to specifically tweak your mip-maps and point-clamping. You have to use a RenderTarget for post-process scaling. You have to cook up multiple custom shaders in order to implement a limited form of color palette support. You have to worry about polygons shifting to sub-pixel values. With Tilengine, all of that is just part of the system. It’s the default approach. No extra effort, tweaking, or coding is needed. Every displayable graphical element in Tilengine has a color palette, and can have that color palette altered freely. It’s not something special that you have to implement, it’s built into the core engine. Tiled tilemap support doesn’t require a separate library or plug-in, and you don’t have to worry about performance, or map size, or how many quads or vertex buffers are going to be fed into the GPU. All that is built-in, and can be loaded with a single line of code.

Is it for everyone? Clearly not. There’s tons that you CAN’T do, and probably shouldn’t try to do in Tilengine. Tilengine does 2D scan-line rendering ONLY. While you can simulate Mode 7 in Tilengine, true 3D rendering is a waste of time. It is very much geared toward classic, 2D, tile-based sprite games. If that’s your jam, it’s worth taking a look at. But given the number of popular and capable 2D, retro sprite-based games that I’ve seen produced using Monogame, I thought that perhaps someone around this community might be interested in such a project. It could be that I was wrong. My initial push for this porting project was for Unity, anyway, and that effort is still moving forward. I can probably save some time by not bothering to make it available for Monogame as well.