Is it possible to access as3 functions and variables through JSFL? - actionscript-3

I am writing a JSFL script to export a set of png files for each major section of a flash animation. In order to do this, I need access to functions and variables in the Flash file's Main.as class. The documentation for JSFL is very sparse on the net, and I can't tell if this is possible. Here is what I have:
var docs = fl.documents;
for(var i = 0; i < docs.length; i++)
{
//loop through everything and turn off compression for max quality
var libItems = docs[i].library.items;
for (j = 0; j < libItems.length; j++){
if(libItems[j].itemType == "bitmap"){
libItems[j].allowSmoothing = true;
libItems[j].compressionType = "lossless";
}
}
//the variable adStages is an array of functions in the as3 class
//this way of trying to access the array doesn't work
for(k = 0; k<docs[i].adStages.length; k++){
//here I need to run function adStages[k] to move the animation on
//to the next frame to capture
docs[i].exportPNG("file:///Users/Graeme/Desktop/"+ docs[i].name + k,true, true);
}
}
I know I can do this a more clunky way by extending the Main.as file and using as3corelib PNGEncoder to export the files but I feel if this way works it could be more elegant and I don't have to be prompted on where to put the files each frame.

You can embed swf file into custom Flash IDE panel and then communicate through external interface or call JSFL code directly from AS3 via MMExecute() method. If plant to do lots of work with JSFL then consider reading Keith Peter's book Extending FlashMX 2004. this book is old but still very good and not outdated.

Related

From synchronous flow to asynchronous flow dilemma

