Articles Archive
Articles Search
Director Wiki

Typewriter Effect modification

September 19, 1999
by Pat McClellan

Dear Multimedia Handyman,

I want to create a text effect and have a blinking cursor proceed the text as the text displayed onto the screen as if typed on automatically. Can you help?

Jim Moretti

Dear Jim,

In Director 7, the library of behaviors includes a great one called "Typewriter Effect" written by Darrel Plant. This behavior can be applied to a text or field member and allows you to specify the rate at which letters will be "typed" onto the screen. The text either types on as soon as the sprite appears on stage, or you can select to have it wait for your cue. You can also choose to play a sound effect for each letter that appears.

This is a really great piece of code, so why re-invent the wheel? It does most of what we need to do, except that it doesn't display a flashing cursor preceeding the letters to be typed on. So, let's just add that to the existing behavior. Cool?

First, let's examine how the existing behavior works. In the beginSprite handler, Darrel calls a handler called mInitialize. This mInitialize handler (also sometimes called a "method" -- hence the convention of using the "m" prefix in the name) grabs the text from the cast member and stores it in a property called pText. It replaces the text in the cast member with a space. You might expect it to simply delete all of the text (so that it wouldn't appear on the screen) but it uses a space instead. This is because if you delete all the text in your cast member, then all of the formatting (font, size, etc) is deleted as well. If there's a space there, the space holds the formatting info.

mInitialize also sets a property called pIndex to 0. pIndex is the "counter" which will keep track of which letter needs to be typed onto the screen. Every time a letter appears, pIndex will be incremented by 1... up until it reaches the total number of chars in pText.

So, let's assume that you've selected to not have the text type on as soon as the sprite appears. So it will just sit there blank, waiting for the cue. (That's where we'll want to have a cursor blinking.) You cue the typing to begin by sending a message to that sprite:

sendSprite x, #mActive

The mActive handler simple "flips a switch", setting a property called pActive to TRUE. When pActive is TRUE, the mType handler executes on every new frame (prepareFrame). mType checks to see if enough time has elapsed since the last letter appeared, and if so, it increments pIndex by 1 and adds another letter from pText into the text of the cast member. It continues to do this until pIndex reaches the number of letters in pText, then it flips the switch, setting pActive to FALSE. A very nice behavior indeed.

Now let's add the blinking cursor bit. We'll want to make it an option that you can turn on or off in the getPropertiesDescriptionList dialog box (the one that appears when you drop it on a sprite.) We'll accomplish this blinking by adding either a "_" (underscore) or a SPACE to the end of the text. One other little handler I'll add is one which can pause the typing. This is very simple; it just sets pActive to FALSE.

on mPause me
  set pActive = FALSE
end mPause

You can see how it works in this demo.

A sample movie is available for download in Mac or PC format

First, declare a new property called pCursor, and add it to the gpdl handler. It's a boolean value. When selected, it will activate the blinking cursor bit.

setaProp vPDList, #pCursor, [#comment: ¬
  "Blinking cursor?",  #format: #boolean, ¬
  #default: FALSE]

Here are the guts of the behavior: the mType handler. Darrel has commented the handler very well, so I'm going to leave his comments and add my own to my modifications.

on mType me
  -- called from prepareFrame handler
  -- only evaluated if behavior is active
  if pActive then
    -- if index value is larger than the number of 
    -- characters, all characters should have been shown
    if pIndex >= pChars then
      -- behavior should no longer be active
      pActive = FALSE
      -- get current millisecond value
      vMillis = the milliseconds
      -- determine if enough time has elapsed 
      -- since last character was shown
      if (vMillis - pStartTime) > pPeriod then
        -- increment index value
        pIndex = pIndex + 1
        if pIndex = 1 then
          -- Cursor modification
          if pCursor then
            pMember.text = pText.char[1] & "_"
                -- this line was in original behavior
            pMember.text = pText.char[1]
          end if
          -- Cursor modification
          if pCursor then
            currentText = pMember.text
            delete the last char of currentText
            pMember.text = currentText & pText.char[pIndex] & "_"
          -- otherwise each character in the text
          -- is concatenated with the text or field member
            pMember.text = pMember.text & pText.char[pIndex]
          end if
        end if
        -- check for sound flag, if no channel is chosen, nothing happens
        if pSoundChannel then
          -- kill sound in channel
          puppetsound pSoundChannel, 0
          -- play sound in specified sound channel
          puppetSound pSoundChannel, pSound
        end if
        -- update timer value
        pStartTime = the milliseconds
      end if
    end if
    -- Cursor modification to end of handler
    -- pActive is not TRUE (waiting to start, paused, or ended)
    if pCursor then -- blink the cursor
      vMillis = the milliseconds
      -- determine if enough time has elapsed since blink
      if (vMillis - pStartTime) > pPeriod * 3 then.
        currentText = pMember.text
        if the last char of currentText =  SPACE then
          delete the last char of currentText
          currentText = currentText & "_"
        else if the last char of currentText =  "_" then
          delete the last char of currentText
          currentText = currentText & SPACE
          currentText = currentTExt & SPACE
        end if
        pMember.text = currentText
        pStartTime = the milliseconds
      end if
    end if
  end if
end mType

That's the way it works. I know that's a lot to go through, but Darrel's original handler is so well coded that it should be a great learning experience to spend some time with it.

The additions that I made do one of two things. When pActive is TRUE and the letters are being typed on, I simply changed it so that the cursor is deleted, then the new letter is added, then the cursor is added to the end. So when the letters are being typed on, the cursor is not really blinking, it's just being appended to the string each time.

When pActive is FALSE, that means that we're waiting to start, paused, or finished. In that case, we want to have the cursor blink at the end of the string. That's a simple matter of testing to see what the last char of the string is. If it's a SPACE, then we change it to an underscore; else, it must already be an underscore and we change it to a SPACE. Pretty easy really, just a little tedious in the code.

Good luck with your program!

Patrick McClellan is Director Online's co-founder. Pat is Vice President, Managing Director for Jack Morton Worldwide, a global experiential marketing company. He is responsible for the San Francisco office, which helps major technology clients to develop marketing communications programs to reach enterprise and consumer audiences.

Copyright 1997-2019, Director Online. Article content copyright by respective authors.