Sponsored By
Nathaniel Stevens, Blogger

February 8, 2016

14 Min Read

While I was overhauling our GUI for Infinite Dive (Android and iOS) version 4.0, I discovered a sexy new way of displaying the encrypted text. Before, every letter had to be it’s own GUI element to allow for the use of a second font. But now, now it’s just one font, one font that combines both the encrypted and decrypted symbols. Here’s the end result:

encrypted-text-tutorial-1

 

ASCII

If you pronounce “ASCII” as ‘ass-key,’ you’re probably good to skip ahead to the next section. If you thought the two ‘i’s at the end were roman numerals, you should probably stick back here with me for a hot second.

ASCII is how your computer understands text. Here’s a nice table that lays it all out. For the purposes of this tutorial, all you need to pay attention to is the “Decimal” column. For every symbol, there is a corresponding integer that can then be converted to binary and stored on your computer. The alphabet starts with capital ‘A’ as 65 and ends with lowercase ‘z’ as 122. (As far as your computer is concerned capital ‘A’ and lowercase ‘a’ have nothing in common with each other! Which is why so many data fields from the olden days were case-sensitive.)

The Crux

Now, here’s where it gets cool: starting at ASCII 127 you’ll see all the characters that aren’t on your keyboard. Assuming you’re not making a Dwarf Fortress clone and looking to encrypt a map, you’re probably not going to use these characters in your text. So, what we can do, is create a new font that combines both the encrypted and decrypted font by copying the encrypted letters over into the upper ASCII region of the decrypted font!

FontForge

We’ll be using FontForge for this task which you can download for free. You’ll also want to grab your two fonts for the encrypted and decrypted states. Font Squirrel is a nice place to start this search.

Once you have FontForge installed and both font files downloaded, open up FontForge, then,

1. Open up your decrypted font file by going to File -> Open and selecting the downloaded font file.
2. Open up a second instance of FontForge by going to File->New.
3. In the new window, open up the encrypted font file.

encrypted-text-tutorial-2

Now let’s check that the dimensions between the two fonts are identical and change them if not. We’re about to spend two minutes on a task that took me two hours. This is probably a good time to confess that I know just enough about FontForge to achieve this task (albeit in what is likely a farcical set of steps to FontForge power users). But, you know, those who can’t do, teach. Anyhow, on both windows go to Element->Font Info. Then in those windows select “General” on the left sidebar. Your screen should look something like this:

encrypted-text-tutorial-3

You’ll quickly notice that the numbers differ (If they match on your fonts, then skip this step and congratulate yourself on your expert font picking skills). After going through this process the hard way, I sorta know what these numbers mean, but not well enough to explain it, which is fine, because all that really matters is that they match. If necessary, go ahead and make the numbers on the smaller font match those of the larger.

Before we copy over the font, there are two things to note:

1. You’re probably not going to want the player to have to decrypt both the uppercase and lowercase form of the letter. Therefore, you can save some time by just adding the lowercase encrypted letters to the combined font.
2. You’ll need to choose an arbitrary number to offset the encrypted font by. I’ve chosen 100, because it’s easy to remember.

Alright, time to copy and paste! (You can drag select to copy multiple letters at once). Make sure you paste by first selecting ASCII symbol 197 (‘a’(97) + arbitrary number(100)). You can see which ASCII symbol is currently selected by looking under the menu bar.

encrypted-text-tutorial-4

If a pop up occurs letting you know that “There is already a glyph with this Unicode encoding blah blah blah” just hit “yes,” and then hit “yes” again, and then continue hitting “yes” until FontForge surrenders unconditionally to your iron will.

By copying over the font selection, the glyphs will retain their original encoding, which is bad. So let’s fix that. Go to Encoding->Force Encoding->ISO 8859-1 (Latin1).

Tweaking the Font

Despite our best efforts to automate everything, your encrypted characters may need some tweaking. If you use an encrypted font like ours, there’s a chance it wasn’t designed in a way to be compatible with other fonts. It’s hard to explain what the problem is but you’ll know it when you look at the combined text and you have no idea where one word ends and another begins. It’s probably best to skip this step and only come back to it if necessary.

Double click the first encrypted letter. A new window should pop up looking something like this:

encrypted-text-tutorial-5

All fonts use an em square to visualize the spatial relationships between the letters.

encrypted-text-tutorial-6

Here are the important lines labeled in FontForge.

encrypted-text-tutorial-7

