How do you handle fonts at various screen/buffer resolutions?

I’m developing a game with MonoGame. While in the past I’ve been unsuccesful in finishing a project, my point of view in development has always been to cater to the user. In that sense it’s necessary to support both HD resolutions like 1280x720, 1920x1080, 2560x1440, 3840x2160 as well as various other resolutions and aspect ratios like 1366x768, 1280x1024, 1680x1050, … Which brings me to fonts.

Fonts in the monogame content pipeline are delightfully easy. You add the truetype font to your VS project, make a spritefont, add it to the pipeline, build, and ready presto. Unfortunately it’s not a very flexible system as far as many sizes or run-time font importing goes.

With the current system I see two solutions to scaling for variable screen resolutions. I add a legion of font sizes, calculate the desired font size, pick the font size either “closest to” or “just below” that and draw. Works well with screen resolutions that are multiples, may lead to odd behavior where not (e.g. not fitting onscreen or in the message box). Does require some one-time work up-front adding all the different spritefonts (per font I intend to use).
// for example; basing font size on backbuffer height int[] fontSizes = { 12, 15, 18, 21, 24, 30, 36, 42, 48, 56, 64, 72 }; // 72 at 4k becomes 36 at 1080p and 24 at 720p. Nice! // 25.6 -> 24 at 1366x768 // 34.1...-> 30 (or 36) at 1280x1024 // 35 -> 30 (or 36) at 1680x1050

Another solution is to pick one font and simply scale it using the scale factor in SpriteBatch->DrawString(). I’m no expert but my brief testing - using Baskerville font size 72 as a base - seems to show it works well enough down to .5f (upon inspection characters 5 and 7 are getting blurry), including odd scaling like 0.948… (1024/1080). It’s clearly not as crisp when scaling up, or below .5 - I could see people scaling down to .33f but .25f is clearly distorted.

Perhaps the ‘best’ solution with the current system is a combination of the two. Round up the desired font size (e.g. 34.1 uses spriteFont for 36) then scale down using drawString (1024/1080*36 = 34.133…).

My ideal solution would be to have a default size based on the resolution height then give the user a slider for font size (and - wild thought - perhaps even a dropdown menu for type), which then gets dynamically generated. One piece of code would be valid for all current, future, esoteric resolutions, and adaptable to user preferences. I might enjoy Baskerville 72, but someone else might prefer Baskerville 108 or even monospace 56. Currently looking into KonajuGames’ TrueTypeSharp which - with a few days work building my own drawString function and such - sounds perfect.

So I’m asking for your thoughts. After 8 months of gaming on a 4k resolution, quite a few games are straining my eyes or are just plain unreadable (tiny red text is the worst!). I on the other hand probably go too far on user customization. I love MonoGame. The pipeline is delightfully simple and makes content very fast to access. My perception (which may be wrong) is that it’s very rigid though. There’s little focus on helping me out with pseudo-random meshes, pseudo-random textures, scalable (pre-texture) fonts (runtime generated fonts), SVG -> appropriately scaled texture, …

Food for thought: Steam Hardware Survey
Primary Display Resolution: Height % _720 1.55 _768 30.45 _800 1.76 _864 4.01 _900 11.02 _960 0.21 1024 4.31 1050 3.78 1080 37.65 1200 1.49 1440 1.72 unk/othr 2.05 sum 100.00

I think you should be able to leverage the existing infrastructure to generate glyphs at runtime. I’ve never looked into this myself so I can’t help you out though.

Another option is to look at vector fonts. Nuclex Framework has support for that.

1 Like

Your solution on using bitmap fonts it’s OK, but it can get very complex and eats memory. On the other hand vector fonts are like using hammer to kill ant. You should check Signed Distance Field Fonts.

SDF fonts have advantages of vector fonts, but are easy to use and render like bitmap ones. All you have to do is some support for MonoGame Pipeline tool and simple shader for SpriteBatch.

1 Like

@Jjagg I’ll admit I don’t google well. I did check the Framework docs but perhaps I didn’t notice out of ignorance. I assume you’re referring to using parts of Framework.Content.Pipeline.Graphics.SpriteFontContent and/or Framework.Graphics.SpriteFont?
Nuclex Framework looks nice. Glossing the feature list outlined text popped out to me. Though I wasn’t looking for a full framework, I will definitely check it out. Worst case it’s a cheat sheet for ideas.

3D Text Rendering (outlined, flat and extruded 3D text)

@filcon SDF popped up in my search alongside the mention that it’s really difficult to implement. Rough quote is “That’s why Valve only uses it for their logo”. Still with a C++ library ready to go all I need to learn is about libraries :slight_smile: More reading material for me.

