Updating the engine


TLDR: I updated the game engine to separately render the user interface and game world to better handle different screen resolutions - this should allow smoother graphics and sharper fonts in-game when interpolation is enabled in the options menu. This new build is available for download.

Introduction

Sometimes a small change has a big effect. One of the issues of working with a tile-based game is handling multiple different resolutions that the viewer may play at. The usual way to accomplish this has been to fix the game's logic at a given resolution and then allow the actual screen resolution to vary. Then in-game the engine handles scaling up or down the tiles to match the screen resolution. 

This is the method I have adopted in the development of the Macrocosm engine. There is a couple of issues with this method which I'll discuss today and share my strategies for dealing with them.

The default way in SDL2 to handle this is to use 

SDL_RenderSetLogicalSize,

to fix the logical resolution of the game - the game programmer then makes all his/her design decisions according to this and lets SDL deal with the scaling. For a tile like game, however, this can lead to blurry text and sprites depending on how different the screen resolution gets from the logical one.

Texture Sampling

Fortunately SDL offers some handy sampling via 

SDL_HINT_RENDER_SCALE_QUALITY, 

I won't go into this topic in detail but there is plenty of good resources on the internet on this topic. I recommend the one from here by javidx9 on texture sampling. This can help smooth out pixelated graphics and text at screen resolutions lower than the logical one. 

One has to watch out for a nasty effect here - because now the mouse coordinates are still expressed in terms of the screen resolution fortunately SDL provides a function to convert between the two scales,

SDL_RenderWindowToLogical,

without which, all the mouse collision detection in-game would be broken.

However, you solve one problem and get two others. 

The first of these is the introduction of artifacts at the edges where tiles meet - this is because on anything other than nearest-neighbor pixel sampling, SDL will sample at the boundary of the tile, you can see this visually as visible "edges" that define the tiles as shown below:

Screenshot from 2023-05-16 10-57-55

I looked up a number of solutions for this, the easiest is to add an artificial edge around each tile, thus stretching it out and giving the interpolation an extra bound to work with. I couldn't get this to work for me as my tiles are designed to give the illusion of being isometric, thus the artwork doesn't cover up the entire tile dimensions but are really only a center rhombus overlaid on top of a transparent background.

The second issue is that the user interface and all fonts now are also scaled up and down and smoothed this same way and this can lead to some pretty pixelated UI and difficult to read font.

Back Buffer

The way I ultimately dealt with this was to render the game world (i.e. tiles) and user interface to a separate texture and then scale it accordingly all at once before rendering to screen - I believe the technical term for this is applying and then rendering from a back buffer. 

The way this works is to switch the rendering target within SDL from the screen to a newly created texture via 

SDL_SetRenderTarget 

and then populate that texture with your entire scene. Then switch the rendering target back to the screen and render that texture to the viewer. The key insight is to create that "scene texture" at the desired logical resolution of the game

Now this has the added bonus allowing us to render the UI separately - we can create a separate texture at the screen resolution and render the UI - which is almost completely vectorized to it and then render it on top of the game texture by overlaying and alpha blending it. This way the UI is unaffect by SDL's scaling and allows me to create a crisper looking UI with sharper edges - regardless of the screen resolution of the player's hardware.

Furthermore, down the line we can imagine creating a zooming function, i.e. allowing the player to zoom in and out - one simply needs to change the size of the game scene texture on command (i.e. scaling up and down according to mouse wheel movement).

Aspect Ratios

Okay but what if the player's screen is not at 16:9 aspect ratio? My naive solution initially was to do nothing, when the aspect ratio is different from the logical resolution, SDL adds letterboxes to accommodate the difference. However, I didn't like this solution and the principle reasoning being this broke my mouse collision code - as now the mouse coordinates could wonder off into the letter boxed areas. I could have corrected for it but the far simpler solution was just to accommodate for different aspect ratios by enumerating through them and providing an single fixed logical resolution for each combination - there are only so many hardware screen standards, 3:2, 4:3, 5:4, 16:9, 16:10...so forth.

Conclusion

Back buffering and separately rendering the UI and sprite tiles makes a big difference in image quality and enables greater flexibility in handling different player hardware, ability to add further graphical effects down the line. 

On a side note, this is the first of what I would consider game design/technical posts, if people find this useful or interesting I'd like to do more. I may in anyway, albeit infrequently as often times I learn things along the path of game development and then I forget them a few months down the line. Making a journal just for my own sake is often good enough a reason to write.

-K

Files

Macrocosm-Linux 26 MB
May 19, 2023
Macrocosm-Windows 31 MB
May 19, 2023

Get Macrocosm

Download NowName your own price

Leave a comment

Log in with itch.io to leave a comment.