So, looking at this example, if we were to use this symbol as is, it would look like it was floating above where it’s supposed to be because of the space between the bottom of the symbol and the Baseline. Also, there’s probably not enough space between the end edges of the symbol and the horizontal margins so it may look pretty cramped when placed next to other letters.

The editing tools in FontForge are similar to those used in vector graphics which is far beyond the scope of this tutorial, but here’s a great tutorial on designing with FontForge that can get you started if you want to get fancy with this. Also, as you’ve probably realized, if you really want to be ballsy, you can create your own encrypted symbols using FontForge.

Exporting

Alright, now we’re all good to go. Let’s export this baby and get back into a program I know more than 3 things about! File->Generate Fonts

encrypted-text-tutorial-8

Just make sure it’s set to TrueType and all should be fine. (FontForge always tells me there’s a bunch of errors and then I tell it to generate it anyways. It’s a little game we like to play. I suggest you do the same).

Now import that font into Unity and I’ll show you the code!

TextEncrypter Component

Set up a new scene and add a UI Canvas. Then add a child GameObject with a Text component. Finally, create a new C# class called TextEncrypter.


using UnityEngine;
using UnityEngine.UI;
using System.Text;
using System.Collections.Generic;

public class TextEncrypter : MonoBehaviour 
{
    public Text text;
    public bool[] encryptedLetters = new bool[26];

    public readonly int cipherOffset = 100;
    public readonly int asciiOffset = 'a';

    void Awake()
    {
        EncryptText();
    }

    public void EncryptText()
    {
        text.text = EncryptText(text.text);
    }

    public string EncryptText(string text)
    {
        // Use StringBuilder to prevent a massive amount of GC
        StringBuilder encrypted = new StringBuilder();
        Queue<char> letterQueue = new Queue<char>(text);

        while (letterQueue.Count > 0)
        {
            char letter = letterQueue.Dequeue();

            if (IsEncrypted(letter))
            {
                // If the letter is encrypted, convert the letter to lowercase if it isn't already
                // then add our arbitrary cipher offset to display it as the encrypted font.
                int ascii = char.ToLower(letter) + cipherOffset;
                letter = (char)ascii;
            }

            encrypted.Append(letter);
        }

        return encrypted.ToString();
    }

    public bool IsEncrypted(char letter)
    {
        // Only encrypt it if it's a letter    
        if (char.IsLetter(letter))
        {
            if (IsValidLetter(letter))
            {
                letter = char.ToLower(letter);
                return encryptedLetters[letter - asciiOffset];
            }
            else
                return false;
        }     
        else
            return false;
    }

    // Checks to make sure the encryptedLetters array.
    // Obviously problems will arise if it's size is
    // less than 26. 
    bool IsValidLetter(char letter)
    {
        if (letter - asciiOffset < encryptedLetters.Length)
            return true;
        else
        {
            Debug.LogWarning("The letter " + letter + " is outside the range of encryptedLetters!");
            return false;
        }
    }
}

 

This is pretty straightforward. We use a boolean array to save which letters are currently encrypted. Then, to encrypt, we convert the string into a char queue, check whether each letter is currently encrypted, and then offset the ASCII assignment if it is. Now
1. Go back to the Unity Editor.
2. Assign the UI Text component to the TextEncrypter’s Text fields.
3. Check a couple of the encrypted letter toggle boxes.
4. Hit play!

…crap. I forgot to assign the combined font to the Text component.
But, even without that new font, just offsetting the ASCII symbols by itself creates a sort of poor man’s encryption. But, it’s sorta lame compared to awesomeness we’re about to bestow upon the world. Let’s assign that combined font!

Oh baby! Now we’re rockin’ and a rollin’!

Now, if you’re a pleb, you can go ahead and call it a day. However, if you really want to sit at the cool kid’s table, you should display the encrypted text as a different color. And, if you’re like me, you never got to sit at the cool kid’s table (Well, except for that one time when they thought it would be funny to invite you to their table and sit you down next to the prettiest girl in school and watch as every noise excluding human speech dripped your mouth. But now you’re going to display your encrypted text as a different color, so, you won the war.), so let’s do that now!

Now in Color!


using UnityEngine;
using UnityEngine.UI;
using System.Text;
using System.Collections.Generic;

public class TextEncrypter : MonoBehaviour 
{
    public Text text;
    public Color encryptedColor;
    public bool[] encryptedLetters = new bool[26];

    publicreadonly int cipherOffset = 100;
    public readonly int asciiOffset = 'a';

    void Awake()
    {
        EncryptText();
    }

    public void EncryptText()
    {
        text.text = EncryptTextWithColorTags(text.text);
    }

