Search Unity

Tilengine - Alternative 2D Rendering?

Discussion in '2D' started by RichardKain, Oct 19, 2018.

  1. RichardKain

    RichardKain

    Joined:
    Oct 1, 2012
    Posts:
    1,261
    Now that I have some tutorials actually coming out, I thought I'd post them up here at the top in list-form so anyone clicking on this thread fresh will have easy access.

    Tilengine + Unity: Basic Embedding

    I ran across a new rendering engine a few months ago. Tilengine is a scan-line rendering engine written in C. It's author made it open source a few months ago, the announcement of this was what drew my attention. (as well as a few youtube videos from Gamesfromscratch)

    I had just learned how to program in Python, and I've been hungry for a performance-friendly solution for Raspberry Pi development for years. So I downloaded Tilengine, and started playing around with it. It took a little experimenting, but I got it running, and found it reasonably easy to work with. When I ran into a few limitations, I noticed that it's documentation boasted of being able to embed the rendering engine as I pleased. I put this functionality to the test, and ran a trial to try and integrate Tilengine with Pygame. I would use Pygame for the window management, input, sound handling, etc... and just use Tilengine for the rendering. To my surprise, it worked like a charm. Rigging up Tilengine for this in Python turned out to be relatively easy and simple.

    It was around this time that I started getting other ideas. Tilengine is written in C, which makes it extremely cross-platform porting friendly. It already has builds for several common platforms. It also comes with numerous bindings, allowing it to be used with a plethora of different languages. Python was just one option. And another option is C#. So, mad scientist that I am, I fired up Unity and began trying to integrate Tilengine into Unity, and possibly use it as an alternative 2D rendering solution.

    Unity has capable tools for 2D rendering, tools that improve with every successive iteration. But one area that has always been difficult is retro pixel rendering. Unity is 3D at its core, and all of its 2D tools work on top of that 3D basis. This allows for some impressive visuals, depending on the style you are going for. But it also makes retro-pixel graphics a bit of a headache. Attempting to emulate that look in Unity is possible, but it takes some serious work-arounds and customization. So while I don't think a Tilengine plug-in would ever be extensively used, I do think there would be some value to such a project. Pixel-pushers looking to work with more 90's-style graphics could use it as an alternative to the look of typical Unity 2D rendering.

    I'm still in-process with my experiment, but so far things have been progressing smoothly. I started off thinking I would use a RenderTexture, but that plan quickly redirected to using a Texture2D instance. Tilengine focuses on drawing pixel data to a byte array, and Texture2D just has way better tools for that. I can't say for certain that I'll be able to get this working, but I have been able to import the needed DLLs, reference the Tilengine Engine object, and even load up resources stored in my Assets folder. I'm feeling pretty good about this one.
     
    Last edited: Jan 18, 2019
  2. RichardKain

    RichardKain

    Joined:
    Oct 1, 2012
    Posts:
    1,261
    I've been using the word "alternative," and not "replacement." This is quite intentional. I learned a while ago, while I was learning how to work with Unity, that attempting to circumvent Unity's normal systems is a bad idea. And it's usually not necessary, either. Unity is flexible enough to allow for quite a bit of bending without skirting around it's systems. The reason why I'm able to tinker with this approach in the first place is because Unity allows for the integration of native plugins.

    My idea is to use this alternative rendering technique to render pixels to a Texture2D object, and then apply that Texture2D to native Unity structures in the normal way. You could, say, draw that Texture2D to a viewport. Or apply it to a material, which would then be stretched across a Plane Object. Or perhaps you apply it to an imported mesh object. Maybe you stretch it across a quad, and then use that quad to scale it based on a pixel integer, and use a script to place it in the viewport to insure it's properly centered. I have no interest in attempting to swap out or replace Unity's native rendering. That would be a waste of time. I'm just tinkering with the possibility of integrating an alternative that is much closer to traditional pixel games.

    It's also nice that Tilengine has full support for loading tilesets, animations, and tilemaps that have been defined using Tiled. Tiled is a pretty kick-ass level editor for 2D tile-based games.
     
  3. RichardKain

    RichardKain

    Joined:
    Oct 1, 2012
    Posts:
    1,261
    Nailed it.

    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4. using Tilengine;
    5.  
    6. public class TileHandler : MonoBehaviour {
    7.     public MeshRenderer trialRenderer;
    8.  
    9.     private Texture2D trialTexture;
    10.  
    11.     public static Engine trialEngine;
    12.  
    13.     private byte[] trialBuffer;
    14.  
    15.     [Range(1, 1920)]
    16.     public int trialWidth;
    17.  
    18.     [Range(1, 1920)]
    19.     public int trialHeight;
    20.  
    21.     [Range(1, 256)]
    22.     public int trialLayersNumber;
    23.  
    24.     [Range(0, 256)]
    25.     public int trialSpritesNumber;
    26.  
    27.     [Range(0, 256)]
    28.     public int trialAnimationsNumber;
    29.  
    30.     // Use this for initialization
    31.     void Start () {
    32.         if (trialWidth <= 0) { trialWidth = 1; }
    33.         if (trialHeight <= 0) { trialHeight = 1; }
    34.         if (trialLayersNumber <= 0) { trialLayersNumber = 1; }
    35.  
    36.         trialEngine = Engine.Init(trialWidth, trialHeight, trialLayersNumber, trialSpritesNumber, trialAnimationsNumber);
    37.         trialEngine.LoadPath = "Assets/TrialMap";
    38.  
    39.         trialTexture = new Texture2D(trialWidth, trialHeight, TextureFormat.BGRA32, false, false);
    40.         trialTexture.anisoLevel = 0;
    41.         trialTexture.filterMode = FilterMode.Point;
    42.  
    43.         trialBuffer = trialTexture.GetRawTextureData();
    44.  
    45.         trialEngine.SetRenderTarget(trialBuffer, 4 * trialWidth);
    46.         Tilengine.Color trialBack = new Tilengine.Color(0x1B, 0x00, 0x8B);
    47.         trialEngine.SetBackgroundColor(trialBack);
    48.  
    49.         if (trialRenderer != null) {
    50.             trialRenderer.material.mainTexture = trialTexture;
    51.         }
    52.     }
    53.    
    54.     // Update is called once per frame
    55.     void Update () {
    56.         trialEngine.BeginFrame(Time.frameCount);
    57.         for (int i = 0; i < trialHeight; i++) {
    58.             trialEngine.DrawNextScanline();
    59.         }
    60.         trialTexture.LoadRawTextureData(trialBuffer);
    61.         trialTexture.Apply();
    62.     }
    63. }
    I managed to cook up a quick script that uses Tilengine to render to a Unity Texture2D object. It's not perfect. You can trigger a crash if you try to tweak certain variables between previews. It mainly seems to be related to switching up how many layers or sprites the engine object is initialized for. The resolution can be tweaked between previews, there's no issue there. And when nothing in the engine initialization is changed, there are no errors, so deployment would not be an issue. It wouldn't hurt to play around with it a little more, and possibly add some tools for initializing and de-initializing, but what I've got so far functions.

    As you can see from the brevity of the code, it really wasn't that hard. I could have even done it with even less code, especially if I didn't care about adjusting anything on the fly. One point of note is the second argument for the SetRenderTarget function. This requires a number based on the bit-depth of the Texture2D, and the total width of the Texture2D. Tilengine needs to know how many total bytes it needs to use for a given horizontal line. Basically, take the bit depth of the Texture2D object, divide it by 8, and then multiply it by the horizontal resolution of the Texture2D object. A switch statement might be appropriate for testing the TextureFormat that you choose for the Texture2D. If you don't get this calculation right, it could also cause the Tilengine to crash. (and take Unity's interface with it)
     
  4. RichardKain

    RichardKain

    Joined:
    Oct 1, 2012
    Posts:
    1,261
    Ran into a bit of an issue last night while I was playing around with this experiment. I was trying to add a sprite to the project, and the whole thing kept crashing on me. After some trial-and-error, I came to realize that the problem was the graphic I was using. Tilengine is limited in the graphics it supports. This is by design, as it has a relatively narrow focus for its approach to rendering. I had saved out a 32-bit PNG sprite-sheet. But Tilengine only supports 8-bit PNGs.

    It took some gymnastics in GIMP to figure out how to provide the proper PNG. But I got there. GIMP's interface for exporting indexed images could use a little more refinement. It can be made to do what I needed, but it doesn't exactly spell out the process. Part of this quirk is that you are limited to indexed transparency. There isn't going to be any anti-aliasing on Tilengine sprites. Tilengine supports alpha-adjusted transparency for its sprites, but only after they're in-engine, so get used to hard pixel edges on your sprites.

    Something to think about for anyone looking to give this a try. Make sure the images you import into your project as resources are 8-bit PNGs. Anything else as a source for backgrounds or sprites will make Tilengine throw a fit.
     
  5. Domarius

    Domarius

    Joined:
    Jan 5, 2013
    Posts:
    103
    It's great you can achieve what you need with GIMP, another win for free software :) If you're keen on doing more, I highly recommend Aseprite (yes that's how it's spelt) for working with pixel art, you have a lot of old fashioned control over the palette, but in a modern (yet retro!) graphical interface! You do have to pay for it but it's totally worth it if you're making pixel art games. And it's for Windows, Mac, and Linux, and you can even buy it through Steam. https://www.aseprite.org/
     
    arkogelul likes this.
  6. RichardKain

    RichardKain

    Joined:
    Oct 1, 2012
    Posts:
    1,261
    I finally got around to cooking up a more complete tutorial, and posted it on my website. Here's a link.

    Tilengine + Unity: Basic Embedding

    It walks you through the process of getting Tilengine running in a standard Unity project. It also includes all of the sample files you would need to get the project running on a 64-bit Windows machine. (you'll have to download some additional libraries to get it working on a 32-bit build or a Mac) It is compatible with Unity 5.0 and above. It should run on earlier versions of Unity, but only on the Pro license. (it requires support for native plug-ins) Nothing fancy, but it covers most of the basics.
     
  7. hlw

    hlw

    Joined:
    Aug 12, 2017
    Posts:
    250
    Can you explain to a 5 years old child what this is and what you are able to do thanks to it ?(Well, maybe a 12 Yo kid)
    It will make unity search engine and google users find your topic easily, and let poeple like me understand if it is something they should use.

    (I'm actually using Tilesheets filled with 8x8 pixels and 16 colors palette (one for transparancy) tiles, making tile tables by creating 16x16 sprites using 4 flipped or not tiles).
    To use it inside unity I screenshot my tilesheet, open it in a software like gimp, supress the pixels that are supposed to be transparant to make it a png transparancy.
    Then I import it inside unity, and slice it in 8x8.
    Create a tile palette in unity, and create my sprites one by one by dragging in the tiles that compose it (and flip it if necessary).
    Then i use it to create my unity tilemap.
    Once done, i run into the common problems of pixel perfect rendering everybody know, and don't remember actually how i overcame these.

    All these steps are a real pain, took my around 8 hours to create one tile palette, and i got around 20 others palettes to create.

    What would be the steps with your way of doing it? Which one of them have to be done outside unity ? Is there anything i won't be able to do anymore if i use it?
     
  8. RichardKain

    RichardKain

    Joined:
    Oct 1, 2012
    Posts:
    1,261
    Sadly, there is quite a bit that you wouldn't be able to do using it, at least for the moment. Tilengine is an external rendering engine. It uses a scan-line approach with rendering similar to old game consoles. As such, it is not designed to work with any of Unity's internal structures. So defining a sprite or spritesheet in Unity will not make that sprite usable in Tilengine. Ditto for Unity tilemaps. Sprites, tilemaps, and tilesets would all have to be designed and edited outside of Unity. The program most often used for cooking up Tilengine tilemaps is called Tiled.

    Tilengine is very good at low-level rendering of pixel graphics, and that's about it. This is part of why I'm so interested in embedding it into Unity. Unity has all sorts of great tools for audio, animation, networking, etc... It's just a little weak when it comes to low-level pixel rendering. Embedding Tilengine into it gives me the best of both worlds. If you are looking for old-school pixel graphics, Tilengine is worth a look. If not, then don't bother, and just go with Unity's default solution, or another engine with a similar focus.

    One nice thing is that the data Tilengine uses for its structures is relatively simple, and all of it can be programmed. I'm looking into the possibility of translating Unity spritesets and tilemaps so that they can be used in Tilengine. It would just take a little bit of coding work to parse the one into the other.
     
  9. Lars-Steenhoff

    Lars-Steenhoff

    Joined:
    Aug 7, 2007
    Posts:
    3,527
    There is also an engine in the asset store that is made for 2d games, will have to find the name
     
  10. hlw

    hlw

    Joined:
    Aug 12, 2017
    Posts:
    250
    So basically you need to create on top of that, all the editor functions and tools to be able to edit and design things inside unity while still being compatible with Tilengine, to be able to edit the tilemap inside unity and make it appear in the scene window?

    I'm not sure how it works, but would we still be able to access tiles data and modify it during runtime ?
    Would we still be able to render in the scene something else something that isn't part of tilengine like a 3D object?
     
  11. RichardKain

    RichardKain

    Joined:
    Oct 1, 2012
    Posts:
    1,261
    Yes. In fact, that is one of the strengths of Tilengine. You can dynamically program and edit assets in Tilengine at run time. You can change tilemaps, sprites, color palettes, all manner of things. And you can generate any of those things through code as well.

    It partially depends on how you are using Tilengine, but yes. The example tutorial I posted shows you how to stretch your rendered Texture2D across a Quad. But you can do whatever you want with the Texture2D that gets generated. And Tilengine doesn't supplant Unity's rendering system. You can still render whatever 3D objects, 2D sprites, scenes, whatever standard features Unity has. I was planning on using Tilengine for the 2D pixel art, and possibly using Unity for the menus/UI. (an area where Tilengine is quite weak, and Unity is very strong)
     
  12. hlw

    hlw

    Joined:
    Aug 12, 2017
    Posts:
    250
    If you ever release something that is relatively easy to use inside unity's inspector/editor in the asset store, i'll take it for sure. Right now, i'm not sure i'm capable of integrating it myself.(I'm definitely sure i can't actually).
    (I want to release an old school 2d platformer thingy in the asset store, that would be great if i could include Tilengine in it)
     
  13. RichardKain

    RichardKain

    Joined:
    Oct 1, 2012
    Posts:
    1,261
    Heh heh, you caught that little snippet, huh? Yeah, it's one of my favorite features too, and it works just like you think it might. Color palettes are going to be one of the next tutorials I work on, I already have a sample project ready. In Tilengine, the palette-swapped sprites of yesteryear are as easy as defining a list of colors. It's even possible to create animated sequences for these color-palette changes. As snazzy as modern graphics are, palette swapping is a feature that we lost in the shift to 3D graphics. Being able to swap those pixel colors is a powerful tool.
     
  14. hlw

    hlw

    Joined:
    Aug 12, 2017
    Posts:
    250
    I'm just wondering about performances. I want to believe that rendering tiles that have a low amount of colors (like 16) would take way less memory and computing than rendering RGB32 textures even if those only have 16 actual real colors, but is this really the case here ?
     
  15. RichardKain

    RichardKain

    Joined:
    Oct 1, 2012
    Posts:
    1,261
    Tilengine is using the BGRA32 TextureFormat. But it is not drawing 32-bit graphics. It specifically draws 8-bit graphics, and it will only load in graphical assets stored as 8-bit PNGs. It's color limit for its palettes is 256, not 16. It doesn't technically have an upward limit on its color support, it simply limits the drawing of any given sprite or tilemap to 256 colors.

    As to performance, I haven't stressed tested it yet, but the performance should be quite high. Tilengine is written in C, and uses a lot of low-level tricks to insure that it draws very quickly. It ought to perform quite well, even on modest hardware. The original developer demonstrates that Tilengine is able to run at full-screen at a steady 60 fps on a Raspberry Pi. Part of the reason for this performance is that it is restricted to a somewhat outdated rendering model. I'm not certain how the Unity overhead might effect performance, but I imagine it wouldn't be much of a problem.
     
    foxnne likes this.
  16. hlw

    hlw

    Joined:
    Aug 12, 2017
    Posts:
    250
    I guess i must give it a try. I've got a lot of characters in my project that work using palette swapping and this seems to be a perfect solution for this, i did read somewhere i could achieve to render this type of behaviour using some custom shaders, but i feel like trying to read chinese when i try to understand how that works.

    On top of that, i saw in your tutorial that it is compatilbe with Unity 5, I guess if Tilengine performs well, i could grab Unity 5.2.4 and make my project compatible with windows XP, which is a really good point for a great part of the ones that'll use it.
     
  17. Lars-Steenhoff

    Lars-Steenhoff

    Joined:
    Aug 7, 2007
    Posts:
    3,527
  18. hlw

    hlw

    Joined:
    Aug 12, 2017
    Posts:
    250
    Well, retroblit (FES) is a no-go for me, i need to be able to redistribute my assets for free. And from what i can see, it is really far from user friendly, and include a big pack of features i don't need at all.
    It seems to be a good asset tho.
     
  19. RichardKain

    RichardKain

    Joined:
    Oct 1, 2012
    Posts:
    1,261
    Hard at work on tutorial #2 and tutorial #3. Originally they were going to be one and the same, but I decided the subjects warranted their own pages. Tutorial #2 is going to be about how to structure a project to separate code out onto different objects. (while still dealing with the static core Engine object) Tutorial #3 is going to be about using the Tilemap structure to render text dynamically. Fun stuff.
     
  20. RichardKain

    RichardKain

    Joined:
    Oct 1, 2012
    Posts:
    1,261
    After exploring possible structural solutions for the second tutorial I was working on, I ran into some integration issues, and went back to the beginning to make sure that the underlying base was solid.

    While this refactoring ended up creating more work for myself, it was ultimately worth-while. A native plug-in like Tilengine features unmanaged memory issues. When integrating it into a garbage-collecting system like C#, it is important to take those issues into account.

    The full-on structural approach I was planning will have to wait. But in its place, my next tutorial will feature a low-level base that uses an IDisposable class to wrap and manage the creation and destruction of Tilengine instances. It will also feature its own custom DLL references, as opposed to relying on the existing Tilengine C# wrapper project. This means that the overall project will be much less comprehensive, only featuring the bare minimum of starting up a Tilengine instance and using it to update a standard pixel array. But it will provide a good a functional example of creating an instance, and cleaning up that instance automatically using the garbage collector. (to effectively eliminate memory leaks and avoid any crashing within the Unity editor) So far, this approach seems to be working very well, and expands the possibilities of what can be done with Tilengine within Unity. (multiple instances running at the same time)

    I was feeling so good about the progress I made, that I went ahead and tried embedding Tilengine into Monogame. And it worked! I was able to use a very similar approach, with only a few slight changes to take the particulars of Monogame into account. I should be posting both of those tutorials along with downloadable projects later in the week.
     
  21. RichardKain

    RichardKain

    Joined:
    Oct 1, 2012
    Posts:
    1,261
    Finally got around to posting that second tutorial.

    Embedding Tilengine in Unity: Memory Management

    I'm pretty pleased with how it turned out. Could be a useful guide for anyone else hoping to tie an un-managed native Plugin into Unity