Monday, April 14, 2008

Problems loading a local XML file in AIR on a Mac

I would never believe that I would curse a Mac -- and it's really not Macs fault! In order to let a user save the photos on this DVD project, I'm trying to build it with AIR and then port to Shu -- but for some reason I am unable to get an AIR file to access a local xml file from the same directory. Here is how I've been trying to do it:

var xmlLoader:URLLoader = new URLLoader();

var file:File = flash.filesystem.File.applicationDirectory;

file = file.resolvePath("my.xml");

var xmlPath:URLRequest = new URLRequest(file.url);

try {
xmlLoader.load(xmlPath);
} catch (error:Error) {
trace("Unable to load requested document.");
}


Works perfectly on Windows but I get the #2032 error on Mac. If I can't get this fixed today then I'll probably be forced to use Zinc instead...

Sunday, April 6, 2008

Loading too many photos too fast!

On this project we have to load over 200 photos to the screen before the app should start. I was using a for loop to instantiate the Loaders, but kept finding sporadic results in loading. Sometimes all the photos would come in and other times only some of them would. Anyway, although I have nothing to prove it, I think it just was too much for flash to load so many things at once, so it would drop some as needed. In any event I decided to use a timer instead to do the loading:
public function addPhotosByTimer():void{
// the photo paths are stored in an array
var photoArrayLen:int = myPhotoArray.length;
// trigger the timer only for the number of items in the array
// and add a photo every 1oth of the sec
var photoAddTimer:Timer = new Timer(100,photoArrayLen);
var j:int = 0;
photoAddTimer.addEventListener(TimerEvent.TIMER,addPhoto);
photoAddTimer.start();

function addPhoto(evt:Event):void{
var photoLoader = new Loader();
var thisPath:String = thisSectPhotoArray[j];
photoLoader.load(thisPath);
j++;
}
}
So far it seems to work fine...

Cannot create property ColorTransform on flash.geom.Transform

Ok, here was a weird one. I was trying to change the color of a movieclip at runtime -- which could be done with setRGB() in actionscript 2. So I did this to match the new AS3 approach:

import flash.geom.ColorTransform;

var myCT:ColorTransform = new ColorTransform();
myCT.color = 0x8e5823;

myMovieClip.transform.ColorTransform = myCT;
But when I run this I got this error:

"Cannot create property ColorTransform on flash.geom.Transform"

After pulling my hair out (just for a bit) I found that I had capitalized the colorTranform property of the MovieClip. Once I changed the last line to this:
myMovieClip.transform.colorTransform = myCT;
...it worked fine. But since I didn't get any Google result for that error I figured I would put this in.

Simple mistake but very annoying ;-)

Play mp3 files in AS3

Here is a method that can be called to play a an MP3 file. If there is a current file playing it stops before starting the next one. It should be noted that once you call the play() method on a Sound you cannot load another mp3 into it -- you need to create a new one.

I should probably turn this into a class to make it easier for reuse:

import flash.media.Sound;
import flash.media.SoundChannel;
import flash.media.SoundTransform;
import flash.net.URLRequest;

private var _soundChannel:SoundChannel;
private var _sound:Sound;

soundVolume = new SoundTransform();
soundVolume.volume = 1;

public function getVolume():SoundTransform{
return soundVolume;
}

public function playSound(url:String):void{
var request:URLRequest = new URLRequest(url);
if(_soundChannel!=null){
_soundChannel.stop();
_sound = null;
}
_sound = new Sound();
_sound.load(request);
_soundChannel = _sound.play();
_soundChannel.soundTransform = getVolume();
}

playSound("myfile.mp3");

Creating a toggling mute button in AS3

Here is some code to create a toggle mute button for sounds in AS3

import flash.media.Sound;
import flash.media.SoundChannel;
import flash.media.SoundTransform;
import flash.net.URLRequest;

private var muted:Boolean = false;
private var soundFactory:Sound;
private var soundClip:SoundChannel;
private var soundVolume:SoundTransform;
private var url:String = "myclip.mp3";
private var request:URLRequest = new URLRequest(url);


// create a new SoundTransform obj
soundVolume = new SoundTransform();
soundVolume.volume = 1;

// now create a new sound obj
soundFactory = new Sound();
// now load the mp3
soundFactory.load(request);
// and play it
soundClip = soundFactory.play();

// now set the volume of the clip --
//you need to do this AFTER the clip is playing
soundClip.soundTransform = soundVolume;

