Share/Save/Bookmark Subscribe

Monday, May 24, 2010

Flash development workflow for traditional dev teams, Part 2

Part 1 of this walkthrough ended off with a flash animation linked up to a main application class that would allow regular updates of that application. This time around we'll see how to access objects in your application added in Flash from the library. Once these are in place, your artists can go nuts customizing them, as long as the basic setup stays the same.

The Menu and Button classes
Let's set up a simple menu class that will allow us to continue to gameplay, or quit the animation. In Flashdevelop, create a new class in your package (right-click the package, Add->New Class) called Menu, specifying MovieClip as it's base class. Add two MovieClip members to it for the buttons, called playBtn and quitBtn:
1 2 3 4 5 6 7 8 9 10 
/**
* Play button.
*/
var playBtn:MovieClip;

/**
* Quit button.
*/
var quitBtn:MovieClip;

As with the App class, add basic singleton functionality, but this time initialize the singleton in the menu constructor:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 
/**
* The Menu instance.
*/
private static var instance:Menu;

/**
* Singleton accessor.
*/
public static function getInstance():Menu
{
return instance;
}

 /**
* Creates a new Menu and sets the singleton pointer.
*/
public function Menu()
{
instance = this;
}

Note I'm not clearing the singleton pointer at any stage, but it will be replaced the next time an instance is created, releasing the reference to the last object and allowing it to be garbage collected. This isn't a singleton in the purest sense, but it does allow us global access to the most current instance. Actionscript doesn't have destructors and I'm yet to find a replacement that I'm 100% happy with.

Finally, add a function onButtonReleaseHandler to handle button presses, and implement the (extremely simple) menu logic:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 
/**
 * Handles an onRelease event.
 * @param source The MovieClip that triggered the onRelease event.
 */
public function onButtonReleaseHandler(source:MovieClip):Void
{
switch(source) {
case playBtn:
App.getInstance().playGame();
break;
case quitBtn:
fscommand ("quit");
break;
}
}

You'll also need to import the App class at the top of the file, using it's fully qualified name:
1 
import us.benic.matt.App;

Note: I've switched on the actual source object here. Actionscript's switch statement is extremely flexible, unlike that in C++ and Java, any object can be used as a case, including strings.

Here is the completed Menu class:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 
import us.benic.matt.App;

/**
* Menu for the Flash Workflow demonstration.
* @author Matt Benic
*/
class us.benic.matt.Menu extends MovieClip
{
/**
* Play button.
*/
var playBtn:MovieClip;

/**
* Quit button.
*/
var quitBtn:MovieClip;

/**
* The Menu instance.
*/
private static var instance:Menu;

/**
* Singleton accessor.
*/
public static function getInstance():Menu
{
return instance;
}

/**
* Creates a new Menu and sets the singleton pointer.
*/
public function Menu()
{
instance = this;
}

/**
* Handles an onRelease event.
* @param source The MovieClip that triggered the onRelease event.
*/
public function onButtonReleaseHandler(source:MovieClip):Void
{
switch(source) {
case playBtn:
App.getInstance().playGame();
break;
case quitBtn:
fscommand ("quit");
break;
}
}
}

Now we need a simpleMovieClip subclass called Button that will notify the menu when it is pressed. Flash does have it's own Button class, but it's really inflexible in that Button components can't use a custom Button subclass. The norm seems to be to just use a custom MovieClip subclass, and the functionality in the Flash Button class can easily be implemented anyway. For our purposes, a simple class that calls the Menu singleton's onButtonReleaseHandler when it is released will suffice:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 
import us.benic.matt.Menu;

/**
* Button for the Flash Workflow demonstration.
* @author Matt Benic
*/
class us.benic.matt.Button extends MovieClip
{
/**
* Creates a new button and sets it's onRelease to notify the Menu instance.
*/
public function Button()
{
// Notify menu instance when released
onRelease = function ():Void {
Menu.getInstance().onButtonReleaseHandler(this);
}
}

}

Changes to the App class
At this point very little needs to be added to the App class, it just needs the function the Menu class called, playGame. This will move the main clip to the Level frame:
1 2 3 4 5 6 7 8 
/**
* Play the game.
*/
public function playGame():Void
{
gotoAndStop("Level");
}

You may also want to remove the trace call in onEnterFrameHandler. It's demonstrative purpose has been served, and outputting text on every frame will just slow down the animation.

The Menu and Button MovieClips
Now we need the actual visual elements used for the menu. First create a new MovieClip in the library (Ctrl+L or Windows-Library to open the library, right click in the library->New Symbol...) called PlayButton. Set it's Type to MovieClip, check the Export for ActionScript checkbox, and specify it's Class as your fully qualified Button class's name (ie including the package):

 

