This user is yet to take control of their account and provide a biography. If you are the author of this article, please contact us via support AT actionscript DOT org. Summary: Using a simple video game level editor in Flash as a foundation, this whitepaper/tutorial explains how to use ActionScript, PHP, and the PHPObject "Flash remoting" libraries to save and retrieve data to/from a server in XML format.
Contents
Introduction
Video games are frequently tile-based. A “tile-based” game uses a library of preset graphical objects to display the game’s interface. One tile might contain graphics for a patch of grass, while another could illustrate a square of cement sidewalk. Tiles are designed to seamlessly mesh with surrounding tiles, so that a large and varied gamespace can be created from a handful of tiles. Popular games like SimCity and Warcraft are good examples, though most side-scrollers and first-person-shooters are also usually tiled. Tile-based games also lend themselves to level editors, graphical interfaces that allow game designers to place game elements on an actual level, rather than trying to create the level arrays with hand-typed text. This also allows players to create their own levels. These levels need to be saved somewhere for designers to retrieve and edit.
This whitepaper introduces a simple game level editor in Flash, using Ghostwire's free PHPObject libraries to do "Flash remoting" to save and retrieve data to/from a server. The official definition of Flash remoting is that it enables "a powerful yet simple programming model and runtime enabling Macromedia Flash MX to connect to remote services exposed by application servers and web services". In other words, Flash can now talk to your server without having to encode and decode variables through loadVariables() or XML.sendAndLoad(); your ActionScript and your server-side language can simply refer to the same sets of objects. Specifically, PHPObject allows Flash and PHP to share the same object class as if it was native to the code.
You can play with this level editor on my web site. It is not for a particular game, but serves as a good vehicle for explaining Flash remoting. In this level editor, there are seven possible tile shapes:
![]()
These represent open space, treasure, obstacles, walls and the player. The level editor provides a grid of tiles, 20 on each side, for a total of 400 tiles. After choosing a shape from the toolbox, you can click on individual tiles to change them to that shape. You could end up with a level like this:
This level could then be read into a game engine and rendered onscreen as a birds-eye game (like Pac-man), an isometric game (like Warcraft) or something else, using far more detailed tiles than those represented in the editor.
2. Getting Started
What You Need
The Flash file is "leditor.fla", and the PHP class file is "Leditor.php", both available in this downloadable archive (Leditor.zip, 168k). The Flash file requires Flash MX 2004, and uses some components from Flash MX 2004 Pro.
To install PHPObject, you need to install the free Macromedia Extensions Manager.
You also need Ghostwire's free PHPObjectFlash remoting libraries. As of this writing, the latest version is v1.45. Newer versions may or may not work the same. Once you have downloaded and expanded the PHPObject ZIP archive, double-click on the file "PHPObject.mxp". This will launch the Extensions Manager and walk you through the process of installing the relevant Flash libraries
You also need a web browser with a recent copy of the Flash plug-in. Mac users, note that versions of Apple's Safari browser prior to 1.2 do not work correctly with Flash remoting, although Internet Explorer 5 and Netscape 7 work fine.
To test out the sample level editor, you also need a web server that has PHP4. The examples in this document use a Mac OS X system, with the computer itself as the web server ("localhost"). However, any web-accessible server should work. You will need to be able to put files in a directory (via ftp) and access them from a web browser (via http://). Create a directory on your web server (such as "leditor"). (Depending on how your server is configured, you may need to change the file permissions of your directory to allow everyone to write to it. Most FTP and SFTP programs offer simple ways of setting these permissions.)
Finally, open up the "leditor.fla" file in Flash MX 2004. Click on the first frame of the first layer and look at the ActionScript in that frame. Near the top, you will see this line:
[as]_global.defaultGatewayDir = "http://localhost/leditor/";
[/as]
The second half of this line is the web URL to the directory you created on the server. If this is not running on your local computer ("localhost"), then change it to the actual URL of the directory you just created (such as "http://a.parsons.edu/~paul/leditor/"). Then, go into Flash's "Publish Settings" and make sure it is set to create Flash Player 6 movies (not Flash Player 7!) and publish the movie to the file "leditor.swf".
Setting Up the Game Editor
To run the level editor, copy the following files into the directory you created on your web server:
- leditor.swf which you just published from Flash, above.
- index.html from the leditor download.
- Sample_Level.xml from the leditor download.
- Leditor.php from the leditor download.
- Gateway.php from the PHPObject download (in its "server" folder).
- config.php from the PHPObject download (in its "server" folder).
To test it, pull up your leditor directory in your web browser. You should see something like this:
If you've gotten this far, great! You can open the sample level, or use the level objects to draw your own level and save it to the server.
3. The Concepts of PHPObject
Using Objects in Flash
If you are familiar with Flash's ActionScript language, you already understand some things about objects (in the programming sense of the word). For example, you may have created a movie clip and given it the instance name "myTitleSequence". To tell it to go to frame 54, you might write this:
[as]// ActionScript
myTitleSequence.gotoAndPlay(54);
[/as]
In this case, "myTitleSequence" is being treated as an object, and "gotoAndPlay" is a function or method of that object. To move the movie clip to a different location on the screen, you might write this:
[as]// ActionScript
myTitleSequence._x = 200;
myTitleSequence._y = myTitleSequence._x + 100;
[/as]
In this case, "_x" and "_y" are propertiesof the object. Flash uses these properties to position this movie – in this case, moving its origin point to coordinate (200,300).
Just about everything in Flash is treated as an object, from movie clips to text boxes. You can create also create your own generic objects and give them their own properties and functions, like this:
[as]// ActionScript
// Create object
var myIceCream = new Object;
// Add some properties
myIceCream.chocolate = "none";
myIceCream.strawberry = "full";
myIceCream.vanilla = "half";
// Add a function
myIceCream.checkStatus = function() {
if (this.chocolate == "none") trace("Buy more chocolate!");
if (this.strawberry == "none") trace("Buy more strawberry!");
if (this.vanilla == "none") trace("Buy more vanilla!");
}
[/as]
Elsewhere in your ActionScript, you could refer to "myIceCream.checkStatus()" to determine which kind of ice cream you need to buy. Because this function is part of the "myIceCream" object, it can use the special variable "this" to refer to its own properties.
Using the PHPObject
PHPObject relies on a kind of "shared object" between ActionScript and PHP. This is a special object that exists in Flash and in PHP, with identical or complementary properties and functions. For the "leditor" game editor, the ActionScript for this object resembles this:
[as]// ActionScript
// Create object
myLeditor = new PHPObject("Leditor");
// Add a property
myLeditor.localFiles = new Array();
// Add some functions
myLeditor.onInit = function() { }
myLeditor.onAbort = function() { }
myLeditor.onResult = function() { }
myLeditor.levelSave_onResult = function() { }
myLeditor.levelListing_onResult = function() { }
myLeditor.levelDelete_onResult = function() { }
[/as]
In this code, there is only one property, "localFiles". Shortly, it will become an array of file names to be displayed in the file listing box.
When the object is created, the PHPObject refers to "Leditor". This tells the PHPObject that there is a PHP file on the server by the same name, "Leditor.php", with a PHP class inside it called "Leditor". That PHP file contains code like this:
[as]// PHP
class Leditor {
function Leditor() { }
function init() { }
function levelSave ($filename, $gameArray) { }
function levelListing() { }
function levelDelete($filename) { }
}
[/as]
(The PHP inside these functions has been removed for brevity.)
With PHPObject, these properties and functions are fully accessible through the myLeditor object from both ActionScript and PHP. Thus, calling the PHP function "levelSave" from ActionScript is as simple as this:
[as]// ActionScript
myLeditor.levelDelete(levelName);
[/as]
The PHP code for the "levelDelete" function will then execute on the server, using the ActionScript variable "levelName" passed to the PHP variable "$filename". PHP can also operate directly on the Flash object properties, and vice versa. Earlier, we gave the myLeditor object this array in ActionScript:
[as]// ActionScript
myLeditor.localFiles = new Array();
[/as]
The PHP function "levelListing" can manipulate this array directly, like this:
[as]// ActionScript
myLeditor.localFiles = new Array();
[/as]
The thing called "localFiles" is the same array of information, shared between the Flash object and the PHP object. Where Flash uses "this.localFiles" inside a myLeditor function, PHP uses "$this->localFiles" to handle the very same array.
Reacting to PHP Execution
When a PHP function finishes executing, it notifies one of the Flash functions associated with the PHPObject. This way, Flash knows when data has been updated, and can modify the display as needed. For Leditor, the connections are as follows:
| When this PHP function finishes... | ...it notifies this Flash function. |
| function init() { } | myLeditor.onInit = function() { } |
| (when the PHP is interrupted) | myLeditor.onAbort = function() { } |
| (for all PHP functions) | myLeditor.onResult = function() { } |
| function levelSave () { } | myLeditor.levelSave_onResult = function() { } |
| function levelListing() { } | myLeditor.levelListing_onResult = function() { } |
| function levelDelete() { } | myLeditor.levelDelete_onResult = function() { } |
The "onInit" and "onAbort" functions are required parts of PHPObject, responding to the initialization and interruption of PHPObject objects.
Look at the four "onResult" functions. The last three specifically have the same names as the PHP functions that notify them; e.g. "levelSave()" notifies "levelSave_onResult". The first "onResult" function is generic, and is only notified after any PHP function that does not have a specifically-named result function. This allows you to handle some functions generically, and handle other functions very specifically. Our Leditor object does not actually use the generic onResult function, opting to have entirely customized functions instead.
Doing Things In Order
When referring to PHP functions from Flash, you must wait for one function to finish before beginning the next function, or the second one will not work. PHPObject functions operate serially. For example, this code will not work:
[as]// ActionScript
myLeditor.levelSave();
myLeditor.levelListing();
[/as]
The second function will never execute. It will be sent to the object before the first one has finished, which is impossible for PHP to handle properly.
There are two solutions for this. If you know that a specific function is always followed by another specific function, you can "chain" the two together through the "onResult" functions in Flash. For example:
[as]// ActionScript
myLeditor.levelSave_onResult = function() {
myLeditor.levelListing();
}
myLeditor.levelListing_onResult = function() {
// some other code
}
myLeditor.levelSave();
[/as]
With this code, Flash calls the "levelSave" function, which then notifies Flash to execute the "levelSave_onResult" function, which then calls the "levelListing" function, which then notifies Flash to execute the "levelListing_onResult" function. Thus, through the prescribed chain of PHPObject events, you can execute a series of functions.
However, this solution only works when the second function always follows the first. This is frequently not true; you may only want the second function to execute in certain circumstances. PHPObject offers a way of storing up a series of commands so that they execute sequentially. By adding the "delayExecute()" and "execute()" functions, the above code can be made to work as follows:
[as]// ActionScript
myLeditor.delayExecute();
myLeditor.levelSave();
myLeditor.levelListing();
myLeditor.execute();
[/as]
The "delayExecute()" command tells PHPObject to queue all the commands that follow, not executing them until it gets an "execute()" command. By encapsulating the PHPObject functions this way, you have a large amount of control over the sequence of functions.
4. How Leditor Works
The Logic of Leditor
Leditor relies on the ActionScript of "leditor.fla" (in the first frame) and the PHP in "Leditor.php". ActionScript hands off to PHP, which hands back, and on and on. Dissecting these files will reveal the symbiosis between them. You should look at the code as you read these notes.
However, before we get into the code, let's review the editor itself. There are seven possible tile shapes in this game editor:
![]()
Respectively, these are: a blank tile, representing open space; a blue rectangle tile, perhaps representing a diamond; a yellow circle tile, perhaps representing a coin; a dark purple star tile, perhaps representing an explosive mine or monster; a red brick wall and a yellow brick wall, representing walls that cannot be walked through; and finally a silver ball, representing the player.
The level editor provides a grid of tiles, 20 on each side, for a total of 400 tiles. After choosing a shape from the toolbox on the left, you can click on individual tiles to change them to that shape. All of the shapes are individually contained in frames 1 through 7 of the "tile" movie clip. Thus, to change a tile's shape, the ActionScript simply tells it to go to another frame; for example, to change a tile to a red brick wall, the tile's movie clip is told to go to frame 5. You could end up with a level like the one in sample_level.xml:
The level editor allows you to save levels. It does this by scanning through every tile movie clip in the grid, recording its current frame, and saving this information to a two-dimensional array. This array is then passed to the server via PHPObject, where PHP saves it as an XML file.
The level editor also allows you to open or delete previously-saved levels. PHP code on the server reads the list of available XML files and passes this information to Flash via an array. Flash then updates its file list. To open a file, Flash uses standard ActionScript XML loading functions; to delete a file, Flash uses PHPObject again.
Analyzing the PHP
The PHP file "Leditor.php" contains one main thing: a PHP class called "Leditor". This class name must match the file name, and both of these must match the ActionScript reference that creates the PHPObject (see below). Within this class are five functions.
The first two functions in Leditor.php are Leditor() and init(). The former simply contains a reference to the latter, and the latter contains any initialization functions. For this application, no PHP-related initialization is used. Other applications might verify database connections or security authorizations at this point. (These are required functions for every PHPObject, and the first function must match the name of the PHP class.)
The third PHP function is levelSave(), which takes two arguments: the string $filename and the two-dimensional array $gameArray, which contains the level's tile information. The function iterates through the array, building a long string of XML tags into the internal variable $saveVal. Then, it makes sure $filenamehas an ".xml" suffix, and saves the string into a file with that name (overwriting any existing data). Once completed, PHPObject notifies the Flash function myLeditor.levelSave_onResult(), which updates the interface accordingly.
The fourth PHP function is levelListing(), which takes no arguments. It simply opens the local directory, reads in each file name, and adds it to the Leditor array $this->localFilesif its file suffix is "xml". Then it closes the directory and sorts the array. Once completed, PHPObject notifies the Flash function myLeditor.levelListing_onResult(), which accesses this array via myLeditor.localFiles and fills the file list box with the array's contents.
The fifth PHP function is levelDelete(), which takes one argument: the string $filename. If the suffix of $filename is "xml", then the function deletes it. Once completed, PHPObject notifies the Flash function myLeditor.levelDelete_onResult(), which in turn calls the PHP function levelListing() to refresh the file list, as above.
Analyzing the ActionScript
The ActionScript code is marked off into different sections with lines of commented asterisks and appropriate labels. We'll follow that organization in this discussion.
Initialization and declarations
The first section of ActionScript begins with this command:
[as]// ActionScript
#include "PHPObject.as"[/as]
This tells Flash to utilize the PHPObject libraries that you installed via the Extensions Manager. Without this, none of the PHPObject features will work. The rest of the commands in this section set up two new objects, level and tool, with various properties about the drawing grid and the drawing tool selections. The maximum number of level files is set in fileMaxQuantity(to 50). The final ActionScript sets an XML property in Flash, telling it to ignore white space (spaces and returns) in XML text files.
PHPObject Declarations
This section contains all of the variables and functions related to PHPObject. It begins by setting globals that tell PHPObject where to look for the "Gateway.php" file (which is necessary to communicate with PHP) and the password to use when communicating with the server (which must match the password in the "config.php" file on the server).
Next, it creates the myLeditor object:
[as]// ActionScript
myLeditor = new PHPObject("Leditor");
[/as]
This tells Flash to create a new object out of the PHPObject code, and tells PHPObject to link this object to the "Leditor.php" file on the server.
The remaining ActionScript defines the response functions for myLeditor, such as:
[as]// ActionScript
// Handle results from levelListing() PHP function
myLeditor.levelListing_onResult = function() {
// Fill the list box with array contents
_root.files_available.dataProvider = this.localFiles;
}
[/as]
This particular function is executed after the PHP function levelListing()has completed. This and the other response functions were discussed earlier in general. They are uncomplicated and well-commented, so read the code.
Grid Drawing Functions
This section contains all of the functions used to display the 20x20 tile grid. The first function is tileName(), which accepts two arguments: a reference to a column and a row. It returns a standardized name as a string. Thus, any place a tile move clip needs to be adjusted by name, that name can be derived from the tile's column and row by using this function.
The second function is drawGridFoundation(), which accepts no arguments, and simply initializes the blank 20x20 grid. It contains nested "for" loops to count through the rows and columns, tile by tile. For each tile, it determines a unique tile name (using tileName(), above), then takes a movie clip called "tile" from the library and attaches it to the empty movie clip instance called "origin" on the main timeline. (The tile's movie clip is called "grid tile" in the library list; however, if you select that item in the library and choose "Linkage" from the library menu, you'll see that its ActionScript name is "tile".) The function positions each "tile" movie clip horizontally and vertically on the screen, then attaches an "onMouseDown" function to the movie clip so it reacts to mouse clicks. Throughout it all, it tracks the movie clip stacking order (or "depth") to ensure that each "tile" clip depth is unique (as required by Flash).
The third function is drawGridXML(), which accepts one argument: an XML object called myXML. Like drawGridFoundation(), this function contains nested "for" loops to count through rows and columns, tile by tile. However, instead of creating a blank grid, this function uses XML node attributes from myXML to change the existing grid tiles.
The fourth function is updateTile(), which accepts one argument: a movie clip reference called who. This function was attached to every grid tile created by drawGridFoundation(), which told each tile to refer to updateTile()with itself ("this"). When you click on a grid tile, this function tests to make sure this tile was clicked (in case an errant click got through to it) and sets the tile to the current drawing tool shape. If the tile is already set to the current shape, then a click toggles it back to being empty. If the chosen drawing tool is the player shape, then the function prevents it from being placed more than once on the level (because you can't have more than one player!).
Input/Output Functions
This section contains all of the functions used to open, save and delete levels from the server.
The first function is openLevel(). It builds a file name from the previously-initialized global defaultGatewayDirwith the name highlighted in the list of available files. Then it creates a Flash XML object and tells it what to do when it has finished loading the XML data. Finally, it loads it. (Note that this could have been handled by using PHPObject to read the file into an array, like the levelListing() function. That might actually be faster and use less bandwidth than having Flash parse the XML itself.)
The second function is saveLevel(). It begins by checking if there is room for another file on the server (by comparing the number of files to the maximum file limit set earlier). If there is room, it uses nested "for" loops to scan through all of the tiles on the grid. It saves information about the current frame of each tile movie clip into the two-dimensional array gameArray. It passes this to the PHP function levelSave() along with the file name typed into the text box, and then calls the PHP function levelListing()to update the file list box. However, if there is not room for another file, it displays an error message by changing the frame on a message movie clip.
The third function is deleteLevel(). This gets the name highlighted in the file list box and passes it to the PHP function levelDelete().
Define button functions
This section initializes the mouse click functions for the tool shapes and the save, open and delete buttons.
Final setup
This section calls the drawGridFoundation() function (explained above) to draw the first blank grid. And that's the end of the ActionScript.
Flowchart
The logic of the functions is illustrated in the flowchart below, from the beginning of the movie, to responses for each click. Functions are labeled according to whether they are in ActionScript (leditor.fla) or passed to PHP (Leditor.php) via PHPObject.
(Note: the "clicked on" boxes do not represent actual "if" statements in the code, but simply reflect the logic of how the clicks are responded to: "clicked on the delete button? do such-and-such.")
5. Comments & Resources
That's a Wrap!
The ActionScript code in leditor.swf, and the PHP code in Leditor.php, contain a lot of comments. These make the code easy to read, even if you are unfamiliar with some of the ActionScript or PHP commands. By reading this tutorial, following the flowchart, and looking at the comments in the source code, you will see how the ActionScript and PHP work together.
There is a lot more than can be done with PHPObject. This tutorial only scratches the surface. Take a look at the official PHPObject documentation and the PHPObject/MySQL tutorials listed below, and explore!
Useful Books
Macromedia Flash MX 2004 Game Design Demystified, by Jobe Makar and Ben Winiarczyk. This is an exceptionally useful book for Flash game developers, particularly in its review of tile-based games and coordinate geometry.
Macromedia Flash MX 2004 ActionScript 2.0 Dictionary, by Macromedia, Inc. This is a must-have reference book for Flash programmers, though at over 1000 pages, it's not exactly portable.
ActionScript for Flash MX Pocket Reference, by Colin Moock. Though slightly outdated for MX 2004 users, this is still a handy reference.
Useful Web Sites
PHPObject's Home Page, Documentation, and Forums are all useful sources of PHPObject information, from its developers.
ActionScript.org's PHPObject tutorial and FlashKit's PHPObject tutorial explain how to use PHPObject to make Flash communicate with a MySQL server.
ActionScript.org's XML tutorial also explains how to handle XML data in Flash, though you may find Macromedia's XML tutorial more understandable.
Macromedia's Flash Remoting Pagehas generic information on Flash remoting topics, although most of it is oriented toward AMFPHP (another way of doing what PHPObject does). Macromedia also has a page on PHPObject, though it doesn't say any more than PHPObject's home page.
The PHP.net reference manual is a great resource for everything you wanted to know about PHP (and then some!).