// here is an eventHandler that will mute and unmute the volume
private function toggleVolumeListener(evt:Event):void{
//toggle the boolean
_muted = !_muted;
if(_muted){
// first set the var for use with other sound clips
soundVolume.volume = 0;
// now reset the soundTransform obj of the soundClip
soundClip.soundTransform = soundVolume;
}else{
soundVolume.volume = 1;
// now reset the soundTransform obj of the soundClip
soundClip.soundTransform = soundVolume;
}
}

gotoAndStop in AS3

Very important changes in AS3 when trying to access objects in a movieClip timeline using gotoAndStop(). Here is a great blog entry outlining this change:

http://www.kirupa.com/forum/showpost.php?p=2113726&postcount=358

Saturday, April 5, 2008

Adobe on demand seminars

Adobes has made available a huge number of on-demand seminars on various topics. As soon as I have time I need to check some of these out:

Building Rich Internet Applications with Flex 3

Building AIR Applcations with Flash CS3

Building Collaborative Applications with Flex

Load Testing Flex Applications with WebLOAD

Flash 101: Part 3 with advanced tips/tricks


and more!




What a difference an IDE makes -- AS3 code editor that is!

I have tried a number of different code editors for Actionscript over the years and when I was working FT with Flash a few years ago had settled on SEPY. But coming back to this work again a few years later I needed to find a new editor for AS3. I loved the one in FlexBuilder but for some reason didn't think to use it for editing the AS3 code that I was working on in Flash CS3. Anyway, thanks to a blog post about code editors I tried opening
FlexBuilder3 and FlashCS3 at the same time. I now use Flexbuilder for the code and Flash to change design elements and to compile the swf and air files.

Very nice!

Friday, April 4, 2008

Copying and saving files to the desktop in an AIR app

Another thing I learned today was how to copy a file from one location to another -- while allowing the user to choose a location and name. This allows the user to select a photo in the App and save a high resolution version to their computer. Here is the class I wound up with:

