back to uSimpleChat (actionscript 2 version) home  back to  
unify the web

Unity 2 uSimpleChat Tutorial, ActionScript 2.0 Version

PART 1

The first version of uSimpleChat will show how to:

Below is a screenshot of the uSimpleChat user interface. It includes a TextArea component for displaying messages, a TextInput for user input, and a Button component for sending messages. All components user interface are from Macromedia's "v2" component set that ships with Flash MX 2004. The .swf file in this example is exported in Flash Player 7 format, but could also be exported to Flash Player 6 format. Macromedia's v2 components require Flash Player 6.0.79.0 or higher, but most of them actually work in older versions of Flash Player 6. For true Flash Player 6 compatibility, see the ActionScript 1.0 version of this tutorial.

Create the Application Room

When the uSimpleChat client connects to Unity, it must join a room in order to communicate with other users. Each application defines its own room (or rooms) that will contain its users. And each room itself must be placed in a namespace, which is simply a named collection of rooms (namespaces contain rooms much the same way directories on a file system contain files). By default, Unity provides a generic namespace, 'udefault', which can hold rooms for any application. In this version of uSimpleChat, we'll create our application room in the 'udefault' namespace. To create the room, we'll modify the server's uconfig.xml file, which lists the rooms that should be made available when the server starts.

