AS3,AIR,Socket. How to check the progress of a writeObject() transfer? - actionscript-3

I'm dealing with AIR and Sockets, using a Server app and another as Client.
Server sends an object to Client:
clientSocket.writeObject(myObject);
Client has a listener, like this:
socket.addEventListener(ProgressEvent.SOCKET_DATA, socketData);
How can I know the size of the incoming object? I need to monitoring this process, cause when the transfer is complete I need to do another processes.
I tried this, but doesn't work :
var total:int = 0;
private function socketData(e:ProgressEvent) :void {
if (total == 0) {
total = socket.readInt();
}
if (socket.bytesAvailable >= total) {
trace('COMPLETE');
total = 0;
} else {
trace('progress:' + socket.bytesAvailable + ' | total: ' + total);
}
}
This post does not work in my specific case: AS3 / AIR readObject() from socket - How do you check all data has been received?

I resolve it, with a hack:
SERVER: Send an object:
clientSocket.writeObject(image);// var image:Bitmap
clientSocket.flush();
CLIENT:
A. Have a listener to receive socket data:
socket.addEventListener(ProgressEvent.SOCKET_DATA, socketData);
B. Receive the bytes. When all the bytes are available, then read the object:
private var prevBytes:int = -1;
private var currentBytes:int = 0;
private function onEnterFrame(e:Event):void {
if (currentBytes == prevBytes) {
removeEventListener(Event.ENTER_FRAME, onEnterFrame);
currentBytes = 0;
prevBytes = -1;
var obj:* = socket.readObject();
} else {
prevBytes = currentBytes;
}
trace('Current : ' + currentBytes + ' | Prev : ' + prevBytes);
}
private function socketData(e:ProgressEvent):void {
trace('on socketData : ' + currentBytes);
if (currentBytes == 0) {
addEventListener(Event.ENTER_FRAME, onEnterFrame);
}
currentBytes = socket.bytesAvailable;
}

Related

Error when loading swf game while stimulate download but not normally