    /// <summary>
    /// Encrypts text and sets encrypted letters to a different color with rich text.
    /// </summary>
    public string EncryptTextWithColorTags(string text)
    {
        StringBuilder encrypted = new StringBuilder();
        Queue<char> letterQueue = new Queue<char>(text);

        // Rich text requires the color to be in hex format
        string colorAsHex = ColorUtility.ToHtmlStringRGBA(encryptedColor);
        string openColorTag = "<color=#" + colorAsHex + ">";
        string closedColorTag = "</color>";
        bool isInColorTag = false;
        
        while (letterQueue.Count > 0)
        {
            char letter = letterQueue.Dequeue();

            if (IsEncrypted(letter))
            {
                int ascii = char.ToLower(letter) + cipherOffset;
                letter = (char)ascii;

                if (!isInColorTag)
                {
                    encrypted.Append(openColorTag);
                    isInColorTag = true;
                }
            }
            else
            {
                if (isInColorTag)
                {
                    encrypted.Append(closedColorTag);
                    isInColorTag = false;
                }
            }

            encrypted.Append(letter);

            if (isInColorTag && letterQueue.Count == 0)
                encrypted.Append(closedColorTag);
        }

        return encrypted.ToString();
    }

    public bool IsEncrypted(char letter)
    {
        // Only encrypt it if it's a letter    
        if (char.IsLetter(letter))
        {
            if (IsValidLetter(letter))
            {
                letter = char.ToLower(letter);
                return encryptedLetters[letter - asciiOffset];
            }
            else
                return false;
        }     
        else
            return false;
    }

    // Checks to make sure the encryptedLetters array.
    // Obviously problems will arise if it's size is
    // less than 26. 
    bool IsValidLetter(char letter)
    {
        if (letter - asciiOffset < encryptedLetters.Length)
            return true;
        else
        {
            Debug.LogWarning("The letter " + letter + " is outside the range of encryptedLetters!");
            return false;
        }
    }
}


Here were using Unity’s Rich Text feature to set the color. What makes the logic in this function a little tricky is the Unity enforced character limit in Text components. It’s pretty high, but, if you insert tags around every letter that’s encrypted, you can reach it with a healthy paragraph of text. So, what you want to do, is only insert the tags when necessary, which means, if there’s more than one encrypted letter in a row, you want to put the tag around that grouping of letters.

So instead of this:
b<color=#75B8EAFF>Ö</color><color=#75B8EAFF>Ó</color><color=#75B8EAFF>Û</color>n

You get this:
b<color=#75B8EAFF>ÖÓÛ</color>n

And with that, we have some pretty sweet lookin’ encrypted text!

encrypted-text-tutorial-12

Adding a Custom Editor

This works pretty well, but there’s one more thing that’s nagging me. Being one of those people who can’t shelve a book without having to sing through the ABCs at least twice, having the inspector show each letter by its array index is annoying at best. So here’s quick and dirty custom editor to display the letters (Make sure you place it in an editor folder!).


using UnityEngine.UI;
using UnityEditor;

[CustomEditor(typeof(TextEncrypter))]
public class TextEncrypterEditor : Editor 
{
    TextEncrypter encrypter;
    bool isArrayVisible = false;

    void OnEnable()
    {
        encrypter = (TextEncrypter)target;
    }

    public override void OnInspectorGUI()
    {
        encrypter.text = (Text)EditorGUILayout.ObjectField("Text", encrypter.text, typeof(Text), true);
        encrypter.encryptedColor = EditorGUILayout.ColorField("Encrypted Color", encrypter.encryptedColor);
        isArrayVisible = EditorGUILayout.Foldout(isArrayVisible, "Encryped Letters");

        if (isArrayVisible)
        {
            for (int i = 0; i < encrypter.encryptedLetters.Length; i++)
            {
                char letter = (char)(i + encrypter.asciiOffset);
                string label = letter.ToString();
                encrypter.encryptedLetters[i] = EditorGUILayout.Toggle(label, encrypter.encryptedLetters[i]);
            }
        }
    }
}


Now lean back, grab a white russian, and smoke that cigar (No! Not the Cuban! Save the Cuban), ‘cause you’ve just done something that looks pretty darn cool. Thank you for sticking with me till the end and, if you want to see it in action, check out Infinite Dive! If you know of a better way to accomplish this task, or you think my jokes are obnoxious, please let me know in the comments!

Read more about:

Featured Blogs
Daily news, dev blogs, and stories from Game Developer straight to your inbox

You May Also Like