Clicking Ok to dismiss the Create New Symbol dialog causes Flash to open that symbol for editing. Use the flash tools to create a simple button. Eg add a Rectangle for a background, and a TextElement for the text to end up with something similar to this:
 
Copy that symbol as a starting point for the Quit button, right click the symbol in the Library and select Duplicate. In the Duplicate Symbol dialog name the symbol QuitButton and as before enable Export for ActionScript and set it's class to your Button class:


Again, dismissing the dialog with Ok opens the component on the stage. Edit it to say Quit instead of Play.

Now create another new Symbol, this time calling it Menu and setting it's class to your Menu class:


Drag the PlayButton symbol from the Library to the stage, and then do the same with the QuitButton. The result should look something like:

The buttons now need to be named to match the members added to the Menu class earlier. To do this, click on a button on the stage to select it (not it's template control in the library), open the Properties window with Ctrl+F3 or Windows->Properties and replace the <instance name> text in the topmost textbox with the appropriate name. Your Play button should be called playBtn and your Quit button quitBtn:


Putting it all together
The last thing to do is add your newly created menu to the appropriate frame at the top level of the timeline. Return to the top level by clicking on Scene 1 above the stage. In the timeline, on the Objects layer, add new keyframes (right click->Insert Keyframe) below the Menu and Level labels (should be frames 10 and 20 respectively). Now with the new keyframe under Menu selected, drag the Menu symbol from the library onto the stage. Frames 10 to 20 on the Objects layer should now be grayed out to indicate content exists on those frames.

 

If you now run the animation with Ctrl+Enter, the menu will be displayed, and clicking Play will take you to the Play frame (which is currently empty). The Quit button may appear to do nothing, but that is just because fscommand ("quit") has no effect when run from the Flash environment. When the generated .swf file is run by double clicking it in an explorer window, the effect is as expected.

The full project so far is attached to the post. That's it for part 2, the next post will go into adding and manipulating objects programmatically.

A note on variables linked to stage objects
In this example, I used variables within a class who's only link to visual objects is their name. That's scary. Actionscript won't tell you if one of these objects is missing, it will just carry on as if nothing is wrong. You can't even expect an error or exception when you call a method on one of these nonexistent member objects. That's terrifying. Now you may be tempted to insist on creating all objects yourself, programmatically, but that's once again taking a certain amount of creative control (placement) away from the artists, which is kind of the whole point of Flash. The only way to be 100% sure objects you expect to be there are actually there is to check them against null and handle the missing case by throwing an exception/error or making the mistake known some other way.
Related to this is the interesting little issue that numeric values in flash are represented by the Number class, as opposed to some kind of integral type, and must be properly initialized like any other object or will be null. This can catch you by surprise with some really wierd behavior, so if you start seeing NaN's in output, there's an uninitialized Number somewhere.

Click here to download:
WorkFlowProject.zip (10 KB)

Posted via email from Matt's thoughts

Displaying UTF8 from MySQL with PHP

I had some issues displaying localized text in a PHP based internal tool recently, it turns out the solution was really simple-the MySql connection just needs to know that the connection must be UTF8. Probably no-brainer stuff for all you PHP-Ninjas.. news to me :)

To do this with a PDO connection, just add an init command when you create the PDO command:

1 2 3 
$this->pdo = new PDO("mysql:host=".$this->host."; dbname=".$this->databaseName,
$this->user, $this->password,
array(PDO::MYSQL_ATTR_INIT_COMMAND => "SET NAMES utf8"));

Thanks to Rob Allen for the tip, variation for other connection methods can be found on his blog.

Posted via email from Matt's thoughts

Friday, May 21, 2010

Some useful regular expression patterns when porting code between languages

I'm currently moving a lot of code from Java to Actionscript, and regex has been a lifesaver. Here are some of the patterns that proved invaluable, and that would work on most c-style languages. The Actionscript replacements are obviously specific to that language, but changing them for a different output language really would be trivial:

Find non-parameter variable declarations and assignments
Note: this will find returns as well, see the next pattern
(\w+)(\s)(\w+)(;|\s=)

Flash replacement:
var$2$3:$1$4

Will replace:
    int myInt;
    boolean myBool = true;
With:
    var myInt:int;
    var myBool:boolean = true;   

Find resulting returns in Flash
This will match all the wonky return statements resulting from the previous replacement.
var(\s)(\w+):return;

Flash replacement:
return$1$2;

Will replace:
    var true:return;
With:
    return true;

Find parameter variable declarations
(\w+)(\s)(\w+)(,|\))

Flash replacement:
$3:$1$4

