QwertyTown: Keyboarding Lesson

QwertyTown is an online community that teaches kids to type. Our goal was to create an interactive experience that makes typing fun. The keyboarding lesson is a flash module where kids learn to type. The client gave us an outline with levels as main categories, then individual lessons for each level. Each lesson covers a specific key set the student needs to learn. We wrote the ActionScript and built the back-end architecture that makes the keyboarding lesson work.

Identifying Structure

One of the first key tasks in building the lesson module was to clearly identify the anatomy of a lesson. After working with the client we determined that every lesson had the following taxonomy: demonstration (or intro), the first guided practice, the second guided practice, and then finally, the independent practice. We called these "sections." Within each section there were a series of sequences that would drive what happens on screen. Maybe we want the onscreen guide to tell the student what key they need to press. This would be a sequence type of "GUIDE_SPEAK." This triggered the speech bubble on screen with dynamic text. If the student was required to press a key on screen it was a sequence type of "REQUIRED_KEY_PRESS." The code waits for that specific key to be pressed before continuing to the next sequence.
We created a framework that is driven by XML that allows each lesson to be totally customizable with set sections and sequence types. You can see the structure discussed above implemented in the XML's structure:

<br />
    <?xml version="1.0" encoding="UTF-8"?><br />
    <sections><br />
      <!-- SECTION HEADER --><br />
      <section type="INTRODUCTION"></p>
<p>        <!-- each sequence plays in order --><br />
        <sequence type="GUIDE_SPEAK"><br />
          <guidespeak><br />
            <![CDATA[In level two, you are going to learn the<br />
                     Upper Row.]]><br />
          </guidespeak><br />
        </sequence></p>
<p>        <sequence type="GUIDE_SPEAK"><br />
          <guidespeak><br />
            <![CDATA[Ready to practice some Upper Row keys?<br />
                     Cool. Let's go.]]><br />
          </guidespeak><br />
        </sequence></p>
<p>    </section><br />

Now the client has the power to craft the lessons into whatever format makes learning better for the students.

The KeyboardEvent

Capturing keyboard input was the next bear to tackle. To be honest, I was a little disappointed with the way Adobe has structured working with key input. There is not a unique index for each key on the keyboard. Some keys have the same Key code, while others return a 0 for the ASCII key code. You can check Adobe's documentation to see what I'm talking about. Instead you have to do a combo key on both the Key code and the ASCII key code in order to identify exactly what key was pressed, and even then it's not entirely accurate. You can get an idea of what type of data is returned with the KeyboardEvent with something like this:

<br />
stage.addEventListener(KeyboardEvent.KEY_DOWN,ReportKeyDown);<br />
    private function ReportKeyDown(event:KeyboardEvent):void {<br />
      trace("ReportKeyDown - "+ event.keyCode+" - "+event.charCode);<br />
    }<br />

One of the quirky bugs we ran into is that flash cannot tell the difference between either of the SHIFT keys on MAC. We worked around this by first detecting what platform the user is on, then looking at what key the user is expected to press, and making an educated guess. At the end of the project there was actually quite a bit of code written to handle "special" keys like shift, caps lock, tab and control.

Event Handling and Delegation

Once I had ActionScript capturing key input correctly, I moved on to parsing out events and delegating them to appropriate handlers. For example, if the current sequence is a "REQUIRED_KEYPRESS" and the student has pressed the correct key then:

<br />
    CustomEventManager.dispatchEvent(<br />
      new CustomEvent(<br />
        "showSpeechBubble",<br />
        {"speak":"Great! You pressed the correct key!"}<br />
      )<br />
    );<br />

The event is then handled by the GuideAvatar class, which shows the speech bubble and assigns the dynamic text. Event firing, handling, and delegation is handled like this throughout the entire framework.

Data Handling

We are still using JSON for data transfers. It's not quite as clean as AMF, but way better than XML. It's easy to convert JSON into something usable in ActionScript. Looping through XML to convert data into an ActionScript object is very intensive and time consuming. When the student has completed an independent practice we need to send their performance metrics back to drupal to be saved in the database. It looks something like this:

<br />
    public function SaveLessonPlay(action:String):void{<br />
      _urlsaveLoader = new URLLoader();<br />
      _urlsaveLoader.addEventListener(Event.COMPLETE,<br />
                                      SaveLessonPlayLoaded);<br />
      var request:URLRequest = new URLRequest(action);<br />
      request.method = URLRequestMethod.GET;</p>
<p>      urlsaveLoader.load(request);<br />
    }<br />

The call that comes back tells us how many QwertyCoins the player has been awarded:
<br />
    private function SaveLessonPlayLoaded(e:Event):void{<br />
      //convert external data to array<br />
      var d:Object=JSON.decode(e.target.data);<br />
      if(d==-1){<br />
        //error occurred<br />
        CustomEventManager.dispatchEvent(<br />
          new CustomEvent(<br />
            "errorOccurred",<br />
            {"message":"An error occurred while trying to save.",<br />
             "button":"gobacktodashboard"}<br />
          )<br />
        );<br />
      }else{<br />
        if(d.hasOwnProperty("qwertycoins")){<br />
          //fire event<br />
          CustomEventManager.dispatchEvent(<br />
            new CustomEvent("gamePlayDataLoadComplete",d)<br />
          );</p>
<p>          if(d.qwertycoins>0){<br />
            //fire off external interface call<br />
            var isAvailable:Boolean = ExternalInterface.available;<br />
            if(isAvailable){<br />
              ExternalInterface.call("updateQwertyCoins",d.qwertycoins);<br />
            }<br />
          }<br />
        }<br />
      }<br />
    }<br />

We take that data and update internally, then kick it out to JavaScript with the ExternalInterface call. That updates the HTML that sits around the flash module on the web page.

We Did it For the Kids

We believe that children are our future, and when we sit back and look and what we've made with QwertyTown we are really excited. Kids are really liking it. It's nice to see that all the behind-the-scenes coding we do materializes into a solution that teaches them well. One very cool project. One very cool client. If you are interested in getting QwertyTown brought to your school, check their information page.
Also, if you haven't read Jason Yee's post on Node.js, you should really check it out. Node.js is ground breaking technology. We used it to build the Chatterbox feature for QwertyTown.

Usability User Experience

Read This Next