AS3 Is there a simple way of creating this array? - arrays

I'll have some task for the user to do, so I'll make an Array(false, false, false, ... number of tasks) and each element will become true when completed, so I'll know when all will be completed and witch one is still incomplete. Usually I create an int to count the tasks when created, and that decrease, but I need to control each item this time (you didn't complete this one...), not only the global progress.
So, I got only the number of tasks:
var Ctrl:Array = new Array();
for(var i=0; i<numberOfTasks; i++){ Ctrl.push(false); }
If I have a lot of tasks, this way may take a while freezing the execution. Is there some "automatic" way?

You can also create array and verify if array[task] is undefined , it was not set and its for You equal false .
var ctrl:Array = new Array();
var maxTasks:int = 100;// doesnt matter here
var tasksComplete:int = 0;// if You dont like to loop each time
function completeTask(id:int):void{
ctrl[id] = true;
tasksComplete ++;
}
function isTaskComplete(id:int):Boolean{
return ctrl[id]?true:false;
}

You can use dictionary instead of Array.
Like this
var dic:Dictionary = new Dictionary();
var task:Task = new Task();
task.addEventListener(Event.Complete, onTaskComplete);
dic[task] = false; //you will set false value only the task executes
task.execute();
private function onTaskComplete(e:Event):void {
var task:Task = e.target as Task;
dic[task] = true;
//check the dic if all tasks are finished
}

Related

Looping for events in actionscript 3, only get last number

So I want to code faster by making array of all button that I have, and also make array of function which index numbers are connected to the array of each buttons.
For example, buttons[0], handler events for hover is button_over_funcs[0] and for out is button_out_funcs[0].
To make it clearer (since english is not my first language), take a look at my code:
var buttons:Array = [playbtn, tutorialbtn];
var button_over_funcs:Array = new Array();
var button_out_funcs:Array = new Array();
var i = 0;
for each(var j in buttons){
j.buttonMode = true;
button_over_funcs.push(function(e:MouseEvent){
j.gotoAndPlay("hover");
});
button_out_funcs.push(function(e:MouseEvent){
j.gotoAndPlay("out");
});
j.addEventListener(MouseEvent.ROLL_OVER, button_over_funcs[i]);
j.addEventListener(MouseEvent.ROLL_OUT, button_out_funcs[i]);
i++;
}
but the j will always refer to tutorialbtn, regardless which button I hover/out. I tried for-in as well
var buttons:Array = [playbtn, tutorialbtn];
var button_over_funcs:Array = new Array();
var button_out_funcs:Array = new Array();
for(var j in buttons){
buttons[j].buttonMode = true;
button_over_funcs.push(function(e:MouseEvent){
buttons[j].gotoAndPlay("hover");
});
button_out_funcs.push(function(e:MouseEvent){
buttons[j].gotoAndPlay("out");
});
buttons[j].addEventListener(MouseEvent.ROLL_OVER, button_over_funcs[j]);
buttons[j].addEventListener(MouseEvent.ROLL_OUT, button_out_funcs[j]);
}
Both seems the same. It seems like actionscript always refers to the last value of j instead of assigning it. Do you have any idea on how to make this as I expected? Is it impossible to make this quicker and not assigning the button to do same exact stuff?
There's no point in reinventing the wheel. Use SimpleButton and be done with it.
In general: a class is the way to go when you want to define behaviour that several objects have in common.
So I found the way by myself. If you found same problem as me, you can take a look.
var buttons:Array = [playbtn, tutorialbtn];
var button_over_funcs:Array = new Array();
var button_out_funcs:Array = new Array();
function addOver(ob){
button_over_funcs.push(function(e:MouseEvent){
ob.gotoAndPlay("hover");
});
}
function addOut(ob){
button_out_funcs.push(function(e:MouseEvent){
ob.gotoAndPlay("out");
});
}
function addEvent(ob){
addOver(ob);
addOut(ob);
}
for each(var j in buttons){
addEvent(j)
}
for(var i = 0; i<= buttons.length - 1; i++){
buttons[i].buttonMode = true;
buttons[i].addEventListener(MouseEvent.ROLL_OVER, button_over_funcs[i]);
buttons[i].addEventListener(MouseEvent.ROLL_OUT, button_out_funcs[i]);
}

Cannot push past 1 element to basic AS3 Array

