Posts: 136
Threads: 26
Joined: Aug 2017
Reputation:
5
Hey Megamarc!
It's been a few years since I was championing Tilengine, and wishing I had a game idea that would need it.
Sadly, now that I am working on a 2D pixel art game, I decided not to use Tilengine! I've been thinking about it a lot, it's such a shame because I really liked the idea behind Tilengine. I thought I would describe the path I took, and get your thoughts on where Tilengine sits in all this.
I think the first reason I didn't start with Tilengine for my retro projects, is the most beaten path to getting Tilengine to run (that I know of) is to run it on top of SDL. However, I went with Unity for the cross platform support, that SDL doesn't have, even though it pained me to know I was using a 3D engine to draw 2D pixel graphics, and this was very evident in my frame rate testing on very low end hardware, compared to more true 2D engines like Godot. But it didn't matter - as long as it ran well enough, my ultimate goal was to be able to easily port to consoles like the Nintendo Switch, and Unity was the best at that, at the time.
Then I came across Kha (I think from the GamesFromScratch YouTube channel), and I decided to give it a solid go, because it rivals Unity for cross platform support, and it's a more "truer" 2D engine. Even better, it's 2D engine is designed to make the best use of modern GPUs. Each "drawImage" or "drawSubImage" function call actually sends a quad of 4 texture mapped verts to the GPU. Sequential draw calls using the same texture will be batched, to reduce overhead. And you can get more involved in this pipeline if you want, and change the way it works. It's meant to be used this way because it's more of a framework than an engine - a modern replacement for SDL. You can make other 2D or 3D engines with it, and it will always be using the GPU.
I then quickly learned shaders to implement my own paletted images, so now my sprite atlases have associated palettes, and each sprite instance (or rather, each drawImage call) has it's own palette index that it can use, meaning each sprite instance can have a different palette, but still use the same texture, and still be batched into the same draw call.
And I've already planned out how I'll do some per-layer scanline effects, by rendering the layer to a pixel buffer and sending that to the GPU where the shader will offset each row of pixels.
And then I finally realised, I was better off learning shaders to emulate old hardware effects. The experience is more relevant to the way modern hardware works, and everything has a GPU now, and the GPU seems like the right place to spend resources emulating old video game effects, leaving the CPU to actually run the game itself.
Although if we're talking about making games that look like SNES or Megadrive games, the extra effort is probably pretty minimal whether it's on the CPU or GPU. And it is likely very possible to implement Tilengine into Kha, and have it just draw a texture every frame. I guess it comes down to me feeling better about using Kha's GPU based drawing commands and getting involved and writing shaders to be part of that draw process, and knowing I'm learning relevant skills while making the best use of the actual hardware the game is running on.
Oh I also recently made a video on Kha over Unity or SDL https://www.youtube.com/watch?v=5A8v7dbkPRg
So yeah, after all the fantasizing I did about making a game using Tilengine, I felt like I needed to come back here and talk about what ended up happening, and see if you had any thoughts on all this.
Posts: 673
Threads: 33
Joined: Jan 1970
Reputation:
13
Hi Domarius!
Long time no see, congratulations for learning new tech with shaders!
There's no absolute "best tool" to do something, multiple factors matter. So any framework you choose that satisfies your requirements will be good.
Why doesn't Tilengine use modern shaders? My vision with Tilengine is replicating the inner working and pipeline of actual 2D chipsets -albeit heavily improved-, not emulating the end result:
- 2D chipsets aren't programable, but configurable -you don't draw, you set attributes-
- Pixel art is about low resolutions, so high fillrate of modern GPUs isn't used
- A shader implementation of Tilengine pipeline would require "ext_shader4" feature of OpenGL 3.0 or newer. When I started Tilengine, many integrated GPUs nor mobile phones had this feature, but could run a CPU implementation at full speed.
I've seen many "retro" 2D games developed in Unity and Game Maker Studio that have a wrong concept of pixel. Authors end up mixing assets of different "pixel sizes" on a Full HD framebuffer, having for example 4x4 and 5x5 macro-pixels, moving at 1 pixel granularity of the final framebuffer. This is totally wrong, producing "unaligned pixels" that ruin any attempt of using a post-processing CRT filter that require a low resolution source. A pixel doesn't have size, it is not a square but a dimensionless point. And I've seen those faults in several commercial games. You must be careful with this. Tilengine does this right, as only the final framebuffer gets upscaled with all pixels having always the same size and alignment.
Yes, linescroll can be emulated by displacing pixels in a pixel buffer as you say, and have color palettes by using integer texture lookups. But raster effects are much more than this. You can do color shifts, vertical scaling, layer and sprite multiplexing...
But shaders are cool! I did some research some years ago and I really like their flexibility, especially after introducing unfiltered integer lookups and arithmetic. But they weren't created having retro 2D graphics in mind, you can use them for this purpose but having important concepts clear so the end result doesn't get screwed.
I didn't know about Kha you mention, I'll take a look to guess where it fits and what does it offer.
Good luck with your new game! Let us know how does it progress.
Posts: 136
Threads: 26
Joined: Aug 2017
Reputation:
5
07-23-2021, 07:42 AM
(This post was last modified: 07-23-2021, 07:48 AM by Domarius.)
When you said "you don't draw, you set attributes" I remembered the point of Tilengine. Those consoles didn't have expensive chips to hold frame buffers, you don't have the ability to draw individual pixels, you do everything with sprites and tiles and layers and using the scanline interrupt for effects. So Tilengine is a 2D engine that gives you support for sprites, tile layers, etc. like other 2D engines, but, it works like those old systems did, allowing you to easily recreate those scanline effects. And colour palette stuff. No other engines do either of those things.
I knew when I was going the route I was with Kha, that I was recreating things that already exist in Tilengine, but I think at this stage in my personal development, creating things in Kha using shaders was a good step for me even if just to learn shaders and drawing with the GPU in a way that takes advantage of it, instead of constantly avoiding it. Even if I use Unity or UE4, the option is there to write powerful shaders, so the knowledge will always be super relevant.
But, I've decided that I owe it to myself, and Tilengine, that I have to at least attempt implementing Tilengine on top of Kha to see what it's like to use in that way. Once I've used just Kha, and then Tilengine on top of Kha, I'm going to decide which path I'll take for my next pixel art project.
(07-20-2021, 04:04 AM)megamarc Wrote: I've seen many "retro" 2D games developed in Unity and Game Maker Studio that have a wrong concept of pixel. Authors end up mixing assets of different "pixel sizes" on a Full HD framebuffer, having for example 4x4 and 5x5 macro-pixels, moving at 1 pixel granularity of the final framebuffer. This is totally wrong, producing "unaligned pixels" that ruin any attempt of using a post-processing CRT filter that require a low resolution source. A pixel doesn't have size, it is not a square but a dimensionless point. And I've seen those faults in several commercial games. You must be careful with this. Tilengine does this right, as only the final framebuffer gets upscaled with all pixels having always the same size and alignment.
Oh man, don't get me started! People using inconsistent pixel sizes has bothered me for years, everything from mixed sizes, to arbitrary scaling. Half the time I bring it up, people are like "Oh it's just an art style, let them be"... it's like nails on a chalkboard to me. I would never mistreat my pixels in this way.
Actually if you watch the video I linked, in the 2nd half I demonstrate exactly this, with my Kha project. It's about how I can control the scaling process, having the whole game render to a "native resolution" frame buffer, and then the code that scales that up is completely seperate, and it tries to stick to whole integer scale values, and when it can't, it scales up to the nearest without filtering, and then scales up again to the target resolution using filtering, which applies just enough to gently hide odd sized pixels but still look sharp. Something I read that Sonic Mania does.
Quote:I didn't know about Kha you mention, I'll take a look to guess where it fits and what does it offer.
Cool, let me know what you think! I think it provides a much more portable layer than SDL ever would, and it would be my choice when I use Tilengine. I could provide a "how to" article for your site when I do!
I'll definitely keep you updated.
Posts: 673
Threads: 33
Joined: Jan 1970
Reputation:
13
I see you really understand how to handle pixels properly, glad to know! But having seen the same mistakes you talk about in released games, I had to mention it.
I've done a bit of research about Kha. It is a complete multimedia framework -not a library- that sits on top of Haxe, an ECMAScript-based language that outputs source code for several other languages. This architecture is very different from SDL, that is a cross-platform native system library that provides basic windowing and multimedia features, with bindings for other languages and environments.
Can be Tilengine mixed with Kha? It depends on Haxe -I haven't researched this topic quite enough-. Haxe compiler outputs source code, not native binary code, so it cannot use bindings to existing binaries the way C#, Java, Python or Freebasic do. Or in the best case scenario, its support would be limited to platforms where Tilengine has already been ported and has a native library available. Understanding that the aim of Haxe is generating code for as much platforms as possible from a single codebase, this approach would be of no use.
Feasible approach would be mixing C and haxe modules at the source level, before generating final output. Emscripten compiler does this for C and javascript, and in fact I'm using it to bring HTML5 support for Tilengine. With this it will run inside a web browser, as either a javascript source library or web-assembly final executable. Tilengine itself is very portable, depending only on basic C stdlib, libpng, and SDL2 for windowing subsystem. And even this one is contained on a single module, isolated from the main engine, so it can be easily removed and replaced with whatever rendering mechanism the host provides. The Java binding and libretro core port don't use SDL at all.
But I'm afraid that in Haxe, the source is always the .hx file, and everything originates from here, not the other way around. However if there's a tool to translate/compile from C to haxe -something like "Emscripten for haxe"-, then they could be interoperated.
For the ongoing HTML5 research, it is interesting because it will bring support for web browsers, but also for mobile platforms via hybrid application, as there are frameworks like Electron that take a client web codebase (HTML5/css/js) and output mobile apps.
Posts: 136
Threads: 26
Joined: Aug 2017
Reputation:
5
08-03-2021, 09:02 AM
(This post was last modified: 08-03-2021, 09:23 AM by Domarius.)
I like reading your take on these technologies, it's particularly insightful because I don't have the same coding experience background you have.
I'd want to see my Tilengine game running on the Nintendo Switch, and if the smooth cross platform experience I've had so far with Kha between Windows and Linux is anything to go by, this could be a good base for Tilengine to run on. There's a couple of points I thought I'd mention, to see if this impacts how useful you think this Kha/Kinc system is for Tilengine.
- Kha is also built on top of "Kinc", which is written in C. Kinc seems to have the same "low dependency" spirit as Tilengine. "Kinc does not depend on any libraries which are not an inherent part of the target systems." The best places I know to learn about Kinc is the Kinc Github Wiki, and the Kinc-Samples repository. Kinc is very cross platform, this includes desktops, consoles, and mobiles (I guess Kha provides additional platform support such as WebGL and Pi via Haxe) Due to the common C/C++ language, perhaps it's more appropriate to use Tilengine directly with Kinc, as opposed to Kha?
- Kha (via Haxe, I believe?) has the ability to include C / C++ code files at compile time. There's a demo project of this in the Kha-Samples repository. My understanding is it lets you include a C/C++ library written in C/C++ directly into your Kha project and get all its functionality, even though Kha is Haxe based. Perhaps Tilengine could be included in Kha this way?
BTW if you ever want to ask the Kha/Kinc author any questions directly, I've found that he's very responsive on his Discord sever! The application link is under the "Community" section on his kha.tech website. His discord name is RobDangerous.
Posts: 673
Threads: 33
Joined: Jan 1970
Reputation:
13
Hi!
Building a native version of Tilengine for the Switch should be easy, as Nintendo offers C/C++ in their SDK. Current tilengine license (MPL 2.0) allows linking statically or even using source modules directly inside other projects. However I understand that in your case, you're not targeting Switch alone, but using cross-platform framework that allows building for the Switch as well as for other platforms.
Kinc seems promising, the "NativeLibrary" Kha GitHub sample you pointed shows how to mix source C/C++ and build a library that can be called from Haxe. It requires work to write the binding -as with any other language-, but it definitely seems to allow it. I've not attempted to do it yet, as I don't know this ecosystem and its tooling, but it's worth trying.
Thanks for sharing this useful info!
Posts: 136
Threads: 26
Joined: Aug 2017
Reputation:
5
Sorry for the late reply. Ok so sounds like the easiest way for me to use Kha as a multi-platform exporter for Tilengine is to hook it in using the NativeLibrary example. It would be cool to try this out after the current project!
|