Will replace:
    public int foo(int intParam, boolean boolParam) {
With:
    public int foo(intParam:int, boolParam:boolean) {

Find function signatures with parameters in c/java format
(\w+)(\s)(\w+\([\s?\w\s\w+,?]*\))(\s?\{)

Flash replacement:
function$2$3:$1$4

Will replace:
    public int foo(int intParam, boolean boolParam) {
With:
    public function foo(int intParam, boolean boolParam):int {


Find function signatures with parameters in Actionscript format
(\w+)(\s)(\w+\([\s?\w+:\w+,?]*\))(\s?\{)

Flash replacement:
function$2$3:$1$4

Will replace:
    public int foo(intParam:int, boolParam:int) {
With:
    public function foo(intParam:int, boolParam:int):int {

Find numeric types
(,|;|\(|\)|=|\s)(int|long|short|float|double|byte)(,|;|\(|\)|=|\s)

Flash replacement:
Number

Will replace:
    var myInt:int;
    long myLong;
    float foo(byte bar) {
With:
    var myInt:Number;
    Number myLong;
    Number foo(Number bar) {

Posted via email from Matt's thoughts

Sunday, May 16, 2010

Untitled

Untitled

Untitled

Untitled

Untitled

Populating and serving a temporary SQLite database file and serving.

Recently I had the requirement in PHP to populate a SQLite file with data from a MySQL database and make it available for download. I was using PDO objects for MySQL, and found I could do the same for SQLite, so the simple answer became to create a temporary SQLite file with PDO, populate it from the existing MySQL PDO object (by querying the required data and inserting it into the SQLite file-no shortcut there unfortunately) allow the user to download it and then delete it. The sample code below excludes the selection from the existing database (which is comparitively trivial) but includes the rest.
One major thing worth noting (and not illustrated here) is that if you're going to be inserting more than a few dozen records, it's worth wrapping everything in a transaction. This may seem overkill for a temporary file that will fail anyway and hit the try-catch if there's a probelm, but it results in massive performance improvements due to PHP doing all the SQL work in memory and writing the transaction to file in one operation. Just don't go doing it with hundreds of megabytes of data ;)


<?php function
outputDownloadHeaders
($path, $filename, $binary = false) { header("Pragma: public"); header("Expires: 0"); header('Cache-Control: no-store, no-cache, must-revalidate'); header('Cache-Control: pre-check=0, post-check=0, max-age=0', false); header('Last-Modified: '.gmdate('D, d M Y H:i:s') . ' GMT'); // Browser specific headers $browser = $_SERVER['HTTP_USER_AGENT']; if(preg_match('/MSIE 5.5/', $browser) || preg_match('/MSIE 6.0/', $browser)) { header('Pragma: private'); // C in control must be lowercase header('Cache-control: private, must-revalidate'); // Must be a number for IE header("Content-Length: ".filesize($path)); } else { header("Content-Length: ".(string)(filesize($path))); } header('Content-Type: application/x-download'); header('Content-Disposition: attachment; filename="'.$filename.'"'); // Tell the client file is binary if ($binary) { header('Content-Transfer-Encoding: binary'); } } function populateSqlite($sqlitefilepath) { // Open the file-creates new file if necessary $pdo = new PDO("sqlite:".$sqlitefilepath); // Create table in the db if needed $pdo->query("CREATE TABLE IF NOT EXISTS test_table ". "(id INT NOT NULL, text VARCHAR(100) NOT NULL, PRIMARY KEY (id));"); // Populate with some data $pdo->query("INSERT INTO test_table (id, text) values (0, 'Hello');"); $pdo->query("INSERT INTO test_table (id, text) values (1, 'World');"); $pdo->query("INSERT INTO test_table (id, text) values (2, 'from');"); $pdo->query("INSERT INTO test_table (id, text) values (3, 'PHP');"); $pdo->query("INSERT INTO test_table (id, text) values (4, 'and');"); $pdo->query("INSERT INTO test_table (id, text) values (5, 'SQLite'); "); // Connection closed by scope return true; } // The filename to present to the client $dbfilename = 'sqlite.sq'; // Temporary file to hold the sqlite db on the host $dbfilepath = sys_get_temp_dir() . uniqid('sqlite') . '.sq'; try { // Populate new file with some data if (populateSqlite($dbfilepath)) { // Prompt the user to save the file locally outputDownloadHeaders($dbfilepath, $dbfilename, true); // Stream the file to the client $file = fopen($dbfilepath, 'rb'); if ($file) { while(!feof($file) and (connection_status()==0)) { print(fread($file, filesize($dbfilepath))); flush(); } fclose($file); } } } catch (Exception $ex) { echo "Oops, something went wrong: $ex->getMessage()"; } // Clean up the file unlink($dbfilepath); ?>

Posted via email from Matt's thoughts

 

Copyright 2007 All Right Reserved. shine-on design by Nurudin Jauhari. and Published on Free Templates

Afrigator