How to create progressive/typing text?

What I mean is instead of just having text pop up on the screen, how would you go about making the text appear progressively? So for instance, when you type on your computer, the words don’t appear automatically, each letter has a bit of delay.

Typewriter Animation That Handles Anything You Throw at It | CSS-Tricks - CSS-Tricks

This is kind of what I mean. But it would be much faster, and it would be used for a sort of intro, right before you start the game.

Here is an example with source code available:

The code is in the OBS_Title.ino file, in particular the function introText(),

It doesn’t have the cursor, but I am pretty sure you can tack that in.

1 Like

How the code works:

The text is defined as an array of characters, with the first value on each line being used to provide an x-offset so the text will be centered on screen. As there are four panels of text, I also have two extra arrays that tell me how many characters in each panel and an array of the text arrays themselves (IntroTexts).

const uint8_t textLengths[] = { 50, 51, 54, 61 };

const uint8_t PROGMEM IntroText_00[] = {    
    5,'Y','o','u','r',' ','s','h','i','p',' ','h','a','s',' ','b','e','e','n',
    11,'d','a','m','a','g','e','d',' ','b','y',' ','s','o','m','e',
    16,'s','p','a','c','e',' ','d','e','b','r','i','s','.','.',
    };

const uint8_t PROGMEM IntroText_01[] = {
    7,'.','.','t','h','e',' ','s','t','e','e','r','i','n','g',' ','i','s',
    5,'b','r','o','k','e','n',' ','a','n','d',' ','y','o','u',' ','c','a','n',
    17,'n','o','t',' ','n','a','v','i','g','a','t','e','.',
};

const uint8_t PROGMEM IntroText_02[] = {
    ...
};

const uint8_t PROGMEM IntroText_03[] = {
    ..
};

const uint8_t * const IntroTexts[] = { IntroText_00, IntroText_01, IntroText_02, IntroText_03 }; 

When rendering the text, I keep a count of the number of characters to render and increase this every x frames.

void introText() {

    uint8_t line = 0;
    uint8_t x = 0;
    
    for (uint8_t i = 0; i < titleScreenVars.index; i++) {

        uint8_t c = pgm_read_byte(&IntroTexts[titleScreenVars.panel][i]);

        switch (c) {

            case 0 ... 31:
                line++;
                x = 0 + c;
                break;

            case ' ':
                x = x + 3;
                break;
            
            default:
                font4x6.setCursor(17 + x, 9 + (line * 10));
                font4x6.print(static_cast<char>(c));
                x = x + 5;
                break;

        }
        
    }

When reading in the characters, if the character is a value < 31 I assume it is an x offset and increase the line counter (so its a new line) and add the value to the x coordinate.

I also wanted to have smaller spaces so I handle a space character explicitly.

Finally, if it is a real character then I print it. (You can use the standard Arduboy print() if you want.

The rest of the introText() function is handling increasing the character count to display and handling changing between panels of text.

2 Likes

My old visual novel engine did this with its TextBox class:

The basic idea is:

  • You maintain an array of char, to store the characters that have already been printed
  • You maintain a pointer to where new characters are being copied from
  • Count a certain amount of frames or time, and every time that amount of frames or time elapses you copy a new character to the output
  • If you encounter a '\0' (null character, see also: null-terminated string) in the source text then you’ve written all the text and can stop the process.

If you’re only printing one line at a time then you can also do it without the array with just a pair of pointers, but the array is important if you’re going to be printing lots of text from different sources.

Unfortunately you’re likely going to have to understand how pointers and pgm_read_byte work before you’ll be able to pull this off and I for one am far too tired to attempt those explanations at the moment.


If you want an easier alternative, you could do a Star Wars crawl, either by having a single massive image and making it gradually move upwards (very easy, but possibly quite memory hungry, and if your text exceeds 255 pixels you’ll have to stitch several images together in code), or try a similar effect with strings/characters, which is probably best achieved with a 2D array and some voodoo.

1 Like

So you have a few arrays to store strings, that’s pretty much all I understand.

If you haven’t already, it would pay to play the game - even in the emulator on the web page to see what I have done.

I have four panels on my introduction - hence four arrays.

My introText() array is called on each frame. The index value indicates how many characters to print this frame, starting at 0 and increasing up to the maximum number of characters in the array.

I have some special values (those below 31) which are not characters but instead are used to center the text. These are explained above.

You may need to read through the code and down load it and play with it to really understand. It is a little complex but not beyond what you have learnt already. You might need to take some time and actually understand it …

1 Like

Bear in mind @Ard_Flamingo probably doesn’t know what pointers are or how they work, or what pgm_read_byte does, and likely hasn’t encountered GCC’s case range extension either.

For that matter @Ard_Flamingo probably doesn’t know what character encodings are either, so the idea that a char could have a value of 0 or 31 is probably also a bit alien.

(At least, going by what I’ve seen/remember seeing. Perhaps he/she has read about them and I’m not aware.)

1 Like

Good point … as I said above, its probably best to grab one of the two programs mentioned above (yours and mine) and pull them apart. When @Ard_Flamingo gets stuck, then they can ask a specific question rather than broad, complex ones.

2 Likes