back to uSimpleChat tutorial home  back to  
unify the web

unity 2 uSimpleChat tutorial, actionscript 1.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 text field for displaying messages, a text field for user input, and a button for sending messages.

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/uSimpleChatMX/v1/uSimpleChat.fla and open it in Flash (version MX or later).

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

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

Let's consider the these states individually.

App State 1: Loading uClientCore.swf

The first version of uSimpleChat.fla, like all Flash-based Unity applications, starts off by loading uClientCore.swf. The uClientCore.swf file contains the uClientCore classes, which provide the ActionScript services and tools needed to create rooms, communicate with other clients, and store client and room information in Unity applications.

We use loadMovie() to load the uClientCore.swf file into a runtime-created empty movie clip named uclientcore. But before we load the uClientCore.swf file, we define a callback function--onUClientCoreLoaded()--invoked automatically when uClientCore.swf finishes loading. Here's the code on frame 2 of uSimpleChat.fla's main timeline:

// Respond to uClientCore loading.
function onUClientCoreLoaded() {
  trace("UClientCore loaded.");
  this.coreLoaded = true;
  this.gotoAndPlay("mainLoadLoop");
}

// Load Unity's uClientCore library.
this.coreLoaded = false;
this.createEmptyMovieClip("uclientcore", 999991);
uclientcore.loadMovie("../../../uClientCore.swf");

While uClientCore.swf loads, the playhead loops between frames 3 and 4, which contain preloading code to displays load progress. When uClientCore.swf finishes loading, onUClientCoreLoaded() executes, and the playhead jumps to "mainLoadLoop", which starts preloading the uSimpleChat.swf movie itself.

Note that the depth 999991 in the above code is arbitrary. You can create the empty uclientcore clip on whatever depth you like--just be sure it doesn't conflict with other depths in your application.

App State 2: Loading uSimpleChat.swf

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

// CODE ON FRAME 17
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.

App State 3: 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 #include the source code for the USimpleChat class, then we make a USimpleChat instance, then we connect to Unity through that instance.

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

// Load application classes.
#include "USimpleChat.as"

// Create USimpleChat instance. 
var sc = new org.moock.unity.simplechat.v1.USimpleChat(this,
                                                       "localhost",
                                                       9100, 
                                                       null,
                                                       true);

// Connect to Unity.
sc.connect();

// Wait here. When connection succeeds, the application
// will automatically 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 4: Application Active

When the uSimpleChat application 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 elements when a message is sent or received. To give the send_pb button access to the USimpleChat instance, we add a single line of code to the scripts layer of the "simpleChatInterface" frame:

send_pb.setClickHandler("sendMessage", sc);

which means, literally, "call the sendMessage() method of the object sc when the send_pb button is pressed". As we'll see in the next section, the sendMessage() method transmits the text in outgoing_txt 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 USimpleChat.as source file in it's entirety. Scan it, then skip down to the explanation that follows the code listing.

// PACKAGE
org.moock.unity.simplechat = new Object();
org.moock.unity.simplechat.v1 = new Object();

// CLASS USimpleChat EXTENDS UClient
org.moock.unity.simplechat.v1.USimpleChat = function (target, 
                          host, 
                          port, 
                          configURL, 
                          disableLog) {
    // Invoke UClient constructor.
    super(target, host, port, configURL, disableLog);
}

// EXTEND SUPERCLASS
org.moock.unity.simplechat.v1.USimpleChat.prototype 
                                             = new org.moock.unity.UClient();

// METHODS
  /**
   * UClient event handler
   */
  org.moock.unity.simplechat.v1.USimpleChat.prototype.onClientReady 
                                                             = function () {
    // Display the user interface for this app.
    this.getTargetMC().gotoAndStop("simpleChatInterface");
  }

  /**
   * Takes user input from outgoing_txt and sends it to all clients
   * in the room "udefault.chat".
   */
  org.moock.unity.simplechat.v1.USimpleChat.prototype.sendMessage 
                                                               = function () {
    // Only send the message if there's text
    // in the outgoing_txt text field.
    if (this.getTargetMC().outgoing_txt.text.length > 0) {
      // The message typed by the user.
      var msg = this.getTargetMC().outgoing_txt.text;

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

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

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

  /**
   * Displays text sent by a client.
   */
  org.moock.unity.simplechat.v1.USimpleChat.prototype.displayMessage
                                                  = function (clientID, msg) {
    this.getTargetMC().incoming_txt.text += "User"
                                         + clientID + ": " + msg + "\n";

    // Scroll the incoming_txt text field to the bottom.
    this.getTargetMC().incoming_txt.scroll = this.getTargetMC().incoming_txt.maxscroll;
  }

Our first task is to define the object on which we'll store the USimpleChat class: org.moock.unity.simplechat.v1. The uClientCore.swf file creates the object hierarchy org.moock.unity (on which all uClientCore classes are stored). We must add simplechat.v1 to that existing structure, as follows:

// PACKAGE
org.moock.unity.simplechat = new Object();
org.moock.unity.simplechat.v1 = new Object();

By storing USimpleChat on org.moock.unity.simplechat.v1, we avoid potential name conflicts and keep our class hierarchy organized. If you prefer not to store your classes on objects in this way, you are not obliged to do so (nothing in uClientCore requires your custom classes to be defined on a simulated package object). For more information on simulating packages in Flash MX, consult slide 4 of my Application Developer's ActionScript Workshop.

Next we define the USimpleChat class constructor (in ActionScript, defining the constructor also defines the class). 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:

// CLASS USimpleChat EXTENDS UClient
org.moock.unity.simplechat.v1.USimpleChat = function (target, 
                          host, 
                          port, 
                          configURL, 
                          disableLog) {
    // 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:
org.moock.unity.simplechat.v1.USimpleChat.prototype.onClientReady 
                                                           = function () {
  this.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:

  this.getTargetMC().gotoAndStop("simpleChatInterface");

Notice how the USimpleChat class accesses the main timeline of our movie: it uses the UClient.getTargetMC() method, 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:

org.moock.unity.simplechat.v1.USimpleChat.prototype.sendMessage 
                                                             = function () {
  if (this.getTargetMC().outgoing_txt.text.length > 0) {
    var msg = this.getTargetMC().outgoing_txt.text;
    var safeMsg = '<![CDATA[' + msg + ']]>';
    this.invokeOnRoom("displayMessage", "udefault.chat", true, safeMsg);
    this.getTargetMC().outgoing_txt.text = "";
  }
}

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

if (this.getTargetMC().outgoing_txt.text.length > 0) {
...
}

The message to send is the text in the outgoing_txt text field:

var msg = this.getTargetMC().outgoing_txt.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 = '<![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:

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

which says, literally: "Unity: please tell all clients in the room 'udefault.chat' to invoke the method 'displayMessage()' on the UClient subclass instance receiving messages from the server. Please pass the argument 'safeMsg' to that method." In our case, the "UClient subclass instance receiving messages" is the USimpleChat instance we created on the frame labeled main.

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.

org.moock.unity.simplechat.v1.USimpleChat.prototype.displayMessage
                                                = function (clientID, msg) {
  this.getTargetMC().incoming_txt.text += "User"
                                       + clientID + ": " + msg + "\n";
  this.getTargetMC().incoming_txt.scroll = this.getTargetMC().incoming_txt.maxscroll;
}

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 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. If your uSimpleChat client doesn't connect, see the Unity Troubleshooting Guide.

C'est tout!

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



Documentation Version

1.0.7