Random 'Undefined' Error - actionscript-3

I am a beginner in ActionScript 3, I am trying to traverse an array of MovieClips and pick hundred random Clips out of that and place those instances on stage but sometimes the code produce an undefined error. I am confused why as the code works well most of the times and there are never any compile time errors.
Here is my code:
for(var i:int = 0; i<100;i++)
{
var rndNum:Number = Math.round(Math.random()*arr.length);
this.addChild(arr[rndNum]);
}

Because you are rounding the randomly generated index which could result in an index out of bound Exception, and that is because you are generating a number up to the array's Length and the number representing the total length of an array can never be a valid index for that Array since the index starts at 0.
Simply change the Math.round to Math.floor
var rndNum:Number = Math.floor(Math.random()*arr.length);
or do a -1 from the arr's length:
var rndNum:Number = Math.round(Math.random()*(arr.length-1));

Related

Actions with a list of instances - AS3

what is the best way to do an action with many instances at the same time?
Lets say I have 50 movieclip instances called A1 to A50, and I want to run an action with only A20 to A35.
For example:
(A20-A35).gotoAndStop(2)
You want an algorithm operation called loop. You are not able to abstractly address things in bunches at once, but you can loop and iterate the bunch one by one which produces basically the same result. Please read this: https://en.wikipedia.org/wiki/Control_flow#Loops When you need to do a quantity of similar operations it is always loop.
With regard to your problem:
// Loop iterator from 20 to 35 inclusive.
for (var i:int = 20; i <= 35; i++)
{
trace("");
// Compose the name of the MovieClip to retrieve.
var aName:String = "A" + i;
trace("Retrieving the MovieClip by name", aName);
// Retrieve the instance by its instance name.
var aChild:DisplayObject = getChildByName(aName);
// Sanity checks about what exactly did you find by that name.
if (aChild == null)
{
// Report the essence of the failure.
trace("Child", aName, "is not found.");
// Nothing to do here anymore, go for the next i.
continue;
}
else if (aChild is MovieClip)
{
// Everything is fine.
}
else
{
// Report the essence of the failure.
trace("Child", aName, "is not a MovieClip");
// Nothing to do here anymore, go for the next i.
continue;
}
// Type-casting: tell the compiler that the child is actually
// a MovieClip because DisplayObject doesn't have gotoAndStop(...)
// method so you will get a compile-time error even if you are
// sure the actual object is a valid MovieClip and definitely has
// the said method. Compile-time errors save us a lot of pain
// we would get from run-rime errors otherwise, so treasure it.
var aClip:MovieClip = aChild as MovieClip;
trace(aClip, "is a MovieClip and has", aClip.totalFrames, "frames.");
if (aClip.totalFrames < 2)
{
// Nothing to do here anymore, go for the next i.
continue;
}
// Now you can work with it.
aClip.gotoAndStop(2);
}
Now that you understand the while idea step by step, if you are sure all of them are present and all of them are MovieClips you can go for a shorter version:
for (var i:int = 20; i <= 35; i++)
{
(getChildByName("A" + i) as MovieClip).gotoAndStop(2);
}
UPD: You can as well address children with square bracket access operator.
for (var i:int = 20; i <= 35; i++)
{
// You can skip type-casting as this["A" + i] returns an untyped reference.
this["A" + i].gotoAndStop(2);
}
Yet there are differences and complications. Method getChildByName(...) always returns a DisplayObject with the given name (or null if none found). Square brackets operator returns an untyped OOP field of the current object.
It will not work with dynamically added children (unless you pass their references to the respective fields).
It will not work if "Automatically Declare Stage Instances" publish option is off.
Finally, this["A" + 1] and A1 are not exactly the same because the latter could refer to a local method variable rather than object member.
I'm not saying that square brackets are evil, they're as fine, yet, as always, programming is not a magick thus understanding what you are doing is the key.

as3 add multiple MovieClips into random MovieClips