I am very new to this and hoping it's something that should have been obvious.
When I run the code below, the Array newHole and newArray both return 1 on the trace. Originally the code was built with only the newHole array, but I created the newArray in the hopes of troubleshooting. It did not help. The class for bulletHole contains no extra code so I didn't post that.
Thank you.
import flash.display.*;
import flash.events.*;
import flash.ui.Mouse;
Mouse.hide();
var myReticle:MovieClip;
var holeArray:Array = new Array();
var randomHole:Number = randomNumber(1, 5);
var newHole:bulletHole = new bulletHole();
var newArray:Array = new Array();
stage.addEventListener(MouseEvent.MOUSE_MOVE, followReticle);
stage.addEventListener(MouseEvent.CLICK, myFire);
stage.addEventListener(MouseEvent.CLICK, checkCount);
function followReticle(event:MouseEvent):void
{
myReticle.x = mouseX;
myReticle.y = mouseY;
}
function myFire(int):void
{
stage.addChild(newHole);
newHole.x = myReticle.x;
newHole.y = myReticle.y;
//holeArray.push(newHole);
newHole.gotoAndStop(randomHole);
//trace(holeArray.length);
}
function checkCount(int):void
{
newArray.push("A");
trace(newArray.length);
}
function randomNumber(low:Number=0, high:Number=1):Number
{
return Math.floor(Math.random() * (1+high-low)) + low;
}
Most likely the issue is that the code you've posted is running over and over again. In other words, you have a looping timeline that eventually goes back to the frame that the code you've shown is on.
Whenever that frame is reached, you have the following:
var holeArray:Array = new Array();
Which creates a new array replacing what used to be in that var.
To solve this, you either need to:
take the code out of the timeline (put it in a class file and attach that as the document class of your project)
re-architect your timeline so the first frame is only reached 1 time
put some checks in so that the code only runs the first time the frame is reached.
Here is an example of the latter option:
//just define the array, don't create it
var holeArray:Array;
//if the array is null, create it (it will only be null the first time this code is run
if(!holeArray){
holeArray = new Array();
}
This line is incorrect:
function myFire(int):void {
Because the function is triggered from a mouse event listener it should read:
function myFire(e:MouseEvent):void {
What you are doing is passing a undefined int to the function. Hope this helps.
EDIT: You should delete the clickCount event listener and function as they're not needed.
Also notice that you should move this line to the top of your myFire function or else you will keep replacing this MovieClip instead of creating it again:
var newHole:bulletHole = new bulletHole();

Select random elements from an array without repeats?

edit: I can't believe I didn't catch this sooner. Turns out my problem was re-declaring my first variables over and over again, essentially starting the program fresh instead of continuing it. To fix it, I replaced the first two lines with this:
if (initialized === undefined) {
trace("INITIALIZING");
var MCs = [];
var lastPos = "intializer";
var initialized = 1;
}
Now it works like a charm. I feel like a noob for this one; sorry to anyone whose time I wasted. I'd post this as an answer to my own question, but it won't let me since I'm still new.
Original Post follows:
I'm trying to make a flash that will randomly choose an ad, play it, and then randomly play another. To that end, I've succeeded by shuffling an array, and then gotoAndPlay-ing the label in the first element of the array, and then removing that element. At the end of each ad is gotoAndPlay(1); with all the main code being on the first frame. If the array is empty, it rebuilds it and reshuffles it.
The problem is, I don't want it to repeat any ads until its run through all of them; I think I've got that down, but I'm not positive. Further, I don't want the last element in the array to be the same as the first in the new one, so the same ad won't ever show twice in a row. I'm trying to have it detect if the element it just used matches the one it's about to use, and reshuffle if that happens, but in my testing it continues to occasionally show the same ad twice in a row.
I'm obviously doing something wrong, but being entirely new to ActionScript3 (and in fact to flash) I'm having a lot of trouble identifying what it is. Here's what I have right now:
var MCs = [];
var lastPos = "intializer";
if (MCs.length == 0) {
MCs = reset();
if (lastPos == MCs[0]) {
while (lastPos == MCs[0]) {
MCs = reset();
}
}
}
if (MCs.length > 0) {
lastPos = MCs[0];
MCs.splice(0,1);
gotoAndPlay(lastPos+"MC");
}
function reset(){
var PrepMCs = new Array("Image1", "Image2", "Image3");
var WorkMCs = new Array(PrepMCs.length);
var randomPos:Number = 0;
for (var i:int = 0; i < WorkMCs.length; i++)
{
randomPos = int(Math.random() * PrepMCs.length);
WorkMCs[i] = PrepMCs.splice(randomPos, 1)[0];
}
return WorkMCs;
}
Personally, I'd rather just do this with JavaScript, HTML, and images; it'd be really simple. But for hosting/CMS reasons I don't have any control over, I'm limited to a single file or a single block of code; I can't host anything externally, which as far as I can tell leaves Flash as my best option for this.
Any help would be greatly appreciated, thanks! If I've done something horribly, horribly wrong, and it's a wonder this even runs at all, don't hesitate to tell me!
edit: It just occurred to me, it is perfectly fine if the second run is in the same order as the first run, etc. The main thing is, it needs to be random. This is probably much easier to implement.
edit 2: MASSIVE DERP HERE. Every time it runs, it re-initializes MCs and lastPos... in other words, it's shuffling every time and starting over. What I should be researching is how to only run a line of code if a variable hasn't been initialized yet.
Blatantly stealing from #32bitKid, this is my version.
The main problem I have with his solution is the push/splice idea. As much as possible, I like to create once, and reuse. Shrinking and growing arrays is bulky, even if effective.
Also, this method does not re-order the array, which may or may not be valuable.
BTW, I like the way that he prevents a repeat of the previous item ("almost empty").
So here is another method:
package
{
public class RandomizedList
{
private var _items:Array;
private var idxs:Array;
private var rnd:int;
private var priorItemIdx:int;
private var curIdx:int;
public function RandomizedList(inarr:Array)
{
items = inarr;
}
private function initRandomize():void
{
idxs = new Array();
//Fisher-Yates initialization (http://en.wikipedia.org/wiki/Fisher%E2%80%93Yates_shuffle):
idxs[i] = 0;
for (var i:int = 1; i < items.length; i++)
{
rnd = int(Math.random() * (i + 1));
idxs[i] = idxs[rnd];
idxs[rnd] = rnd;
}
curIdx = 0;
priorItemIdx = -1;
}
private function randomize():void
{
var tempint:int;
//Fisher-Yates (http://en.wikipedia.org/wiki/Fisher%E2%80%93Yates_shuffle):
for (var i:int = items.length; i >= 1; i--)
{
rnd = int(Math.random() * (i + 1));
tempint = idxs[i];
idxs[i] = idxs[rnd];
idxs[rnd] = tempint;
}
curIdx = 0;
}
public function next():void
{
if (curIdx >= idxs.length)
{
randomize();
}
if (items.length > 1 && priorItemIdx == idxs[curIdx])
{
curIdx++;
}
priorItemIdx = idxs[curIdx++];
return items[priorItemIdx];
}
public function get items():Array
{
return _items;
}
public function set items(value:Array):void
{
_items = value;
initRandomize();
}
}
}
I would use a utility class like this to abstract out the behavior I wanted:
import flash.text.TextField;
class Randomizer {
private var unused:Array = [];
private var used:Array;
public function Randomizer(playList:Array) {
used = playList;
}
public function next():* {
// If almost empty, refill the unused array
if(unused.length <= 1) refill();
// Get the first item off the playList
var item:* = unused.shift();
// Shove it into the bucket
used.push(item);
// return it back
return item;
}
public function refill():void {
var i:int;
// Fisher-Yates shuffle to refill the unused array
while(used.length > 0) {
i = Math.floor(Math.random() * used.length)
unused.push(used.splice(i,1)[0])
}
}
}
Notice that it refills the unused array when the unused array still has one item in it, this makes it impossible for the last result to repeat twice in a row. This will return each item once before before looping, and will never repeat the same item twice.
You would use it by saying something like:
var ads:Randomizer = new Randomizer(["Image1", "Image2", "Image3"]);
ads.next(); // will return something
ads.next(); // will return something
ads.next(); // will return something
ads.next(); // will return something
// Keep going into infinity...
There is a little test example of this code working here.
See if this makes any sense
//create your array of all your ad names/frame labels
var PrepMCs:Array = new Array("Image1", "Image2", "Image3");
var shuffledMCs:Array = [];
//store the name of the last played ad in this var
var lastAdPlayed:String;
//shuffle the array
shuffleArray(PrepMCs);
function shuffleArray(arrayToShuffle:Array):void {
//clear the array
shuffledMCs = [];
var len:int = arrayToShuffle.length;
for(var i:int = 0; i<len; i++) {
shuffledMCs[i] = arrayToShuffle.splice(int(Math.random() * (len - i)), 1)[0];
}
//test to see if the new first ad is the same as the last played ad
if (lastAdPlayed == shuffledMCs[0]) {
//reshuffle
shuffleArray(PrepMCs);
} else {
lastAdPlayed = [0];
trace(shuffledMCs);
playAds();
}
}
//after each ad has played, call this function
function playAds():void {
if (shuffledMCs.length > 0) {
gotoAndPlay(shuffledMCs[0]);
shuffledMCs.splice(0,1);
} else {
//array is empty so we have played all the ads
shuffleArray(PrepMCs);
}
}

flash as3 how to prevent an item from being added to an array if it already exists in the array

I know how to remove duplicates from an array, but what I'm trying to do is prevent an item from ever being added to an array in the first place if it already exists. I'm pulling in data from an xml feed in a loop, and I thought that searching for that values index would work, but no matter what, the index is always -1. Here's my code:
var yearArr:Array = new Array();
for (var i=0;i<numCovers;i++){
var coverRef = xmlObj.cover[i];
var coverClip:MovieClip = new MovieClip();
coverClip.year = coverRef.#year;
if (yearArr.indexOf(coverClip.year === -1)){
yearArr.push (coverClip.year);
}
}
Maybe I'm misunderstanding the indexOf function, but I thought it was supposed to return -1 if a value did not exist in an array. What am I doing wrong?
Here's the solution I came up with:
var yearArr:Array = new Array();
for (var i=0;i<numCovers;i++){
var coverRef = xmlObj.cover[i];
var coverClip:MovieClip = new MovieClip();
coverYear = coverRef.#year;
addCoverYear(coverYear);
}
function addCoverYear(coverYear:int):void {
if (yearArr.indexOf(coverYear) == -1){
yearArr.push(coverYear);
}
}
you can reduce an array by passing everything to a dictionary, which will automatically remove redundancies. then pass the dictionary back as a new array.
//Reduce Array
private function reduceArray(array:Array):Array
{
var dictionary:Dictionary = new Dictionary();
for each (var element:String in array)
dictionary[element] = true;
var result:Array = new Array();
for (var key:String in dictionary)
result.push(key);
dictionary = null;
return result;
}
Your code is almost fine. The problem is that an E4X property .#year is not a literal string (I'm not sure right now, but I believe it's an XMLList object). That's why the indexOf call will keep returning -1, because it is looking for a duplicate of that object, not a string. E4X will convert it to a string as soon as you put it somewhere where only strings can go, but until that time it is something else.
If you rewrite your code like this, it should work right away:
var yearArr:Array = new Array();
for each (var coverRef : XML in xmlObj.cover){
var year : String = coverRef.#year; // force the property to be a string
if (yearArr.indexOf(year) < 0){
yearArr.push (year);
}
}
There were also a few other optimizations you could do to your code. The new MovieClip() part wasn't used, not all variables were strongly typed and by using a for each loop, you can state much clearer what objects you're looping through.
Here is what you could do, if for example, you have an array of strings.
var ItemList:Array = new Array();
for each(var Item:String in UseYourXMLFeed)
{
if(ItemList.indexOf(Item) == -1)
{
ItemList.push(Item);
}
}
Edit:
Anyways, your real answer is in the comment by Sam.

Reordering an array in flash as3

I have an array of objects, each of which is assigned an ID when it is first created. I give the user the ability to visually reorder the objects, which changes their position in the array. They then have the option to save that order using a flash sharedObject or "cookie" and then later, if they reopen the flash file, I want them to be able to hit a button to restore that order. I'm just not sure what the syntax would be to set the object's index within the array. Here's my code:
VARIABLES:
var project_settings = SharedObject.getLocal("settings"); //saves all project settings for the next time the file is opened
var project_order:Array = []; //saves project order for the next time the file is opened
var project_display:Array = []; //saves whether each project should be displayed or hidden for the next time the file is opened
SAVE CODE:
function saveOrder(){
for (var i=0;i<project_array.length;i++){
project_order[i] = project_array[i].id;
project_display[i] = project_array[i].projectThumb.thumbActive;
}
project_settings.data.order = project_order;
project_settings.data.active = project_display;
//trace (project_settings.data.active[1]);
project_settings.flush(); //saves most recent "cookie"
}
RESTORE CODE:
function loadOrder(){
for (var i=0;i<project_array.length;i++){
/* NEED THE CODE THAT GOES HERE. BASICALLY, PROJECT_ARRAY[i] SHOULD BE THE ITEM WITH AN ID EQUAL TO PROJECT_SETTINGS.DATA.ORDER[i] */
}
}
Something like this should work:
function loadOrder()
{
var dict = new Dictionary();
for (var i = 0; i < project_array.length; i++)
dict[project_array[i].id] = project_array[i];
project_array = [];
for (var i = 0; i < project_settings.data.order.length; i++)
project_array[i] = dict[project_settings.data.order[i]];
}
Just load in your array and sort on the ID. Something like this should work:
private function _loadArray():void
{
// fill in your array
project_array.sort( this._sortFunc );
}
// replace the * by whatever your object type is
private function _sortFunc( a:*, b:* ):int
{
return a.id - b.id;
}
More info: http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/Array.html#sort()
Or even the sortOn() function (which might be easier) should work:
http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/Array.html#sortOn()

Resources