package {
import flash.filesystem.File;
import flash.filesystem.FileMode;
import flash.events.*;

public class FileSaver{
private var fileSaver:File;
private var fileName:String;
private var pathToFile:String;

public function FileSaver(){//empty constructor}
public function saveFile(_pathToFile:String,_fileName:String):void{
fileName = _fileName;
pathToFile = _pathToFile;
// point the browse to the desktop--the user can change it on their ow
fileSaver = File.desktopDirectory.resolvePath(fileName);
// once they select the location it calls saveData
fileSaver.addEventListener(Event.SELECT, saveData);
fileSaver.browseForSave("Choose the location to save the file");
}

private function saveData(evt:Event):void{
//first get the file to save
var copyFromFile:File =
File.applicationDirectory.resolvePath(pathToFile + fileName);
// now copy it to the location chosen above
copyFromFile.copyTo(fileSaver);
}

private function completeHandler(evt:Event):void {
//put something here so the user knows the file is saved
}
}

To use it, create an instance of the class and call the saveFile() method -- passing it the path to the file you want to copy and the name of the file itself. If you're using Shu (as mentioned in the post below) make sure that the path includes the Application directory as well.

One other thing I need to fix (and add to the class) is that if the user trys to save over an already exiting file, it crashes the app. It's too late tonight to fix that but it should be a bit easier now that I have the basics of the problem down...

Using Shu to create a standalone DVD project

Here is something that took a lot of time to learn (while using Shu to create a standalone app from an AIR project) Perhaps it will help someone else...

(Keep in mind that I am using Flash CS-3 because the instructions will differ if you are using FlexBuilder or the SDK)

The DVD app I'm working on needs to access a locally stored xml file and hundreds of photos. If you do this with AIR, you can have the installer include all of the files -- but in this case it creates an AIR file that was 150MB -- and the resulting Shu file was 200MB! Of course, the better way is to keep the files external to the exe. To do this you need to use the built-in Shu app path variable. Here are the steps to
implement it:

1. Follow the instructions in Shu to install its swc file into your Flash CS3 components directory
2. Drag an instance of the Shu component onto your stage
3. Import the Shu classes:
import com.cjt.shu.Shu;
4. Create a Shu instance:
   var shu = Shu.GetInstance();
5. Set the var to the appPath:
   var  shuAppPath:String = shu.Paths.app;
Now when you want to access anything that is outside of the flash file you need to prepend this variable to the path. So if you have an xml that resides in the same directory and would normally reference it this way:
pData.loadXML("presentation.xml");
you would do this instead:
pData.loadXML(shuAppPath + "presentation.xml");
...or if you have a photo that loads at runtime and its in the photos folder you do the same thing. Of course, if you don't do some error checking the app will compile but won't work when you run it as an AIR app -- but everything will be fine when you create and run the Shu file.

The good news is after I finally figured it all out, it works like a charm! The user can choose a photo and download it anywhere they want. I still have to try it out on MAC before I buy the program, but at this point I'm impressed.

However, it would be great if they included some simple tutorials on how to build stuff -- I almost gave up altogether because when I compiled my first Shu file from a fully working AIR app - and put the exe in the same folder, it didn't work at all. It was only because I had worked with Zinc before that I thought it might be necessary to access files using a application directory locater - rather than being able to just reference them relative to the app itself.

Opening a window fullscreen in AIR

Working in Flash CS3 (with the AIR updater) -- if you want an AIR app to open full screen:
import flash.display.StageDisplayState;

public function maximizeWindow():void{
this.stage.nativeWindow.stage.displayState =
StageDisplayState.FULL_SCREEN_INTERACTIVE;
}

Creating a standalone AIR app

For this DVD project I'm working on there is a requirement to let a user save selected photos to their computer. Flash of course does not allow this unless you host the files on a live server so I have been considering wrapping the whole thing in Zinc. At one point I considered AIR -- but the fact that the AIR runtime has to be installed before you can run an AIR app, killed that idea.

But, as luck (or Providence) might have it, I just got a RSS feed entry that talked about Shu -- which allows you to create standalone AIR apps. This could be just the ticket:

http://shu-player.com/

Wednesday, April 2, 2008

Determining the running frame rate in an Flash AS3 movie

I'm working on a DVD project that has over 200 photos in the main movie and have been having a LOT of trouble with the framerate. But I wasn't able to see what the actual running framerate was and what was causing my issues. There are lots of results in Google on "framerate in AS3" but all about how you can now dynamically change the framerate of an AS3 movie at runtime -- not how to determine the actual current framerate of a running movie.

Anyway, I did find one that was written in AS2 and with MANY thanks to TechMono, I've modified it for use in AS3. So, to pass on the favor, here it is. To set it up, create a new instance of the class and use addChild() to place it at the top level of your movie:
private var myFPS:FramesPerSecond;

myFPS = new FramesPerSecond();
addChild(myFPS);

...and you're done. The Class offers two public methods -- start() and stop() -- but it starts by default.

Here is the Class:

package {
import flash.display.Sprite;
import flash.text.*;
import flash.utils.*;
import flash.events.*;

public class FramesPerSecond extends Sprite {
private var fps_txt:TextField = new TextField();
private var m_counter:int = 1;
private var m_startTime:Number;
private var m_updateRate:int;
private var m_precision:int;
private var txtFormat:TextFormat;

public function FramesPerSecond(updateRate:Number=6,precision:Number=1) {
m_startTime = getTimer();
m_updateRate = updateRate;
m_precision = Math.pow(10, precision);
fps_txt.width =100;
fps_txt.autoSize = TextFieldAutoSize.LEFT;
fps_txt.textColor=0xFFFFFF;
fps_txt.opaqueBackground = true;
fps_txt.backgroundColor=0x000000;
txtFormat = new TextFormat("Verdana",13);
fps_txt.setTextFormat(txtFormat);
addChild(fps_txt);
start();
}

private function update(evt:Event):void {
if (m_counter == m_updateRate) {
var stopTime:Number = getTimer();
var elapsedTimeInSecs:Number = (stopTime - m_startTime) / 1000;
var fps:Number = m_updateRate / elapsedTimeInSecs;
fps = Math.floor(fps * m_precision) / m_precision;
fps_txt.text = "FPS: " + fps;
fps_txt.setTextFormat(txtFormat);
this.m_startTime = stopTime;
this.m_counter = 1;
} else {
this.m_counter++;
}
}

public function start():void{
this.addEventListener(Event.ENTER_FRAME,update);
}

public function stop():void{
this.removeEventListener(Event.ENTER_FRAME,update);
}
}
}


...if you want a good explanation of how it works, best to go back to the original creator. As for me, I am VERY happy to have this and was able to identify the frame-rate-killing function immediately ;-)