I'm trying to to do some dynamic MovieClips placement here. I've been trying for a couple'o days and I can't find the right way to do the trick. It's the continuation for this. I didn't manage to properly make my MC's appear in the triangle, so I've made a pyramid of rectangles (suits my case better, beacuse I can change each of'em, to better fit my desired shape - a not-so-regular triangle).
Here's the code with some comments:
import flash.events.MouseEvent;
import flash.display.MovieClip;
btn_toys_2.confirm.addEventListener(MouseEvent.MOUSE_UP, confirmToys);
var toysPlc:Array = new Array(); //an array for a bunch of rectangles
var toAdd:int = 100 //this is supposed to be dynamic, user defined
var toy:MovieClip = new shar_001; //an MC from a library
for (var j:int=0; j<33; j++){
toysPlc.push("tPl_" + j); //here I add that bunch of rects into an array
}
function confirmToys(e:MouseEvent):void{
for (var k:int=0; k<toAdd; k++){ //supposed to add an "toAdd" amount of "toys"
var p:int = Math.random()*toysPlc.length; //^do so in a random rect
toysPlc[p].addChild(toy); //supposed to place a toy in a random rect
toy.x = Math.random()*toysPlc[p].width; //positioning
toy.y = Math.random()*toysPlc[p].height; //positioning
}
}
The error I get is: TypeError: Error #1006: value is not a function.
What I DID manage is to place a single toy in a random of these rects, tho I don't remember how :)
Thanks in advance!
EDIT: null asked me to clarify the case, so here's the whole picture:
I've got:
- triangle-like MC (since a triangle MC is a rectangle for flash anyway, I've solved this by creating a pyramid of 33 rectangles, layered on each other);
- toys (multiple frames to change whenever I need to);
- text field (to input the quantity of desired toys);
- confirm button (to make the magic happen once clicked);
Now I need a user to input a 3-digit number in the input field, press "confirm", and 0.4 of that quantity is supposed to appear in the "triangle-like MC".
For example: user inputs a number: 600, the end value is 600*0.4=240. Now 240 "toys" are randomly spreaded between my 33 rectangles, within their width (which is different for every single one).
Hope that explains a bit more.
A simplified sample of what I need here.
Is there a way to fill an array with MovieClips rather than String values? That would be THE answer to this question here.
There is in fact more than one way:
Place all the instance names into the Array when you create it:
var rectangles:Array = [instanceName1, instanceName2];
there are no quotation marks, which create string literals. Just the names.
This approach quickly becomes impractical for large numbers of objects.
Given that the instance names have a number component, iterate through the names in conjunction with getChildByName(). I assume that this is what you were trying with in your question:
var rectangles:Array = []; // initialise empty array
for (var j:int=0; j<33; j++){
rectangles.push(getChildByName("tPl_" + j));
}
original answer
toysPlc.push("tPl_" + j); //here I add that bunch of rects into an array
No you don't. You are filling the Array with String objects. It's totally unrelated to any rectangle whatsoever.
Now this next line, tries to call a function on each String, which fails.
toysPlc[p].addChild(toy);
The above is equivalent to
"tPl_0".addChild(toy);
"tPl_1".addChild(toy);
// etc.
A String doesn't have that method addChild.

Getting number of bytes of a bitmap

Goal is to show progress of jpeg encoding from bitmap. I have couple of bitmaps that need to be encoded. So I get the total number of bytes as was suggested here:
for (var i:int = 0; i < bitmaps.length; i++)
{
bmp = bitmaps[i];
total_bytes += bmp.getPixels(bmp.rect).length;
}
Then I'm trying to show progress when doing asychronous encoding. I get a ProgressEvent which gives me bytesLoaded. So I calculate the progress like so:
total_loaded_bytes += event.bytesLoaded;
var percentage:int = ((total_loaded_bytes / total_bytes) * 100);
However, total_bytes does not add up to total_loaded_bytes. Total bytes loaded is way highter.
A wrong approach to use bytesLoaded property. This is not to be blandly added up, as this contains the total of already loaded bytes bu the Loader object that issued the event. And a wrong approach on getting total bytes too, you need to use event.bytesTotal from within the progress event listener, since you are loading bytes, not pixels. Even if you are uploading. Also, the exact progress may be totally unavailable for asynchronous encoding, you are only displaying the uploading/downloading progress.
Update: to receive an accumulated progress, do something like this:
var loaders:Array=[]; // array of loaders. Fill manually
var progress:Array=[]; // fill with zeroes alongside loaders, should be same size
function updateProgress(e:ProgressEvent):void {
var loader:Loader=e.target as Loader;
if (!loader) return; // or skip type coercion, we just need the reference
var i:int=loaders.indexOf(loader); // now get an index from array
if (i<0) return; // not found, drop it
progress[i]=e.bytesLoaded; // and update progress array with new value
// now sum up progress array and divide by aggregated bytesTotal
// this one is up to you
}

flash as3 How to remove part of byteArray?

var fData:ByteArray = new ByteArray();
I need to remove some bytes in this array, but can't find any public method in Flash to do that.
I searched something like fData.remove(start,length) but with no success.
here is a code
function _dlProgressHandler(evt:ProgressEvent):void { //this is progressEvent for URLStream
............... ///some code
var ff:ByteArray = new ByteArray();
stream.readBytes(ff,0,stream.bytesAvailable);
fileData.writeBytes(ff,0,ff.length); //stream writes into fileData byteArray
//and here is cutter:
fileData.position=0;
fileData.writeBytes(ff,100,fileData.length);
fileData.length=fileData.length-100);
}
So, fileData cut itself unpredictably sometimes.
Sometimes old blocks're found twice, sometimes they're not found at all.
You can always just only read the bytes that you want, which will have the same effect as discarding the bytes that you don't want. As a very simple example, assume you have a ByteArray that is 10 bytes long and you want to discard the first 3 bytes:
var newBytes:ByteArray = new ByteArray();
newBytes.writeBytes(fData, 2, 7);
So instead of removing the bytes that you don't want from fData, you just create a new ByteArray and only get they bytes that you want from fData.
Obviously, if the sequence of bytes you want to remove is not just a sequence from the beginning or end of fData it will be a little more complicated, but the method remains the same: read the bytes that you want, instead of removing the ones you don't.
AS-3 is actually very nice sometimes. This removes bytes from your array anywhere you want. Beginning, middle or the end. Just need to check indices to avoid IndexOutOfBounds
var array: ByteArray = ...; // create the byte array or load one
var index: int = 4;
var count: int = 5;
array.position = index;
array.writeBytes(array, index + count, array.length - (index + count));
array.length = array.length - count;
I tested this and it works well, just the checks are missing
a byte array can write to itself

adding items from an array multiple times to the stage

I have a for loop which passes 11 times:
private var currentItem:uint;
for(var i:uint = 0;i<10;i+){
addChild(arr[currentItem]);
currentItem++;
if(currentItem == arr.length){
currentItem = 0;
}
}
So the problem is that the array only contains 6 items. So when it comes to the 6th item the currentItem resets and the next 4 items that are getting added are the 4 first from the array again. Now when I trace the items, the last 4 trace "null". My question is, how can I add items from the array multiple times without losing its properties etc?
There's nothing inherently wrong with your loop. However, a DisplayObject can only be on the display list once. It can't have multiple parents or be a child of the same parent many times. That's why your code isn't working.
Update:
If you want to create new instances from a list of Classes you can do that, but your current approach wont work. This is what you need to do:
// the square bracket notation is shorthand for creating an array.
// fill the array with references to *classes* not instances
var classes:Array = [ MyClassOne, MyClassTwo, MyClassThree ];
// we run the loop much as you did, but we can make it much more compact
// by using the modulus operator
// since the array is full of classes, we can use the new operator to
// create new instances of those classes and add them to the display-list
for(var i:uint = 0; i < 10; i++ ){
addChild(new classes[i % classes.length]);
}