David J McClelland

eLearning Designer / Developer

I read an article recently describing this concept at CoderHump. Ben Garney used it to make it easier for game artists and designers to tweak game settings without training them on an API or building and maintaining a separate tweak input tool for them to use.

This concept takes advantage of a useful feature of Google Docs spreadsheets: they can be set up to publish in ATOM format, which is XML. You can lay out columns and rows so that they correspond to variables and values like a flat-file database. There are docs on the API.

Like any Google Doc, you can set up access control if you want. If you want multiple editors, think carefully about how you deal with unexpected values. It would be nice if there was cell-level protection.

Step-By-Step

Google Docs:

Create a spreadsheet. Put in a couple columns and rows worth of data, such as the days of the week in column one and their corresponding name in Spanish in column two. (This could be an awesome translation tool, no?).

The settings you need are in the Share Tab on the Right:

Using Sharing settings… , publish the spreadsheet so that it is publicly viewable:

Then open the pubish as a Web Page settings from the same settings tab:

Change the dropdown menu from Web page to ATOM…

… and copy the ATOM URL to use in ActionScript.

Flash: Load the XML using the URL for the ATOM feed as the URLRequest string.

Post to Twitter

  • 0 Comments
  • Filed under: Techniques
  • Isometric Character Motion Analyzer

    As I promised, I will describe how I set up the motion analyzer to test character animations without loading them into a game. It should go without saying that you should still test your character in the game – the idea is to eliminate simple issues before getting to that stage.

    This is really quite simple: set up a document class that puts listeners on every character on the stage. put a button in your library and use it to set up the controls you want to wire up to the character. If the clip has a timeline it will run the lead-in and loop until you stop it.

    I added a tile background to help visualize foot position and relative angles.

    The same swf that is used to test can be used in the game as media. I recommend hiding the tiled background if you want to optimize file size. Do this by leaving “include hidden layers” unchecked in the publish dialog or setting the layer that contains them to be a guide.

    The ActionScript is very basic. The connection between the frame animation and the document class are one of the things that keeps me coming back to Flash. At some point I want to try sprite sheets in ffilmation too though.

    package {
    	import fl.controls.Button;
    	import flash.display.MovieClip;
    	import flash.events.MouseEvent;
    	import flash.filters.GlowFilter;
     
    	public class FFChar_MotionAnalyzer extends MovieClip {
    		public var FFChar_0:MovieClip;
    		public var FFChar_45:MovieClip;
    		public var FFChar_90:MovieClip;
    		public var FFChar_135:MovieClip;
    		public var FFChar_180:MovieClip;
    		public var FFChar_225:MovieClip;
    		public var FFChar_270:MovieClip;
    		public var FFChar_315:MovieClip;
     
    		public var run_btn:Button;
    		public var walk_btn:Button;
    		public var jump_btn:Button;
    		public var stop_btn:Button;
     
    		private var cur_FFChar:MovieClip;
    		private var glowFilter:GlowFilter;
    		private var rollFilter:GlowFilter;
    		private var characters:Array;
     
    		public function FFChar_MotionAnalyzer() {
    			rollFilter=new GlowFilter(0x00FF00);
    			glowFilter = new GlowFilter();
    			characters = new Array(FFChar_0, FFChar_45, FFChar_90, FFChar_135, FFChar_180, FFChar_225, FFChar_270, FFChar_315);
    			for each (var char:MovieClip in characters)
    			{
    				char.addEventListener(MouseEvent.CLICK, FFCharClicked);
    				char.buttonMode = true;
    			}
    			run_btn.addEventListener(MouseEvent.CLICK, run_btnClicked);
    			run_btn.label="Run";
    			walk_btn.addEventListener(MouseEvent.CLICK, walk_btnClicked);
    			walk_btn.label="Walk";
    			jump_btn.addEventListener(MouseEvent.CLICK, jump_btnClicked);
    			jump_btn.label="Jump";
    			stop_btn.addEventListener(MouseEvent.CLICK, stop_btnClicked);
    			stop_btn.label="Stop";
    		}
     
    		private function FFCharClicked(event:MouseEvent):void {
    			trace(event.target.name);
    			cur_FFChar=event.target as MovieClip;
    			clearGlows();
    			cur_FFChar.filters=[glowFilter];
    		}
     
    		private function clearGlows():void {
    			for (var i:uint = 0; i < numChildren; i++) {
    				getChildAt(i).filters=[];
    			}
    		}
     
    		private function run_btnClicked(event:MouseEvent):void {
    			if (cur_FFChar!=null) {
    				cur_FFChar.gotoAndPlay("Run");
    				cur_FFChar.filters=[];
    			}
    		}
     
    		private function walk_btnClicked(event:MouseEvent):void {
    			if (cur_FFChar!=null) {
    				cur_FFChar.gotoAndPlay("Walk");
    				cur_FFChar.filters=[];
    			}
    		}
     
    		private function jump_btnClicked(event:MouseEvent):void {
    			if (cur_FFChar!=null) {
    				cur_FFChar.gotoAndPlay("Jump");
    				cur_FFChar.filters=[];
    			}
    		}
     
    		private function stop_btnClicked(event:MouseEvent):void {
    			if (cur_FFChar!=null) {
    				cur_FFChar.gotoAndStop("Stand");
    				cur_FFChar.filters=[];
    			}
    		}
    	}
    }

    My next post will show an example where using this approach is essential to setting up a hybrid Poser/Photo cartoon character.

    Post to Twitter

  • 0 Comments
  • Filed under: Techniques
  • Subversion and DropBox

    subversion_logo

    I have been using Subversion for several years, and generally love it (I use the Tortoise Client). My one complaint about it is that its development is limited to addressing shortcomings of CVS. That means no innovation.

    My Pet Peeve

    No innovation means no hope that Subversion will ever store files that aren’t under version control. I use many open source libraries that are under version control from their own repositories, and I can’t store them in the same directory with my own code.  So I used to make exports of updates of multiple libraries and tell Subversion to ignore them.

    This process was inconvenient, but it worked. Since I work on multiple workstations I would have to make a copy of the entire project and move it around with a thumb drive. This made things exponentially more complicated, inconvenient and error-prone. To help compensate I put all libraries into a single directory so that I only needed to maintain a single classpath in Flash.

    dropboxjunction3

    Thumbdrive Update Flow

    DropBox to the rescue

    box2

    After I started using it, it occurred to me that I could use my DropBox folder to store my library. This solved the thumbdrive manual sync issue. I still needed to update and export third-party libraries with some regularity, but a major improvement.

    dropboxjunction

    Update flow with DropBox

    Symbolic Links

    While checking out the DropBox forums I chanced upon an entry describing how symbolic links could be used to include folders outside the My Dropbox folder. It occured to me that this technique would allow me to connect the source folder in third party libraries to folders inside my own library. This would eliminate the export/copy requirement from my update workflow.

    Junction

    junctionicon

    Creating symbolic links could be as easy as creating a shortcut, but isn’t. Windows doesn’t come with tools to create symbolic links unless you have a server version. Luckily Mark Russinovitch created a free tool that will create these links on any Windows OS that supports them. It’s called Junction. Microsoft bought his company, Sysinternals, but they still host his products.

    dropboxjunction2

    Update Flow with DropBox and Junction

    Post to Twitter

  • 0 Comments
  • Filed under: Techniques, Tools
  • Using …Rest to Retrieve Data from XML


    I needed a way to get all display object coordinates from XML to support a liquid layout. The XML Data format is specified to mirror the names and hierarchy of the display. The class used to get the data needed to be able to return data from any level in the hierarchy, since the display objects were nested clips.

    Other XML getters I have written either expect the path to the data as a literal argument or assume a unique node name at a constant location. In this case I couldn’t provide a literal path argument because the path would differ with each call. The location within the data source could be any number of nodes deep. The end node in each case would be a coordinate value (“X”, “Y”, “Z”, “H”, “L”, “D”) corresponding to a display object attribute, by definition non-unique.

    Using the …rest parameter enabled the getter to accept a series of arguments of any length and iterate through them in order to create the path to the data.

    public static function getCoordinate( ...clipLevels):Number

    Although it isn’t typed, we have to assume that each clipLevels parameter will be an XML node name. A caller looks like this:

    private static const scaledSizerX = ContentMeta.getCoordinate("HEADER", "SIZERRADIOGROUP","_SCALEDSIZER","X");

    Each string must be in the order [highest to lowest] to build the correct path to the data. A local var [query] stores the result of the highest level query, which is the only one against the data [coordinates_meta].

    var query:XMLList = coordinates_meta[clipLevels[0]];

    After that, looping through the cliplevels arguments (args = clipLevels.length) against query gets down to the data:

    for (var i:uint = 1; i < args; i++)
    {

    query = query[clipLevels[i]];

    }

    Then recast the result to a Number and return it to the caller:

    returnVal = Number(query[0]);

    return returnVal;

    Post to Twitter

  • 0 Comments
  • Filed under: Techniques
  • Shiny