A productive week, followed by some much-needed vacations!
On week 106 I added text to SUGAR and it tooks a lot more time than anticipated! On week 107, I went to Belgium and ate some delicious food!
SUGAR now has text rendering! You can use whichever font you want, after feeding it as a png to the engine. (I might add ttf support in the future) Unicode is supported, not just ASCII, although you will have to provide a font with the unicode characters you need.
Let’s talk about unicode, since we’re already on the subject. That was the first thing I hadn’t anticipated. SUGAR is to be a professional game framework. So SUGAR games should be able to handle localization, there’s hardly any way around it. That means SUGAR needed to support unicode characters. Except the standard way to store text in C++ is through byte strings, which only support ASCII.
With those byte strings, each character takes up one byte in the string of bytes. That’s great, it’s fast, it doesn’t take much memory and it’s very simple to use. But you can only use single-byte characters, whereas the large majority of unicode characters are multi-byte.
Of course there is another way, just for unicode characters, called wstring. (‘w’ is for wide) Here, each character (‘wchar’) takes up multiple bytes and it’s compatible with all of unicode. It’s still fast but it takes up more memory and more importantly, it’s not compatible with regular strings. If you want to convert a string to a wstring, you’ll have to do a slow byte-per-byte conversion. And from wstring to string, you will have to check that all the ‘wide characters’ are in fact single-byte characters, so that they fit in the regular string.
It’s a mess. It feels horrible. But that’s how it is.
So I quickly realized that, ideally, I would be supporting both regular strings, with single-byte characters, and wide strings, with multi-byte characters.
Here is what I came up with:
A ‘font’ structure, which would store a map which maps ‘wchar’ multi-byte characters to small graphic surfaces containing the individual glyphs, and then another map, which maps the ‘char’ single-byte characters to those same graphic surfaces.
When rendering a string, be it wide or not, we would take each character, find it in the correct map (‘wchar’ or ‘char’), draw the glyph sprite and move the cursor by the glyph’s width, plus one pixel for spacing.
However, we don’t actually need a graphic surface like I implemented for the sprites. The surface we need here only needs one bit per pixel, rather than one byte. Each pixel of a glyph is either full or empty. When drawing, you check which it is and you draw a pixel with the wanted color if it’s full. Besides, all the glyphs aren’t the same height. So unless you want to store a lot of unnecessary empty pixels, you’ll want to store the height offset on a per-glyph basis.
So here comes the ‘glyph’ structure, which has a width, a height, a height offset, and of course the pixel data, as an array of booleans.
In the font structure, we also store a ‘spacing’ value, which is the width of the first glyph in the font. This value is how much the cursor should move in the case where a character isn’t found in our letter maps. (one such case is when the character is ‘ ‘)
With all that settled, I could finally code the font loading from the png. And then the print function. And its four overloads.
I think text effects, like rainbow text or wavy text, are great and I want to encourage them. That’s why the print function has overloads to draw single characters (wide or not) faster than by printing a single-character string.
The two other overloads are the classic string printing you’d expect, one for wstrings and one for regular strings.
Finally, I wanted to be able to export and import fonts as strings of data. That also proved to be a challenge but I’m going to keep it short.
Basically, I had to come up with a way to encode for each glyph in the font: the wide character it corresponds to, the width of the glyphs in pixels, the height, the height offset, and the 1-bit pixel data. In the end, it’s all hexadecimal numbers, with the byte-width of each element (except the pixel data, since that’s just width * height bits for every glyph) defined at the very beginning of the string.
With this feature working, I can have a default font embedded in SUGAR! \o/
Later on, I’d like to implement a ‘print pattern’ feature to make shadow and outline effects easier and more optimized! For now, what’s done is enough to be very usable!
In the last weekly recap, I said I would be rendering metaballs in SUGAR. Well here’s the proof that I did. I don’t think it looks too good though.
I tried to make it look more interesting in a variety of ways but nothing could really save it to my eyes, so I just abandoned the idea. It’s a shame but it’s not a big deal.
And that’s it for Week 106! During Week 107 I went to Belgium with my partner as a couple vacations! We went to Bruges, Liège and we stayed in Brussels. We ate a lot of french fries and waffles and ice cream and it we had a very nice time, despite the fact that we were both sick for most of the trip! (I’m still sick as I write this, but I’m getting better)
I did not read nor write a line of code during that time and tried not to think about it either, and it felt really good! I never actually took a break like this since I started my indie adventure two years ago. It felt weird at first but at no time did it feel wrong. So that’s cool!
I definitely feel refreshed now! With SUGAR getting ever-closer to be usable, I’m excited to start making games again! And that might be coming very soon, this week maybe!
The only missing system in SUGAR is the audio system. All the other ones are functional despite not being complete. That means that I can already use it to make a small sound-less game! (maybe I should do that??)
I’m actually hunting for the right solution for the audio system right now. I’m not rulling out using an external library, since I’m already using SDL. But if it’s not too complex, I’d rather implement my own thing with SDL, that would answer only my needs. Hopefully I’ll have more to tell you about this next week!
But that’s it for these two weeks! Both of them were very good, each in its own way! I learned a lot about rendering text and I got some much-needed rest!
I want to thank all my Patreon supporters, because I couldn’t be writing posts like this one without them! Here are the names of all the 3$+ supporters:
Ryan Malm, Joseph White, Austin East, Marcin Majewski, Zachary Cook, Jefff, Riccardo Straccia, HERVAN, Andreas Bretteville, Bitzawolf, Alan Oliver, Paul Nguyen, Dan Lewis, Christian Östman, Dan Rees-Jones, Reza Esmaili, Thomas Wright, Chris McCluskey, Joel Jorgensen, Marty Kovach, Cole Smith, Giles Graham, Tim and Alexandra Swast, Sasha Bilton, berkfrei, Jearl, Dave Hoffman, Finn Ellis, Egor Dorichev, Jakub Wasilewski, amaris, Brent Werness, Anne Le Clech, Jeremy Bouin, Jesse Bergerstock, Jacel the Thing, Pierre B., Sean S. LeBlanc, C Oakreef, Andrew Reist, vaporstack
This week, I’m researching a solution for my audio needs, and I might start on a small project using SUGAR!
Have a nice week!