Okay, so When I run my loader.swf, it's loads my game.swf perfectly fine. But when I stimulate download, at exactly 70%, My music of game.swf starts playing and it shows following errors:
TypeError: Error #1009: Cannot access a property or method of a null object reference.
at GAME_fla::MainTimeline/frame11()
And
Error #2044: Unhandled IOErrorEvent:. text=Error #2036: Load Never Completed.
The game works perfectly fine when loaded normally. This issue persists when I upload it online and check if it's working. It doesn't. My loader works well for other swf files. How to solve this?
Code of layer 1, frame 11
import flash.media.SoundChannel;
var bgmusic2: loverswalk = new loverswalk;
var bgmusic: spring = new spring;
var forestmusic: NatureAmbiance = new NatureAmbiance;
var cafemusic: cafeteria1 = new cafeteria1;
var shopmusic: pamfluiteloop = new pamfluiteloop;
var cash: CashRegisterChaChing = new CashRegisterChaChing;
var beeps: beep = new beep;
var musicboxsound: BlaclRockShooterMusicBox = new BlaclRockShooterMusicBox;
var failsound: fail = new fail;
var stgup: stageup = new stageup;
var talkbg: talkbag = new talkbag;
var ca: correct = new correct;
var myChannel: SoundChannel = new SoundChannel();
var bg: SoundChannel = new SoundChannel();
var mb: SoundChannel = new SoundChannel();
var myChanneltalk: SoundChannel = new SoundChannel();
var lastpositionbg: Number = 0;
var moodshop = 0;
var dilbgrun = 0;
var alertsound: alert = new alert;
Layer 2, frame 20
var hp: int = 100;
var day: int = 1;
var week: int = 1;
var money: Number = 0;
var pendants: int = 0;
var bracelets: int = 0;
var comics: int = 0;
var poetrys: int = 0;
var tshirts: int = 0;
var named: String = "Driver";
var age: int = 17;
var reply: int = 0;
var dilrun: String = " ";
var ncpvar: int = -1;
var ncpvare: int = -1;
var ncpvarm: int = -1;
var flavie_stage: int = 0;
var shannon_stage: int = 0;
var vivette_stage: int = 0;
var audrey_stage: int = 0;
var gabriel_stage: int = 0;
var toret: int = 0;
function refresh1(event: Event): void {
Object(this).bar.hp_text.text = String(hp);
Object(this).bar.day_text.text = String(day);
Object(this).bar.week_text.text = String(week);
Object(this).bar.money_text.text = String(money);
}
function bedbtn(event: MouseEvent): void {
alert_mc.gotoAndStop("Alert");
alert_mc.alert_mc.alert_text.text = "You slept a night and gained a massive 100 HP. Enjoy!";
hp = 100;
day = day + 1;
if (day == 8) {
day = 1;
week = week + 1;
}
}
function woman(event: MouseEvent): void {
if (event.target == audrey_btn) {
if (hp >= 30) {
hp = hp - 30;
dilrun = "Audrey";
dial_mc.gotoAndPlay("Dialogue");
} else {
alertsound.play();
alert_mc.gotoAndStop("Alert");
alert_mc.alert_mc.alert_text.text = "Go get some more HP silly!";
}
}
if (event.target == shannon_btn) {
if (hp >= 30) {
hp = hp - 30;
dilrun = "Shannon";
dial_mc.gotoAndPlay("Dialogue");
} else {
alertsound.play();
alert_mc.gotoAndStop("Alert");
alert_mc.alert_mc.alert_text.text = "Go get some more HP silly!";
}
}
if (event.target == vivette_btn) {
if (hp >= 30) {
hp = hp - 30;
dilrun = "Vivette";
dial_mc.gotoAndPlay("Dialogue");
} else {
alertsound.play();
alert_mc.gotoAndStop("Alert");
alert_mc.alert_mc.alert_text.text = "Go get some more HP silly!";
}
}
if (event.target == gabriel_btn) {
if (hp >= 30) {
hp = hp - 30;
dilrun = "Gabriel";
dial_mc.gotoAndPlay("Dialogue");
} else {
alertsound.play();
alert_mc.gotoAndStop("Alert");
alert_mc.alert_mc.alert_text.text = "Go get some more HP silly!";
}
}
if (event.target == flavie_btn) {
if (hp >= 30) {
hp = hp - 30;
dilrun = "Flavie";
dial_mc.gotoAndPlay("Dialogue");
} else {
alertsound.play();
alert_mc.gotoAndStop("Alert");
alert_mc.alert_mc.alert_text.text = "Go get some more HP silly!";
}
}
}
function NCP(event: MouseEvent): void {
if (event.target == odile_btn) {
if (hp >= 10) {
hp = hp - 10;
dilrun = "Odile";
dial_mc.gotoAndPlay("Dialogue");
} else {
alertsound.play();
alert_mc.gotoAndStop("Alert");
alert_mc.alert_mc.alert_text.text = "You need more HP to talk to Odile.";
}
}
if (event.target == elias_btn) {
if (hp >= 10) {
hp = hp - 10;
dilrun = "Elias";
dial_mc.gotoAndPlay("Dialogue");
} else {
alertsound.play();
alert_mc.gotoAndStop("Alert");
alert_mc.alert_mc.alert_text.text = "You need more HP to talk to Elias.";
}
}
if (event.target == mikel_btn) {
if (hp >= 10) {
hp = hp - 10;
dilrun = "Mikel";
dial_mc.gotoAndPlay("Dialogue");
} else {
alertsound.play();
alert_mc.gotoAndStop("Alert");
alert_mc.alert_mc.alert_text.text = "You need more HP to talk to Mikel.";
}
}
}
function buy(event: MouseEvent): void {
if (event.target == gifts_btn) {
Object(this).menu_mc.gotoAndStop("Shop");
}
if (event.target == buy_food_btn) {
Object(this).menu_mc.gotoAndStop("Cafe");
}
}
function job(event: MouseEvent): void {
alertsound.play();
alert_mc.gotoAndStop("Alert");
if (event.target == job1_btn) {
if (hp >= 40) {
hp = hp - 40;
money = money + 30;
alert_mc.alert_mc.alert_text.text = "You earnt 30 for 40 HP";
} else if (hp < 40) {
alert_mc.alert_mc.alert_text.text = "You don't have 40 HP to that. Better sleep or eat to gain more HP.";
}
}
if (event.target == job2_btn) {
if (hp >= 30) {
hp = hp - 30;
money = money + 20;
alert_mc.alert_mc.alert_text.text = "You earnt 20 for 30 HP";
} else if (hp < 30) {
alert_mc.alert_mc.alert_text.text = "You don't have 30 HP to that. Better sleep or eat to gain more HP.";
}
}
if (event.target == job3_btn) {
if (hp >= 20) {
hp = hp - 20;
money = money + 10;
alert_mc.alert_mc.alert_text.text = "You earnt 10 for 20 HP";
} else if (hp < 20) {
alert_mc.alert_mc.alert_text.text = "You don't have 20 HP to that. Better sleep or eat to gain more HP.";
}
}
}
If I add them, other frames are not being able to access those variables.
Loader code before:
var myrequest: URLRequest = new URLRequest("GAME.swf");
var myloader: Loader = new Loader();
myloader.load(myrequest);
myloader.contentLoaderInfo.addEventListener(ProgressEvent.PROGRESS, progresshandler);
function progresshandler(myevent: ProgressEvent): void {
var myprogress: Number = myevent.target.bytesLoaded / myevent.target.bytesTotal;
bar_mc.scaleX = myprogress;
percentage_text.text = Math.round(myprogress * 100) + "%";
}
myloader.contentLoaderInfo.addEventListener(Event.COMPLETE, finished);
function finished(myevent: Event): void {
addChild(myloader);
removeChild(percentage_text);
removeChild(bar_mc);
removeChild(background_mc);
}
Loader Code After:
var myrequest: URLRequest = new URLRequest("GAME.swf");
import flash.net.URLRequest;
import flash.display.MovieClip;
import flash.events.Event;
var myloader: Loader = new Loader();
myloader.load(myrequest);
function progresshandler(myevent: ProgressEvent): void {
var myprogress: Number = myevent.target.bytesLoaded / myevent.target.bytesTotal;
bar_mc.scaleX = myprogress;
percentage_text.text = Math.round(myprogress * 100) + "%";
}
myloader.contentLoaderInfo.addEventListener(Event.INIT, finished);
myloader.contentLoaderInfo.addEventListener(Event.COMPLETE, completes);
function finished(myevent: Event): void {
addChild(myloader);
removeChild(percentage_text);
removeChild(bar_mc);
removeChild(background_mc);
}
function completes(myevent: Event): void {
var loaderInfo: LoaderInfo = myevent.target as LoaderInfo;
var swf: Object = loaderInfo.content;
swf.play();
}
Maybe you should give a try to this event: ADDED_TO_STAGE.
The ADDED_TO_STAGE event is triggered when an object is added to stage (very curious name and correlation), that is when you execute an addChild method.
Try to listen to this event in your Main class of your external SWF, so, no code will be executed before you add this respective object to stage (now you can hear the music even during the load process.
Follow some example:
package
{
import flash.display.Sprite;
import flash.events.Event;
public class TheMainClassOfYourExternalSWF extends Sprite
{
public function TheMainClassOfYourExternalSWF()
{
addEventListener(Event.ADDED_TO_STAGE, initHandler);
}
private function initHandler(event:Event):void
{
removeEventListener(Event.ADDED_TO_STAGE, initHandler);
trace('this is the stage:', stage);
trace('this is my parent:', this.parent);
// here you start to include your logic
}
}
}
if you are using a FLA file and adding your code using timeline, you can try:
addEventListener(Event.ADDED_TO_STAGE, initHandler);
function initHandler(event:Event):void
{
removeEventListener(Event.ADDED_TO_STAGE, initHandler);
// here you start to include your logic, call your functions, initialise your instances...
}
When loading an external swf there are 2 things to be aware of.
First the first frame of the loaded swf start playing when the Event.INIT triggers not when the Event.COMPLETE triggers. The event init triggers when the first frame can be played while the complete triggers when the whole swf is loaded.
Second stage or root is only available in the loaded swf when it had be added to a display list that is added to the stage. A loaded swf has no stage or root when the Event.INIT triggers so the first frame of the swf should not have any reference to stage or root.
If you do not use a document class with your swf then make your first frame empty with just a stop() then once loaded and added to a display list make your swf go to frame 2 where you have all your code.

Trouble with addChild/removeChild and display list order in ActionScript3

I am working on a project, which includes a Lake symbol that the player can throw stones into, which in turn causes octopi to rise out of the lake in the positions that each stone hits the lake.
There is also a symbol for the splash made by the stone which will appear after the stone hits and before the octopus appears.
It is likely that there will be many octopi on the screen at the same time and they need to be ordered in the display list so that the ones that should appear further back are behind the others.
Each instance of these symbols should only play once and then be removed.
My code for this makes use of the different add/remove child method alongside for loops, conditionals and arrays which I have put together with the help of various tutorials and forums.
The problem I have is that when you click on the lake two or more times in quick succession, the stone and the splash symbols aren't removed properly and often keep looping.
Here is the code I am using. Any ideas?
var stone:Stone;
var stoneSplash:StoneSplash;
var octopus1:Octopus1;
var octopus2:Octopus2;
var whichOctopus:Array = [addOctopus1, addOctopus2];
var octopusScale:Number;
var octopusContainer:MovieClip = new MovieClip;
lake.lakeHitArea.addEventListener(MouseEvent.CLICK, onClickLake);
//Add octopusContainer to the stage's display list just above the Lake
addChildAt(octopusContainer,getChildIndex(lake) + 1);
octopusContainer.x = 0;
octopusContainer.y = 0;
function onClickLake(e:MouseEvent):void
{
trace("CLICK");
throwStone(mouseX, mouseY);
}
function throwStone(stonePositionX:int, stonePositionY:int)
{
stone = new Stone();
stone.x = stonePositionX;
stone.y = stonePositionY;
addChild(stone);
addEventListener(Event.ENTER_FRAME, removeStone);
}
function removeStone(e:Event):void
{
var count:int = numChildren;
var children:Array = [count];
//load all the children of the component into an Array
for (var i:int=0; i<count/* -1*/; i++)
{
children[i] = getChildAt(i/* + 1*/);
}
for (i=0; i<count/* - 1*/; i++)
{
if (children[i] is Stone)
{
if (children[i].currentFrameLabel == "Splash")
{
stoneSplash = new StoneSplash();
octopusContainer.addChild(stoneSplash);
stoneSplash.x = children[i].x;
stoneSplash.y = children[i].y;
}
if (children[i].currentFrameLabel == "end")
{
octopusContainer.removeChild(stoneSplash);
var positionX:int = children[i].x;
var positionY:int = children[i].y;
addOctopus(positionX, positionY);
removeChild(children[i]);
}
}
}
}
function addOctopus(positionX, positionY)
{
var o:int = Math.round(randomNumber(0,1));
whichOctopus[o](positionX, positionY);
reorderDisplayList();
addEventListener(Event.ENTER_FRAME, removeOctopus);
}
function addOctopus1(positionX: int, positionY:int):void
{
// if (whichOctopus1 == true)
// {
// var octopus:* = octopus1_1;
// }
// else
// {
// octopus = octopus1_2;
// }
octopus1 = new Octopus1();
var octopus:DisplayObject = octopus1;
octopusContainer.addChild(octopus);
octopus.x = positionX;
octopus.y = positionY;
octopusScale = randomNumber(0.5,0.85);
octopus.scaleX = octopusScale;
octopus.scaleY = octopusScale;
trace("children = " + octopusContainer.numChildren);
testPosition(octopus);
}
function addOctopus2(positionX: int, positionY:int):void
{
// if (whichOctopus2 == true)
// {
// var octopus:* = octopus2_1;
// }
// else
// {
// octopus = octopus2_2;
// }
octopus2 = new Octopus2();
var octopus:DisplayObject = octopus2;
octopusContainer.addChild(octopus);
octopus.x = positionX;
octopus.y = positionY;
octopusScale = randomNumber(0.25,0.5);
octopus.scaleX = octopusScale;
octopus.scaleY = octopusScale;
trace("children = " + octopusContainer.numChildren);
testPosition(octopus);
}
function testPosition(octopus:Object):void
{
trace(octopus)
for (var i:int = 0; i < 200; i++)
{
if (lake.hitTestPoint(octopus.x + octopus.hitTestBox1.x * octopus.scaleX,octopus.y + octopus.hitTestBox1.y * octopus.scaleY,true))
{
break;
}
else
{
octopus.x++;
}
}
for (i = 0; i < 100; i++)
{
if (lake.hitTestPoint(octopus.x + octopus.hitTestBox2.x * octopus.scaleX,octopus.y + octopus.hitTestBox2.y * octopus.scaleY,true))
{
break;
}
else
{
octopus.y--;
}
}
for (i = 0; i < 200; i++)
{
if (lake.hitTestPoint(octopus.x + octopus.hitTestBox3.x * octopus.scaleX,octopus.y + octopus.hitTestBox3.y * octopus.scaleY,true))
{
break;
}
else
{
trace(i);
octopus.x--;
}
}
for (i = 0; i < 100; i++)
{
if (lake.hitTestPoint(octopus.x + octopus.hitTestBox1.x * octopus.scaleX,octopus.y + octopus.hitTestBox1.y * octopus.scaleY,true))
{
break;
}
else
{
octopus.y--;
trace(i);
}
}
}
function randomNumber(min:Number, max:Number):Number
{
return Math.random() * (max - min) + min;
}
function reorderDisplayList():void
{
//the number of children in our component
var count:int = octopusContainer.numChildren;
var children:Array = [count];
//load all the children of the component into an Array
for (var i:int=0; i<count; i++)
{
children[i] = octopusContainer.getChildAt(i);
}
//sort the Array children based on their 'y' property
children.sortOn("y", Array.NUMERIC);
//re-add the children to the component ;
//in the order of the sorted Array we just created.
//When we add the children using 'addChild' it will
//be added at the top of the component's displaylist
//and will automatically be removed from its original position.
for (i=0; i<count/* - 1*/; i++)
{
if (children[i] is Octopus1 || children[i] is Octopus2)
{
// trace("child = " + children[i] + " at i: " + i);
octopusContainer.removeChild(children[i]);
octopusContainer.addChild(children[i]);
}
}
}
function removeOctopus(e:Event):void
{
var count:int = octopusContainer.numChildren;
var children:Array = [count];
//load all the children of the component into an Array
for (var i:int=0; i<count/* -1*/; i++)
{
children[i] = octopusContainer.getChildAt(i/* + 1*/);
}
for (i=0; i<count/* - 1*/; i++)
{
if (children[i] is Octopus1 || children[i] is Octopus2)
{
trace(i);
trace("Is an octopus");
if (children[i].currentFrame >= 202)
{
octopusContainer.removeChild(children[i]);
}
}
}
}
I would greatly appreciate any advice to help me overcome this hurdle and continue with my project.
Thank you in advance.
Chris Collins.
Your issue (or at least one of them) is that your code will only remove the most recent StoneSplash. So if you click a bunch of times in between the splash and end animation , only the last clicked one will get removed.
This is because you are using a global var (stoneSplash) to reference the splash, and it gets overwritten to the new one. You need to either add a splash reference on the stone itself, or create a dictionary so you know which splash goes with which stone.
Here would be one way:
if (children[i].currentFrameLabel == "Splash")
{
stoneSplash = new StoneSplash();
MovieClop(children[i]).stoneSplash = stoneSplash; //add a reference the splash on the stone itself
Then later, instead of octopusContainer.removeChild(stoneSplash); do:
octopusContainer.removeChild(MovieClop(children[i]).stoneSplash);
This way your removing the correct splash that goes with this stone.
Here would be a much cleaner way to architect this instead of using an enter frame handler:
On your Stone class timeline, put the following code on your Splash and End frames respectively:
Splash frame: this.dispatchEvent(new Event("Splash"));
End frame: this.dispatchEvent(new Event("End"));
Listen for those events when you create a new stone instance:
stone = new Stone();
stone.x = stonePositionX;
stone.y = stonePositionY;
stone.addEventListener("Splash", splashHandler,false,0,true);
stone.addEventListener("End",removeStone,false,0,true);
addChild(stone);
Respond to those events appropriately:
function splashHandler(e:Event):void {
var stone:Stone = e.currentTarget as Stone;
stoneSplash = new StoneSplash();
//you need a reference to the splash from the stone class - it would be best to create a class file and add a public property called splashObj and then just use stone.splashObj = new StoneSplash();
MovieClip(stone).stoneSplash = stoneSplash; //so on the end event we can read this var to remove stoneSplash
octopusContainer.addChild(stoneSplash);
stoneSplash.x = stone.x;
stoneSplash.y = stone.y;
}
function removeStone(e:Event):void {
var stone:Stone = e.currentTarget as Stone;
octopusContainer.removeChild(MovieClip(stone).stoneSplash);
addOctopus(stone.x, stone.y);
removeChild(stone);
}

as3 Quadtree being slow

This is my quadtree class, but i haven't added the collision detection yet, in all the examples online they can get 500 + at 60 fps with collision detection but my one only running at 20 fps without collision detection.
I'm following this tutorial http://gamedevelopment.tutsplus.com/tutorials/quick-tip-use-quadtrees-to-detect-likely-collisions-in-2d-space--gamedev-374 which is java but im using as3
public class Quadtree extends Entity
{
private var Max_objects:int = 1;
private var Max_levels:int = 5;
private var level:int;
private var objects:Vector.<Rectangle>;
public var rectangle:Rectangle;
public var Quadtree_list:Vector.<Quadtree>;
public function Quadtree(tmp_level:int , tmp_rec:Rectangle)
{
level = tmp_level;
objects = new Vector.<Rectangle>();
rectangle = tmp_rec;
Quadtree_list = new Vector.<Quadtree>();
Quadtree_list.length = 3;
}
public function clear():void
{
objects.length = 0;
for (var i:Number = 0; i < Quadtree_list.length ; i++)
{
if (Quadtree_list[i] != null)
{
Quadtree_list[i].clear();
world.remove(Quadtree_list[i]);
Quadtree_list[i] = null;
}
}
}
public function split():void
{
var subWidth:int = rectangle.width / 2;
var subHeight:int = rectangle.height / 2;
var xx:int = rectangle.x;
var yy:int = rectangle.y;
Base._world.add(Quadtree_list[0] = new Quadtree(level + 1, new Rectangle(xx + subWidth, yy, subWidth, subHeight)));
Base._world.add(Quadtree_list[1] = new Quadtree(level+1,new Rectangle(xx ,yy,subWidth,subHeight)));
Base._world.add(Quadtree_list[2] = new Quadtree(level+1,new Rectangle(xx,yy + subHeight,subWidth,subHeight)));
Base._world.add(Quadtree_list[3] = new Quadtree(level+1,new Rectangle(xx + subWidth,yy + subHeight,subWidth,subHeight)));
}
/*
* Determine which node the object belongs to. -1 means
* object cannot completely fit within a child node and is part
* of the parent node
*/
public function get_index(tmp_rect:Rectangle):Number
{
var index:int = -1;
var verticalMidpoint:Number = rectangle.x + (rectangle.width / 2);
var horizontalMidpoint:Number = rectangle.y + (rectangle.height / 2);
// Object can completely fit within the top quadrants
var topQuadrant:Boolean = (tmp_rect.y < horizontalMidpoint && tmp_rect.y + tmp_rect.height < horizontalMidpoint);
// Object can completely fit within the bottom quadrants
var bottomQuadrant:Boolean = (tmp_rect.y > horizontalMidpoint);
// Object can completely fit within the left quadrants
if (tmp_rect.x < verticalMidpoint && tmp_rect.x + tmp_rect.width < verticalMidpoint)
{
if (topQuadrant)
{
index = 1;
}
else if (bottomQuadrant)
{
index = 2;
}
}
else
// Object can completely fit within the right quadrants
if (tmp_rect.x > verticalMidpoint)
{
if (topQuadrant)
{
index = 0;
}
else if (bottomQuadrant)
{
index = 3;
}
}
return index;
}
/*
* Insert the object into the quadtree. If the node
* exceeds the capacity, it will split and add all
* objects to their corresponding nodes.
*/
public function insert(tmp_rect:Rectangle):void
{
if (Quadtree_list[0] != null)
{
var index:int = get_index(tmp_rect);
if (index != -1)
{
Quadtree_list[index].insert(tmp_rect)
return;
}
}
objects.push(tmp_rect);
if (objects.length > Max_objects && level < Max_levels)
{
if (Quadtree_list[0] == null)
{
split();
}
var i:int = 0;
while (i < objects.length)
{
var indexx:int = get_index(objects[i]);
if (indexx != -1)
{
Quadtree_list[indexx].insert(objects[i]);
objects.splice(i, 1);
}
else
{
i++;
}
}
}
}
Can you see why it's not performing very well?
Hard to say for certain without seeing exactly how you're using it. Plus it extends Entity which could be doing ...anything :)
I'm not an expert on Quadtrees either, but if you're calling split() a lot, it looks like it could end up being taxing - lots of instantiation calls to new Quadtree and new Rectangle. If this is indeed a bottleneck, you could look into instantiating one rectangle instance that you just pass around. Same with Quadtree. Or use object pooling so you're at least recycling instead of creating new things like crazy.
I hope that helps :)

No looping of generated audio stored in ByteArray and loaded into Sound object?

EDIT 2: This problem still exists, but seems to be a bug. Adobe Sound class does not send the Sound.length value after loading a ByteArray. Here is the bug report I filed (please vote for it!):
https://bugbase.adobe.com/index.cfm?event=bug&id=3749649
= = = = =
The following code works to produce a sound once -- it plays the correct sound, but does not loop. I believe it should. I cannot seem to debug it
It also does not seem to throw a SOUND_COMPLETE event. Am I missing something here?
EDIT: Still broken, but I updated the code below so you can test it. Just copy to a class and call testSound():
private var NUM_SAMPLES:int = 16384 * 2;
private var soundByteArray:ByteArray;
private var volume:Number = 1;
private var channel:SoundChannel = new SoundChannel();
private var RATE:int = 44100;
public function testSound():void
{
var baseSound:Sound = new Sound();
storeAudio();
var trans:SoundTransform = new SoundTransform(volume, 0);
SoundMixer.soundTransform = trans;
soundByteArray.position = 0;
baseSound.loadPCMFromByteArray(soundByteArray, NUM_SAMPLES, "float", true, RATE);
soundByteArray.position = 0;
baseSound.addEventListener(flash.events.Event.SOUND_COMPLETE, onPlaybackComplete);
trace("loaded 1: " + baseSound.length);
trace("loaded 2: " + baseSound.bytesLoaded);
trace("loaded 3: " + baseSound.bytesTotal);
channel = baseSound.play(0, 20, trans);
channel.addEventListener(flash.events.Event.SOUND_COMPLETE, onPlaybackComplete);
}
protected function onPlaybackComplete(event:flash.events.Event):void
{
trace("onPlaybackComplete" + channel.position);
}
private function storeAudio():void
{
soundByteArray = new ByteArray();
for (var i:Number = 0; i < NUM_SAMPLES; i++)
{
soundByteArray.writeFloat(
Math.sin(
((i / RATE))
* Math.PI * 2 * 440
)
);
soundByteArray.writeFloat(
Math.sin(
((i / RATE))
* Math.PI * 2 * 440
)
);
}
trace("storeAudio i = " + i + ", " + soundByteArray.length);
}
OK I appreciate you've accepted this issue as a bug and possibly moved on. However I used a mini-hack to replay the loadPCMFromByteArray. Rather than rely on Sound.Complete just write code that knows when the PCM audio bytes length is fully reached.
By converting bytes length to milliseconds and using channel.position you will get essentially the same result as the .Complete feature (anyone can correct me if I'm wrong here). If you really need an event firing, i.e for the sake of some function that relies on that feedback, then you can simply despatch your own custom event and listen out for that one instead of Sound.Complete
From testing I reason that the continous glitch/click sound is actually Flash trying to play the sound forward but not moving very far into the PCM data's final bytes.
Think of it as a very audible version of an E-O-F error found in ByteArrays but then also running from an internal (never ending?) while loop just to pleasure your ears.
Some notes before code:
At measured sound ending I tried.. soundByteArray.position = 0; Not good! channel.position trace shows as stuck on the Ending pos amount. Also tried
channel = baseSound.play(0); Not good! channel.position trace shows as stuck at the zero pos. Both gave stuttering sound
Also whilst I didnt try it this time, I have looped sampleData before without glitches so I'm sure it could be worth considering copying the PCM over to a sampleData setup also and see if that works better with looping & firing a Sound.Complete etc.
If it's just simple tones you are generating & looping you don't even need to use PCMdata just go with dynamic sound generation using sampleData as first choice. If however you involve PCM samples of vocals or band music then you will need the hack below to replay on sound ending
So anyways, for now here is some code demonstration to illustrate the looping hack
package
{
import flash.display.MovieClip;
import flash.events.*;
import flash.utils.*;
import flash.media.*;
import flash.text.*;
public class testSound extends MovieClip
{
private var BIT_TYPE:int = 16; //16bit audio
private var RATE:int = 44100;
private var NUM_SAMPLES:int = 8192; //16384 * 2;
private var NUM_CHANNEL:int = 2; //if stereo
private var NUM_TEMP:int =0; //adjustable number for test without changing others
public var NUM_TONE_FREQ:int = 440;
private var soundByteArray:ByteArray;
private var volume:Number = 1;
private var channel:SoundChannel = new SoundChannel();
public var final_samples:int = 0;
public var time_total:Number; //dont use Integers here - wont always be correct
public var time_kbps:Number; //"bytes per second" count
public var loop_count:int = 0;
public var timerCount:Number = 0;
public var timerSecs:Number = 0;
public var timer:Timer;
public var trans:SoundTransform;
public var baseSound:Sound = new Sound();
public var timeText:TextField;
public var txtFormat:TextFormat;
public function testSound():void
{
//correct NUM_SAMPLES helps with end-time check
NUM_SAMPLES *= NUM_CHANNEL * BIT_TYPE;
trans = new SoundTransform(volume, 0);
channel.soundTransform = trans; //SoundMixer.soundTransform = trans;
soundByteArray = new ByteArray();
soundByteArray.position = 0;
//setup textField for debug feedback
setupTextFBack();
//generate PCM
storeAudio();
}
protected function onPlaybackComplete(event:flash.events.Event):void
{
//only works if you are passing your PCM to sampleData events,
trace("onPlaybackComplete" + channel.position);
}
private function storeAudio():void
{
for (var i:Number = 0; i < NUM_SAMPLES; i++)
{
soundByteArray.writeFloat
( Math.sin((i / RATE) * Math.PI * 2 * NUM_TONE_FREQ) );
soundByteArray.writeFloat
( Math.sin((i / RATE) * Math.PI * 2 * NUM_TONE_FREQ) );
}
trace("storeAudio samples (i) = " + i + ", ByteArray length: " + soundByteArray.length);
final_samples = i;
playAudio();
}
public function playAudio():void
{
soundByteArray.position = 0;
baseSound.loadPCMFromByteArray(soundByteArray, final_samples, "float", true, RATE);
channel = baseSound.play(); //channel = baseSound.play(0, 0, trans);
setupTimeCount(); //count when play starts
time_kbps = (RATE * NUM_CHANNEL * BIT_TYPE) / 4; //not /8 because time is never doubled on stereo
time_total = (soundByteArray.length / time_kbps);
time_total = Math.round(time_total * 100) / 100;
trace ("=== DEBUG INFO : (loop: "+loop_count+ ") =========================================");
trace ("*** Playing beyond Total Time (PCM end) creates sound glitch issues ");
trace ("*** Re-Play on a continous Tone will creates short click when it stops to replay ");
trace ("*** If PCM was music/vocals this click might not be perceived by ear if looped right");
trace ("====================================================================");
trace ("Total Kb/sec : " + time_kbps + " bytes per sec");
trace ("Total time : " + time_total + " secs" );
//trim Total millisecs just to avoid any glitches/clicks. Test & fine-tune
time_total -= 0.314; //PI divided by 10. Need fine-tune? Hell Yes!
trace ("Total (trim) : " + time_total + " secs" );
}
public function setupTimeCount():void
{
timer = new Timer(100);
timer.addEventListener(TimerEvent.TIMER, timerHandler);
timerCount = 0;
timer.start();
}
function timerHandler(Event:TimerEvent):void
{
timerCount += 100;
checkTime(timerCount);
//trace("channel.pos = " + channel.position); //for debug only
}
function checkTime(miliseconds:int) : void
{
timerSecs = miliseconds/1000;
timeText.text = ("elapsed : " + timerSecs);
//if (timerSecs >= time_total)
if ( channel.position >= (time_total * 1000) )
{
reloopAudio();
}
}
function reloopAudio():void
{
channel.stop(); //else you get stutter from going forward
timer.stop();
trace("attempting replay / loop..");
loop_count += 1;
playAudio(); //redo playing function
}
public function setupTextFBack():void
{
txtFormat = new TextFormat();
txtFormat.size = 20;
txtFormat.font = "Arial";
timeText = new TextField();
timeText.defaultTextFormat = txtFormat;
timeText.antiAliasType = AntiAliasType.ADVANCED;
timeText.x = stage.stageWidth / 2 ;
timeText.y = stage.stageHeight / 2 ;
timeText.textColor = 0xFF0000;
timeText.text = " ";
timeText.width = 200;
addChild(timeText);
}
}
}

How to check if UPLOAD_COMPLETE_DATA event was not dispatched?

Is there a way how to check if UPLOAD_COMPLETE_DATA event was not dispatched after COMPLETE in Flash?
I'm working on file uploader. It uploads file after file – after COMPLETE next file starts uploading – this is repeated for every file. On last file on COMPLETE if there is no next file allCompleteHandler is dispatched and if some errors occurs it shows that errors at once trough javascript to the user.
I handle that errors in javascript – it stores every error to variable in javascript and after all completed it shows that errors.
The problem is:
I can't store error from the last file (error from server that I get through UPLOAD_COMPLETE_DATA) because this is dispatched after COMPLETE (after allCompleteHandler).
I need to solve this because I don't wont to show javascript alert box for every invalid file (if there are 100 files for example).
var parameters:Object = LoaderInfo(this.root.loaderInfo).parameters,
fileFilter:Array,
browseFilter:Array = [],
files:FileReferenceList = new FileReferenceList(),
selectedFiles:Array = [],
file:FileReference = new FileReference(),
url:URLRequest = new URLRequest(parameters.phpScript),
uploadFolder,
postMaxSize:Number,
uploadMaxFilesize:Number,
maxInputTime:int,
speedTimer:Timer = new Timer(1000),
uploadTimer:Timer = new Timer(60000, 1),
count:int = 0,
totalBytes:Number = 0,
processedBytes:Number = 0,
currentBytes:Number = 0,
currentBytes2:Number = 0,
lastBytes:Number = 0,
uploadSpeed:Number = 0,
inProgress:Boolean = false;
// Browse filter setup
fileFilter = ExternalInterface.call(parameters.fileManager + ".getFileFilter");
if (fileFilter) {
for (var i:int = 0; i < fileFilter.length; i++) {
browseFilter.push(new FileFilter(fileFilter[i][0], fileFilter[i][1]));
}
}
function clickHandler(event:MouseEvent):void {
if (!inProgress) {
uploadFolder = ExternalInterface.call(parameters.fileManager + ".getCurrentFolder");
if (uploadFolder != undefined) {
files.browse(browseFilter);
}
}
}
stage.addEventListener(MouseEvent.CLICK, clickHandler);
function selectHandler(event:Event):void {
var variables:URLVariables = new URLVariables();
variables.folder = uploadFolder;
url.data = variables;
url.method = URLRequestMethod.POST;
selectedFiles = files.fileList;
postMaxSize = ExternalInterface.call(parameters.fileManager + ".getPostMaxSize");
postMaxSize = postMaxSize ? postMaxSize : 50 * 1024 * 1024;
uploadMaxFilesize = ExternalInterface.call(parameters.fileManager + ".getUploadMaxFilesize");
uploadMaxFilesize = uploadMaxFilesize ? uploadMaxFilesize : 50 * 1024 * 1024;
maxInputTime = ExternalInterface.call(parameters.fileManager + ".getMaxInputTime");
maxInputTime = maxInputTime ? maxInputTime : 60;
// Get total size of selected files
for (var i:int = 0; i < selectedFiles.length; i++) {
totalBytes += selectedFiles[i].size;
}
ExternalInterface.call(parameters.fileManager + ".selectHandler", {
selectedFiles : selectedFiles,
totalBytes : totalBytes
});
// Start upload process
inProgress = true;
speedTimer.start();
upload();
}
files.addEventListener(Event.SELECT, selectHandler);
function upload():void {
uploadTimer.reset();
currentBytes2 = 0;
if (count) {
processedBytes += file.size;
if (currentBytes < processedBytes) {
currentBytes = processedBytes;
}
}
if (selectedFiles.length) {
file = FileReference(selectedFiles.shift());
count++;
ExternalInterface.call(parameters.fileManager + ".beforeUploadHandler", {
file : file,
currentBytes : currentBytes
});
if (file.size <= postMaxSize) {
if (file.size <= uploadMaxFilesize) {
file.addEventListener(SecurityErrorEvent.SECURITY_ERROR, securityErrorHandler);
file.addEventListener(HTTPStatusEvent.HTTP_STATUS, httpStatusHandler);
file.addEventListener(Event.OPEN, openHandler);
file.addEventListener(IOErrorEvent.IO_ERROR, ioErrorHandler);
file.addEventListener(ProgressEvent.PROGRESS, progressHandler);
file.addEventListener(Event.COMPLETE, completeHandler);
file.addEventListener(DataEvent.UPLOAD_COMPLETE_DATA, uploadCompleteDataHandler);
file.upload(url);
} else {
ExternalInterface.call(parameters.fileManager + ".uploadMaxFilesizeHandler", file);
upload();
}
} else {
ExternalInterface.call(parameters.fileManager + ".postMaxSizeHandler", file);
upload();
}
} else {
ExternalInterface.call(parameters.fileManager + ".allCompleteHandler", {
currentBytes : currentBytes
});
speedTimer.stop();
count = 0;
totalBytes = 0;
processedBytes = 0;
currentBytes = 0;
lastBytes = 0;
uploadSpeed = 0;
inProgress = false;
}
}
function securityErrorHandler(event:SecurityErrorEvent):void {
ExternalInterface.call(parameters.fileManager + ".securityErrorHandler", event);
}
function httpStatusHandler(event:HTTPStatusEvent):void {
ExternalInterface.call(parameters.fileManager + ".httpStatusHandler", event);
selectedFiles = [];
}
function openHandler(event:Event):void {
ExternalInterface.call(parameters.fileManager + ".openHandler", event);
uploadTimer.delay = maxInputTime * 1000;
uploadTimer.start();
}
function ioErrorHandler(event:IOErrorEvent):void {
ExternalInterface.call(parameters.fileManager + ".ioErrorHandler", event);
upload();
}
function progressHandler(event:ProgressEvent):void {
currentBytes += event.bytesLoaded - currentBytes2;
currentBytes2 = event.bytesLoaded;
ExternalInterface.call(parameters.fileManager + ".progressHandler", {
current : event,
currentBytes : currentBytes
});
}
function completeHandler(event:Event):void {
ExternalInterface.call(parameters.fileManager + ".completeHandler", event);
upload();
}
function uploadCompleteDataHandler(event:DataEvent):void {
ExternalInterface.call(parameters.fileManager + ".uploadCompleteDataHandler", "(" + event.data + ")");
}
function updateUploadSpeed(event:TimerEvent):void {
if (currentBytes > lastBytes) {
uploadSpeed = currentBytes - lastBytes;
ExternalInterface.call(parameters.fileManager + ".uploadSpeedHandler", uploadSpeed);
lastBytes = currentBytes;
}
}
speedTimer.addEventListener(TimerEvent.TIMER, updateUploadSpeed);
function maxInputTimeHandler(event:TimerEvent):void {
ExternalInterface.call(parameters.fileManager + ".maxInputTimeHandler", file);
}
uploadTimer.addEventListener(TimerEvent.TIMER, maxInputTimeHandler);
function cancelUpload():void {
file.cancel();
selectedFiles = [];
upload();
}
ExternalInterface.addCallback("cancelUpload", cancelUpload);
I can do this by setting up my PHP script to always return data and check this data and start next file upload with UPLOAD_COMPLETE_DATA, but I don't like this (it can be slow maybe I think)...
It is simple question but maybe hard to explain why I need that. Thank you for your help!
okay, what about http://help.adobe.com/en_US/AS3LCR/Flash_10.0/flash/net/FileReference.html#event:progress
and sample
http://help.adobe.com/en_US/AS3LCR/Flash_10.0/flash/net/FileReference.html#includeExamplesSummary
http://blog.flexexamples.com/2007/10/30/using-for-the-filereference-classs-uploadcompletedata-event-to-capture-data-from-a-server-side-script/
http://bcdef.org/2006/12/09/new-event-for-as3-filereference-uploadcompletedata/
From the docs:
uploadCompleteData:
Dispatched after data is received from the server after a successful upload. This event is not dispatched if data is not returned from the server.
complete:
Dispatched when download is complete or when upload generates an HTTP status code of 200. For file download, this event is dispatched when Flash Player or Adobe AIR finishes downloading the entire file to disk. For file upload, this event is dispatched after the Flash Player or Adobe AIR receives an HTTP status code of 200 from the server receiving the transmission.
So, if you're uploading a file and not expecting any kind of response from the server you can just use complete. However, if you're expecting the server to return data to you in the upload's response then you need to wait for uploadCompleteData.