As a PHP programmer I'm very habituated to the fact that the program flow goes line by line, if I call function1() that have to return some value I know the program wont continue until that function makes a return.
Now, im developing a AS3 app using AIR, I need to download some images if some conditions are met, don't know how many, so im using a For like this:
for (var w:int=0; w < newData_array[2]['content'].length; w++ ) {
for (var j:int=0; j < oldData_array[2]['content'].length; j++ ) {
if((oldData_array[2]['content'][j]['id'] == newData_array[2]['content'][w]['id']) && (oldData_array[2]['content'][j]['last_mod'] != newData_array[2]['content'][w]['last_mod'])){
//need to download a image...
}
}
}
As you can see im just comparing each element from each array (newData_array and oldData_array). If the condition met, I need to download something, for that I'm using URLloader and as you know this function is asynchronous, adding a listener, an event will be triggered when the download is complete, the problem is very clear, since the urlloader wont stop the for cycle (like I would expect on a PHP alike language) I'm just going to download a bunch of images at the same time creating a disaster because I wont know when to save. So I need to use by any mean the listener, but since I'm not very habituated to this kind of procedures I'm pretty munch stuck.
Im not interested on the save to disk routines, that part is pretty much done, I just want to understand how I should structure this algorithm to make this work.
This annoyed me too, but one has to realize that you can't do something to something that doesn't exist in memory yet, and these loading operations can take quite some time. Being a single-threaded stack, Flash Player's load operations would require that the entire interface to freeze during the load (if restricted to the loop).
Obviously there's data available to the loop that dictates what you do with the image. The solution I've been using is create a custom load function that starts the load, and returns a proxy image . Your loop gets a container back and you can freely place your image where you want.
In the meantime, your loader listener has kept track of the active images loading, and their associated proxies. When loaded, swap the image in.
Your loop would look something like this...
for (var w:int in newData_array[2]['content']) {
for (var j:int in oldData_array[2]['content']) {
if ((oldData_array[2]['content'][j]['id'] == newData_array[2]['content'][w]['id']) && (oldData_array[2]['content'][j]['last_mod'] != newData_array[2]['content'][w]['last_mod'])){
var proxy:MovieClip = loadImage("image/path.jpg");
// Do stuff to proxy.
}
}
}
And your function + listener would look something like this...
var proxies:Object = {};
function loadImage(path:String):MovieClip {
// load Image
var proxy:MovieClip = new MovieClip();
proxies.path = proxy;
// Load Image
}
function imageLoaded(e:Event):void {
// Once loaded, use the filename as the proxy object's key to find the MovieClip to attach the image.
proxies[e.data.loaderInfo.filename].addChild(e.data);
}
This is just off the top of my head, so you'll need to find the actual variable names, but I hope that makes sense.
I am not really sure if I understood your problem but it seems to me that you simply add the according listener in your inner loop.
//take care this is pseudocode
var parent = ... // some stage object
for (var w:int=0; w < size1; w++ ) {
for (var j:int=0; j < size2; j++ ) {
if(some_condition){
request = new URLRequest(getNextImagePath();
urlLoader = new URLLoader();
urlLoader.addEventListener(Event.COMPLETE, onComplete);
urlLoader.load(request);
}
}
}
function onComplete(event:Event) {
parent.addChild(createImageFromData(this.data));
}
I solved the problem using an third party library that pretty munch handles this problem.
https://github.com/arthur-debert/BulkLoader

AS3 dynamic variable creation in DC

Can variables be created dynamically without declaration when we write as Document class in AS3?
For example, from a library I'm importing sound files. Some 20 sound files.
If the code is in fla itself, we can assign in for loop like:
this["SOUND"+increasingNumber]
But in documentClass this is not working , since this refers the class here not the stage.
Any method to create variables?
When imported into your library, right click the sound file and go to its properties. Click the actionscript tab and check 'export for actionscript'. Give it a class name which you can then use in your document class to instantiate that sound.
If you named it Sound1:
var sound:Sound = new Sound1();
sound.play();
more detailed info here
[Edit to loxxy's reply] above shows how to create the variables in the document class.
To dynamically create all the sound variables, I'd recommend using an array, like so:
Suppose you named all your sounds in your library Sound1 to Sound20
import flash.utils.getDefinitionByName;
var sounds:Array = [];
var soundClass:Class;
for(var i:int = 1; i<21; i++){
soundClass = getDefinitionByName("Sound" + i) as Class;
sounds.push(new soundClass());
}
In fla when you add code, you add it into a framescript.
A framescript is a block of code repeated at a regular interval (framerate).
You can achieve that using addFrameScript like this.
However a better approach would be to not mix up framescript & the regular class methods.
You can access the 'stage' from the code but only after the added_to_stage event to be sure.
addEventListener(Event.ADDED_TO_STAGE, init);
function init(e:Event):void{
// Access 'stage' here
}

Using AS3WavSound to play WAV - cannot stop instantly

I'm using the AS3WavSound (http://code.google.com/p/as3wavsound/) class to playback externally loaded wavs. This is working successfully. The library is simple and effective.
After decoding the Wav ByteArray the method the library employs for playback is using the SampleDataEvent.SAMPLE_DATA event and then writing the mixed samples to the output stream.
player.addEventListener(SampleDataEvent.SAMPLE_DATA, onSamplesCallback);
private function onSamplesCallback( evt : SampleDataEvent ):void
{
for (var i:int = 0; i < samplesLength; i++)
{
if(_mute == false){
outputStream.writeFloat(samplesLeft[i]);
outputStream.writeFloat(samplesRight[i]);
}
}
}
My problem is that I need to silence this audio output immediately but whatever method I have tried there is a distinct (1 second approx) delay before the silence takes effect.
As you can see I've attempted to add a boolean to block any samples being written to the output stream but this has had no effect on the delay.
My suspicion is that this is a fundamental part of how the samples are buffered and then written out. Essentially by the time a user action on screen (clicking a mute button) has been called and the _mute boolean is set to true there are already samples waiting to be written to the output that cannot be affected.
Any advice or confirmation of my suspicion would be greatly appreciated.
Thanks,
gfte.
Your suspicion is probably right - but why stop it at that level? If you want to turn off the sound, would it not be better to set the volume on the soundTransform-property on the SoundChannel-object returned by the play method? (I assume the wav library returns this in some way)
It looks like the library you are using has a similar design to the native Flash Sound API wherein a SoundChannel object is returned from the play() method. This SoundChannel instance has a stop() method which should stop the sound right away.
var sound:WavSoundPlayer = new WavSoundPlayer();
var channel:WavSoundChannel = new WavSoundChannel();
sound.addEventListener( SampleDataEvent.SAMPLE_DATA, onSampleData );
channel = sound.play();
private function onSamplesData( evt : SampleDataEvent ):void
{
for (var i:int = 0; i < samplesLength; i++)
{
outputStream.writeFloat(samplesLeft[i]);
outputStream.writeFloat(samplesRight[i]);
}
}
channel.stop()
The _mute variable in your example will only be able to change either before or after the loop, not while it is looping.

Creating Classes and Properties in AS3

I'm new to AS3. Learning how to create classes. Is comp = new HouseObjects creating a new class? Is comp creating an instance of the HouseObjects? I realize that this is inside public class TreeHouse. I'm thinking that HouseObjects, how I set it up is not a class...not sure what the correct way to set up classes and properties.
Also I noticed, that when I tried to link another movieclip using the same linkage name HouseObjects--it asked to enter a unique class. I'm trying to create multiple instances from the same class called HouseObjects.
package {
import flash.display.MovieClip;
import flash.events.MouseEvent;
import flash.events.Event;
public class TreeHouse extends MovieClip
{
private var comp:MovieClip;
var powerData:int; // stores user data (of selected data)
//var currentPower:int; // stores current power
public function TreeHouse()
{
comp = new HouseObjects; // linkage in library
comp.power = 2; // amount of power
comp.name = "comp";
comp.buttonMode = true;
comp.bstate = 0; // button state
//add event listeners -- listens to functions that are called
comp.addEventListener(MouseEvent.MOUSE_OVER, rolloverToggle);
comp.addEventListener(MouseEvent.MOUSE_OUT, rolloutToggle);
comp.addEventListener(MouseEvent.CLICK, toggleClick);
comp.addEventListener(MouseEvent.CLICK, toggleClick);
stage.addChild(comp); // add computer to stage -----------------------------------
trace("tracing...");
comp.x = 100;
comp.y = 100;
}
// function rollOver --------------------------------------------------------------
function rolloverToggle(e:MouseEvent) {
if (e.currentTarget.currentFrame == 1)
e.currentTarget.gotoAndStop(2);
if (e.currentTarget.currentFrame == 3)
e.currentTarget.gotoAndStop(4);
}
// function rollOut-- --------------------------------------------------------------
function rolloutToggle(e:MouseEvent) {
if (e.currentTarget.currentFrame == 2)
e.currentTarget.gotoAndStop(1);
if (e.currentTarget.currentFrame == 4)
e.currentTarget.gotoAndStop(3);
}
// function toggleClick-------------------------------------------------------------
function toggleClick(e:MouseEvent) {
// On MouseEvent gotoAndStop(Frame Number)
if (e.currentTarget.currentFrame == 2)
{
e.currentTarget.gotoAndStop(3);
e.currentTarget.bstate = 1;
}
if (e.currentTarget.currentFrame == 4)
{
e.currentTarget.gotoAndStop(1);
e.currentTarget.bstate = 0;
}
//var powerData:int = HouseObjects[e.currentTarget.power]; // set power value
// Find out which object selected-------------------------------------------------
//trace("movieClip Instance Name = " + e.currentTarget); // [object Comp]
//trace(houseArray[e.currentTarget.name]); // comp
trace("using currentTarget: " + e.currentTarget.name); // comp
//trace("powerData: " + powerData); // power of user data
//trace("houseArray: " + houseArray[0]); // the 0 index of house array
trace(e.currentTarget.power); // currentTarget's power************
}
} //end of class
} // end of package
I am not quite sure if I understood your question correctly. comp = new HouseObjects creates a new instance (object) of the type HouseObjects. (A little research on OOP basics would probably make life easier for you.)
Regarding the »Please enter a unique class name« error: You cannot assign the same class to two library symbols because the symbol is hooked up to the class internally so that if you create a new instance (var x = new HouseObjects; addChild(x);), the content from the linked symbol is also added to the display list. If there were multiple library symbols linked to the same class, how would the Flash compiler know which one to choose?
Your question is pretty broad and , as klickverbot suggests, it would be better if you took a little time to understand basic OOP concepts.
There are a lot of resources available to get you started with AS3, check this for instance
http://tv.adobe.com/watch/colin-moocks-lost-actionscript-weekend/course-1-introduction
Colin Moock's tutorial is very easy to follow and will give you most of the tools you need to get started.
If you are new to AS3, and OOP in particular, you should check out Moock's Essential Actionscript 3 which is beyond fantastic for a step by step education in OOP in AS3.
HouseObjects appears to be a class and you are creating a new instance of it for the variable comp
You've got a duplicate definition. It appears that you are trying to use Flash Pro to extend HouseObjects for the lightbulb. It doesn't work like this in Flash Pro. You are creating a MovieClip symbol and giving it a class definition. It has to extend MovieClip and you cannot change this in this case. You could likely extend HouseObjects in an AS3 file and make use of it in your application.
Personally think that if you want to really get your head around OOP with AS3 you should get the book and get out of Flash Pro. Use an IDE like Flash Builder, FDT, Flash Develop, or IntelliJ IDEA. It is a lot easier to understand when you get away from the dialogs and other complications of the Flash Pro IDE :>

How to add all symbols in library folder to stage at runtime

I just submitted this question but i can't see if it posted anywhere, so I apologize if this is a duplicate.
For a Flash CS4 project, I am constantly importing new images, converting them to movieclips and storing them in the library under an "Ornaments" folder. All of these ornaments need to be on the stage in a certain place when the program is initialized. Instead of having to drag the new symbols onto the stage every time I add a new, is it possible to add all of the symbols in the "Ornament" library folder to the stage at runtime?
Thanks
You can do it in code if you wish, but you'd have to still add the names of the symbols to the code. That is, the folder is merely a convenience for organizing within the CS4 library and it does not translate to code (AFAIK).
To instantiate the item in AS3, simply right click the symbol in the library and check the box labelled "Export for ActionScript". If you can't see it, click the Advanced button. It will default the Class to the name of the symbol. That will be the class you can instantiate in ActionScript to put an instance on stage.
You could keep an array of the ornament names and loop through them adding them to the stage:
var ornaments:Array = [OrnamentGold, OrnamentSilver, OrnamentBronze];
for each(var ornament:Class in ornaments)
{
var ornamentClip:MovieClip = new ornament();
addChild(ornamentClip);
}
If you name all of your instances the same with only a trailing digit incremented, you can save yourself some time and just increment a single number:
const NUM_ORNAMENTS:int = 5;
for(var i:int = 0; i < NUM_ORNAMENTS; i++)
{
// ornaments are names Ornament0, Ornament1, Ornament2, etc. in the library
var ornamentClass:Class = new getDefinitionByName("Ornament" + i) as Class;
var ornamentClip:MovieClip = new ornamentClass();
addChild(ornamentClip);
}