At least they’re dead :slight_smile:

@kennumen Don’t feel bad about not finding it, the docs are in a pretty bad state. But yeah, those are the parts that I mean, mostly the pipeline parts that are used for processing spritefonts. I think @harry-cpp and @KonajuGames know how that all works.

IIRC there are nuget packages with separated parts of Nuclex ported for MG (I think the name is also changed slightly for the port…). There should be one specifically for fonts.

Not sure what you mean by “difficult”, no idea what’s your tech level. On that link I posted you can find generator (simple command line exe). With that generator you can generate texture (same like sprite font generator does in MonoGame). And that texture you can use with SpriteBatch. Shader is very simple actually, few lines of code.

That’s it. It’s prolly about few hours of work, no need to know C++, only C#.

@Jjagg thanks for clarifying

@filcon Just wasted half an hour trying to find that page again with browser history and google searches but no luck. On the contrary, found one that claims it’s relatively easy, so perhaps I remembered wrong. All I meant in the previous reply was a roundabout way to thank you for correcting my wrong assumption about SDF and that with such a ready-made solution it would be easy even if SDF were hard.

You say no library - the generator may be easy but I’m looking for a runtime solution. Are you saying that texture stays sharp when scaling up/down? (because it’s multi-colored)

Also, would you mind briefly explaining why vector fonts are overkill?

Hi, Just wanted to share my experience with this.

My first completely working game, I spent a lot of time on resolution and aspect ratios. sprites in different resolutions, all the font issues, etc…
My last completely working game, I drew the whole thing on an HD render-target, and just scaled that to user specifications.

The difference, in my opinion, was not worth the extra effort, for a one man project with plenty of other features to worry about. -when weighed against the actual user count with such needs…
With the simple scaling approach, you can fludly get any window size you want, and add black bars to compensate for aspect ratio.

-But if you want to give it some polish, I think your idea sounds fine. Considering the physicality of pixels on the screen, its hard to get around scaling to some degree, I think…

we’ve probably all seen it done something like that in working games… Some (arbitrary) small number of base-fonts, (sizes that fit natively with the most widespread screen sizes) and just scale them.

Yes, SDF fonts are generated like simple texture (similar to sprite fonts in MonoGame), but generation is lil bit special. With simple shader you can draw and scale font as you like. There are some limitations, but for games it’s OK.

You have to imagine what is vector font. It’s complex set of bezier curves, very hard to do correctly and need much more GPU power. Rendering SDF font is just simple quad with very fast shader.

@monopalle Thanks for sharing your experience. It may well not be worth my time but I doubt I’ll be able to let it go without at least having a go at it myself. I would like to clarify that I’m merely talking about font scaling. While I plan to scale the entire UI as crisply as possible, the main game would contain a vast range of zoom so scaling is not an additional issue there.

I should definitely concede your approach has proven merit - most console games do it (e.g. render at 900p, scale to 1080p) to millions of content gamers. Your approach has the added benefit that while things aren’t as crisp when say scaling 1080p to 2160p at least all text and UI are readable and clear. If you’re using pixel art it might even be a non-issue.

Despite that I still want to give it a go. If nothing else it’s crisp text for me and a fun, interesting project. Be it vector fonts as an inlay for rasterizing vector graphics (which could be interesting as well) or SDF fonts as an inlay into shaders, I’m bound to learn something with a relatively easy learning curve.

@filcon Thanks for both clarifications. It’s weird how in my mind I hadn’t linked vector fonts to vector graphics (which I know from Illustrator and SVG). The difficulty of it should not be much of an issue if the generation is done by Nuclex - then using it is just another texture, and the slowness of generation isn’t much of an issue since it’s done once per new GameState or after the resolution or font size are changed. Still I’m leaning towards SDF - learning simple shaders would be a boon.

Perhaps I’ll feel adventurous and test them both, like a proper dev would.

For anyone like me looking for a simple solution to scale fonts down without losing quality, here’s how to do it.

Get Monogame Extended (its on nuget), and then follow these instructions
Note 1: When building the font in BMFont, add some padding on all sides (I do 2px) to prevent clipping on the edges.
Note 2: This is the most important part. In the pipeline tool, select all of your font textures and turn mipmaps ON. Then build, save, and give it a try.

It took me a long time to figure this out and I kept coming back to these threads trying different suggestions. So hopefully I’ll save someone else the nightmare of trying to swap spritefonts during runtime, SDF shaders, runtime font generation, etc

1 Like