Follow these steps to create the room 'udefault.chat' on your server.

  1. If Unity is currently running, stop it. (Rooms specified in uconfig.xml are loaded at startup. We'll learn how to create rooms from Flash at runtime in parts 2, 3, and 4 of this tutorial.)
  2. In your Unity installation directory, edit the file uconfig.xml.
  3. Add the following XML code to the <INSTANCES> tag of uconfig.xml:
    <ROOM>
      <ID>chat</ID>
      <AUTOJOIN>true</AUTOJOIN>
    </ROOM>
  4. Save uconfig.xml.
  5. Start Unity. If you are unfamiliar with starting Unity, refer to the server's Installation and Use documentation (in the file /docs/server/install.html of your Unity server directory).

When Unity starts, it will now automatically create a room called 'chat' in the 'udefault' namespace. Note that because we did not supply a namespace for the room, Unity automatically places the room in 'udefault'. If we were so inclined, we could have supplied the namespace explicitly via the <NAMESPACE> tag, as in:

<ROOM>
  <NAMESPACE>udefault</NAMESPACE>
  <ID>chat</ID>
  <AUTOJOIN>true</AUTOJOIN>
</ROOM>

Any namespace specified is automatically created when Unity starts.

Notice that the <ROOM> tag from step 3, above, includes an <AUTOJOIN> tag with a value of true. This forces every client that connects to the server to automatically join the room 'chat'. As we'll see in part 2 of this tutorial, clients can also use ActionScript to join rooms. (See also: URoom.join() and UClient.joinRoom() in the uClientCore API documentation.)

An Overview of uSimpleChat.fla

Now that the Unity server is started with the room 'udefault.chat' deployed, let's take a look at the Flash client application that will connect to the server and join that room.

Locate the file /uClient/uSimpleChat/v1/source/uSimpleChat.fla and open it in Flash (version MX 2004 or later).

Adding the uClientCore Component to uSimpleChat.fla

All Flash-based Unity applications must include uClientCore, either by loading it at runtime or by adding it at author time as a component. The uClientCore class library provides the ActionScript services and tools needed to create rooms, communicate with other clients, and store client and room information in Unity applications. In uSimpleChat, we'll use the component version of uClientCore. The uSimpleChat sample files already include the uClientCore component. If you want to add the uClientCore component to a new .fla file, you can either drag it from uSimpleChat.fla to the new file, or you can follow the directions in the Application Developer's Guide for adding the uClientCore component to the Components panel in Flash MX 2004.

Application States in uSimpleChat.fla

The main timeline of uSimpleChat.fla reflects the general structure of our application. It uses frame labels to represent the application's 3 states:

  1. application loading ("mainLoadLoop")
  2. application startup ("main")
  3. application active ("simpleChatInterface")

Let's consider the these states individually.

App State 1: Preloading the Movie

Our application's main preloader starts on frame 2. In our case, the application should be entirely loaded before it starts up, so the preloader code is as follows:
// CODE ON FRAME 2
if (this._framesloaded < this._totalframes) {
  this.loaded_txt.text = Math.floor(100 * (this.getBytesLoaded()/this.getBytesTotal())) + "%";
} else {
  this.gotoAndStop("main");
}

// CODE ON FRAME 3
this.gotoAndPlay("mainLoadLoop");
Pretty standard stuff. When the movie finishes loading, the playhead advances to the frame labeled "main", where the application starts and attempts to connect to the server. However, when working with ActionScript 2.0 classes and v2 components, we must make the following special arrangements in uSimpleChat.fla to ensure that the classes and components preload properly:

App State 2: Application Startup

All Unity applications consist of at least one UClient subclass. The UClient subclass provides the foundation of the application. It connects to the server, receives and sends data, and provides access to the application's RoomManager and RemoteClientManager. Our application's UClient subclass is named USimpleChat; it happens to be the only class in the application for now. The USimpleChat class implements three methods: one for sending messages (sendMessage()), one for receiving messages (displayMessage()) and one for displaying the simpleChatInterface frame once the client is connected and intialized (onClientReady()).

The USimpleChat class is defined on the frame labeled "main", after all preloading is done. On the main frame, we make a USimpleChat instance, then we connect to Unity through that instance.

Here's the code on the frame labeled "main":

// Create UClient instance and connect to Unity.
import org.moock.unity.simplechat.v1.USimpleChat;
var sc:USimpleChat = new USimpleChat(this, "localhost", 9100, null, true);
sc.connect();

// Wait here. When connection succeeds, the application
// will proceed to the frame labeled "simpleChatInterface".
stop();

Note that the connect() method can be used on our USimpleChat instance ("sc") because USimpleChat inherits that method from UClient. All methods defined by UClient (listed in the documenation) can be invoked on the USimpleChat instance.

The arguments passed to the USimpleChat constructor initialize our Unity client, as follows:

The bulk of our application's logic lives in the USimpleChat class. But before we examine that class, let's consider the user interface that it controls, which resides on the frame labeled "simpleChatInterface".

App State 3: Application Active

When the USimpleChat class receives notice that a connection to Unity has been established, it displays the "simpleChatInterface" frame. On that frame, three UI elements make up the basis of our chat:

The USimpleChat instance that we created on the frame labeled "main" interacts with the above UI components when a message is sent or received. To give make the send button invoke USimpleChat.sendMessage() when clicked, we three lines of code to the scripts layer of the "simpleChatInterface" frame:

send.clickHandler = function (e:Object):Void {
  sc.sendMessage();
}

As we'll see in the next section, the sendMessage() method transmits the text in outgoing to all clients in the room udefault.chat.

Our tour of the uSimpleChat application is almost done. All that's left to explore is the USimpleChat class.

The USimpleChat Class

USimpleChat, a UClient subclass, is the "meat and potatoes" of our application. It connects to the server, handles the connection completion, sends messages to clients, and receives messages from clients. We'll walk through the code for USimpleChat very closely here, bearing in mind that this is the first, simplest version of our chat application.

Here's a look at the source code for USimpleChat in it's entirety. The code for USimpleChat resides in the file /uClient/uSimpleChat/v1/source/org/moock/unity/simplechat/v1/USimpleChat.as. Scan it, then skip down to the explanation that follows the code listing.

import org.moock.unity.*;

class org.moock.unity.simplechat.v1.USimpleChat
      extends UClient {

  /**
   * Constructor
   */
  public function USimpleChat (target:MovieClip, 
                          host:String, 
                          port:Number, 
                          configURL:String, 
                          disableLog:Boolean) {
    // Invoke UClient constructor.
    super(target, host, port, configURL, disableLog);
  }

  /**
   * UClient event handler
   */
  public function onClientReady ():Void {
    // Display the user interface for this app.
    getTargetMC().gotoAndStop("simpleChatInterface");
  }

  /**
   * Takes user input from outgoing and sends it to all clients
   * in the room "udefault.chat".
   */
  public function sendMessage ():Void {
    // Only send the message if there's text
    // in the outgoing text field.
    if (getTargetMC().outgoing.text.length > 0) {
      // The message typed by the user.
      var msg:String = getTargetMC().outgoing.text;

      // The message we'll send to the server.
      var safeMsg:String = '<![CDATA[' + msg + ']]>';

      // Send the message to the server.
      invokeOnRoom("displayMessage", "udefault.chat", true, safeMsg);

      // Clear the user input text field.
      getTargetMC().outgoing.text = "";
    }
  }

  /**
   * Displays text sent by a client.
   */
  public function displayMessage (clientID:String, msg:String):Void {
    getTargetMC().incoming.text += "User" + clientID + ": " + msg + "\n";
    getTargetMC().incoming.vPosition = getTargetMC().incoming.maxVPosition;
  }
}

THe first task in USimpleChat is to define the class constructor. The constructor specifies a parameter list matching that of its superclass, UClient. In uSimpleChat version 1, the constructor's sole duty is to pass its arguments on to its superclass's constructor, using the super operator. (Recall that the argument values were provided when we instantiated USimpleChat on the frame labeled "main").

Here is the USimpleChat constructor function:

public function USimpleChat (target:MovieClip, 
                          host:String, 
                          port:Number, 
                          configURL:String, 
                          disableLog:Boolean) {
  // Invoke UClient constructor.
  super(target, host, port, configURL, disableLog);
}

The USimpleChat class has no properties and defines only three methods: onClientReady(), sendMessage(), and displayMessage().

The first method, onClientReady(), is invoked automatically after a connection has been established with the Unity server and the client has been fully initialized. Here's what it looks like:

public function onClientReady ():Void {
    // Display the user interface for this app.
    getTargetMC().gotoAndStop("simpleChatInterface");
  }

The code in onClientReady(), defines what the application should do when it connects successfully and is ready to communicate with the server. In our case, we know that the client will automatically join the room 'udefault.chat' when it connects (remember uconfig.xml?). So all we have to do in onClientReady() is display the frame labeled "simpleChatInterface", which contains our application's "active" interface:

  getTargetMC().gotoAndStop("simpleChatInterface");

Notice that the USimpleChat class accesses the main timeline of our movie via UClient.getTargetMC(), which returns a reference to the movie clip specified as the first argument to the USimpleChat constructor.

The next method in USimpleChat--sendMessage()--sends a message to all clients currently in the room 'udefault.chat'. Here's the code:

public function sendMessage ():Void {
  if (getTargetMC().outgoing.text.length > 0) {
    var msg:String = getTargetMC().outgoing.text;
    var safeMsg:String = '<![CDATA[' + msg + ']]>';
    invokeOnRoom("displayMessage", "udefault.chat", true, safeMsg);
    getTargetMC().outgoing.text = "";
  }
}

The sendMessage() method sends a message only when there's at least one character in the outgoing text field:

if (getTargetMC().outgoing.text.length > 0) {
...
}

The message to send is the text in the outgoing TextInput component:

var msg:String = getTargetMC().outgoing.text;

But we can't just send that message in its raw format because it will confuse the XML parser (and be discarded) if it contains tag characters such as "<". Hence, we wrap the message in a CDATA section for safe transmission:

var safeMsg:String = '<![CDATA[' + msg + ']]>';

Then the magic moment is upon us...we're going to send something to other clients! In this case, we want to invoke the method USimpleChat.displayMessage() on all clients in the room "udefault.chat". To do so, we use the UClient.invokeOnRoom() method, as follows:

invokeOnRoom("displayMessage", "udefault.chat", true, safeMsg);

which says, literally: "Unity: please tell all clients in the room 'udefault.chat' to invoke their 'displayMessage()' method. Please pass the argument 'safeMsg' to that method."

Here, then, is the displayMessage() method that is invoked remotely by sendMessage(). It prints the message sent and the id of the client that sent it.

public function displayMessage (clientID:String, msg:String):Void {
  getTargetMC().incoming.text += "User" + clientID + ": " + msg + "\n";
  getTargetMC().incoming.vPosition = getTargetMC().incoming.maxVPosition;
}

All remotely invoked methods are passed the client id of the client that invoked them. Hence, our displayMessage() method's first argument is, by necessity, clientID. Its second argument, msg, receives the value of safeMsg, which we passed as the fourth argument to invokeOnRoom(). (If we had wanted to pass more arguments, we would simply have listed them after safeMsg).

With our displayMessage() and sendMessage() methods defined we can send a text message from one client to another and have it appear on screen. And that, my friends, is what chat is all about. Try exporting a .swf file from uSimpleChat.fla and running it. If you have Unity running and have followed instructions in this tutorial correctly, you should be able to type into the bottom text field, press the "Send Message" button, and see your text show up in the top text field.

C'est tout!

Well that about does it for uSimpleChat version 1! Ready for more? Head on to part 2...



Documentation Version

1.0.1