random movieClips when timer is complete - actionscript-3

I'm making a game in AS3, and I would like to add movieClips randomly on the screen when a a timer is complete.
Exemple :
something.addEventListener(TimerEvent.TIMER_COMPLETE, Finish);
function Finish(event : TimerEvent) : void {
randomly add movieClip1 or movieClip2 or movieClip3
}
How can I do that ?
Thank you very much.
EDIT
Thank your for your answer. I've tried a lot of things, but nothing really works.. I've tried :
_movieClips.push(new _classes[Math.floor(Math.random() * _classes.length)]()); // this line chooses a random index of your _classes Array which will return the Class at that index
stageRef.addChild(_movieClips[_movieClips.length-1]);
if (stageRef.getChildByName("_movieClips[0]") == null) {
trace("poubelle1");
_movieClips[0].addEventListener(MouseEvent.CLICK, removePoubelle, false, 0, true);
}else if (stageRef.getChildByName("_movieClips[1]") == null) {
trace("poubelle2");
_movieClips[1].addEventListener(MouseEvent.CLICK, removePoubelle2, false, 0, true);
}else if (stageRef.getChildByName("_movieClips[2]") == null) {
trace("poubelle3");
_movieClips[2].addEventListener(MouseEvent.CLICK, removePoubelle3, false, 0, true);
}
No errors, but I can only click if the movieClip just appears. If I'm waiting and a second one appears, I can't click on either one of them.
I've tried :
_movieClips.push(new _classes[Math.floor(Math.random() * _classes.length)]()); // this line chooses a random index of your _classes Array which will return the Class at that index
stageRef.addChild(_movieClips[_movieClips.length-1]);
if (_movieClips[0].visible== true){
trace("poubelle1");
_movieClips[0].addEventListener(MouseEvent.CLICK, removePoubelle, false, 0, true);
}
if (_movieClips[1].visible== true){
trace("poubelle2");
_movieClips[1].addEventListener(MouseEvent.CLICK, removePoubelle2, false, 0, true);
}
if (_movieClips[2].visible== true){
trace("poubelle3");
_movieClips[2].addEventListener(MouseEvent.CLICK, removePoubelle3, false, 0, true);
}
But Error #1010: A term is undefined and has no properties.
Do you know how I can do that ?
Thanks !

You COULD use an Array 'loaded' with Class names:
var _classes:Array = new Array(movieClip1, movieClip2, movieClip3);
var _movieClips:Array = new Array(); // this Array is used to instantiate your random MovieClips
Then, in your timer finished handler:
_movieClips.push(new _classes[Math.floor(Math.random() * _classes.length)]); // this line chooses a random index of your _classes Array which will return the Class at that index
addChild(_movieClips[_movieClips.length-1]);

Related

make sure the same event listener is not added more than once

I am implementing a singleton IEventDispatcher class but ran into a problem that sometimes I add the same event listener function to my instance multiple times. I tried keeping an array of the eventFunctions as follows to not add the event more than once but it does not seem to be working as expected.
private var eventFunctions:Array =[];
public function addEventListener(type:String, listener:Function, useCapture:Boolean = false, priority:int = 0, useWeakReference:Boolean = false):void {
if(dispatcher.hasEventListener(type) && eventFunctions.indexOf(listener) > -1){
trace("adding event listener that already exists " + type + " with function " + listener);
return;
}
dispatcher.addEventListener(type, listener, useCapture, priority, useWeakReference);
}
First as a note, there is no issue with adding a listener more than once - so long as the object/dispatcher, the callback function, and the useCapture parameter are identical, it will not actually add another listener.
For example:
bob.addEventListener(MouseEvent.CLICK, clickHandler);
bob.addEventListener(MouseEvent.CLICK, clickHandler, false);
function clickHandler(e:Event):void { trace("clicky"); };
Will only produce 1 trace per click.
Now, if you changed it to:
bob.addEventListener(MouseEvent.CLICK, clickHandler, false);
bob.addEventListener(MouseEvent.CLICK, clickHandler, true); //<-- use capture is true on this one
Now you will get two traces per click.
Now, if besides this first point, you still have a need to track all your listeners in a singleton, I'd suggest doing something like this:
//instead of an array, use a dictionary so you can have weak keys that don't prevent your objects from being garbage collected
private static var listeners:Dictionary = new Dictionary(true);
//you need to pass the dispatcher into the function (missing from your question code)
public function addEventListener(dispatcher:EventDispatcher, type:String, listener:Function, useCapture:Boolean = false, priority:int = 0, useWeakReference:Boolean = false):void {
//check is there a record in the dictionary for this object (array)
if(listeners[dispatcher]){
//loop through the array and see if there are any matches
for(var i:int=0;i<listeners[dispatcher].length;i++){
if(listeners[dispatcher][i] && listeners[dispatcher][i].listener == listener
&& listeners[dispatcher][i].type == type
&& listeners[dispatcher][i].useCapture == useCapture
&& listeners[dispatcher][i].useWeakReference == useWeakReference){
trace("adding event listener that already exists " + type + " with function " + listener);
return;
}
}
}
//add the listener to the dispatcher
dispatcher.addEventListener(type, listener, useCapture, priority, useWeakReference);
//if no record exists yet, create the array and associate it with this dispatcher
if(!listeners[dispatcher]) listeners[dispatcher] = new Array();
//add the parameters just used to the array.
listeners[dispatcher].push({listener: listener, useCapture: useCapture, useWeakReference: useWeakReference});
}
//you will need a way to remove the listeners as well
public function removeEventListener(dispatcher:EventDispatcher, type:String, listener:Function, useCapture:Boolean = false):void {
if(listeners[dispatcher] && listeners[dispatcher].length > 0){
//loop through the array and see if there any matches
for(var i:int=0;i<listeners[dispatcher].length;i++){
if(listeners[dispatcher][i] && listeners[dispatcher][i].listener == listener
&& listeners[dispatcher][i].type == type
&& listeners[dispatcher][i].useCapture == useCapture){
//remove the event form the dispatcher
dispatcher.removeEventListener(type, listener, useCapture);
//remove the item from the array
listeners[dispatcher].splice(i,1);
//if the array is now empty, remove the dictionary item
if(listeners[dispatcher].length < 1) delete listeners[dispatcher];
//exit the function now
return;
}
}
}
}

cannot convert flash.display::Stage#2a2cdf99 to flash.display.MovieClip?

So I'm creating a platform game in Actionscript 3.0, trying to call a function that spawns blocks based on an array. The code is in a 'Game' class and is directed towards a movieclip on my .fla
When it is ran I get the error:
"cannot convert flash.display::Stage#2a2cdf99 to flash.display.MovieClip."
Here's the code:
public function GameScreen(stageRef:Stage = null )
{
this.stageRef = stageRef;
btnReturn.addEventListener(MouseEvent.MOUSE_DOWN, returnMainMenu, false, 0, true);
mcMain.addEventListener(Event.ENTER_FRAME, moveChar);
this.addEventListener(Event.ENTER_FRAME, createLvl);
this.stageRef.addEventListener(KeyboardEvent.KEY_DOWN, checkKeysDown);
this.stageRef.addEventListener(KeyboardEvent.KEY_UP, checkKeysUp);
this.stageRef.addChild(blockHolder);
}
And
private function createLvl(event:Event):void
{
var lvlArray:Array = MovieClip(root)['lvlArray' +lvlCurrent];
var lvlColumns:int = Math.ceil(lvlArray.length/16);
for(var i:int = 0;i<lvlArray.length;i++){
if(lvlArray[i] == 1){
if(i/lvlColumns == int(i/lvlColumns)){
row ++;
}
var newBlock:Block = new Block();
newBlock.graphics.beginFill(0xFFFFFF);
newBlock.graphics.drawRect(0,0,50,50);
newBlock.x = (i-(row-1)*lvlColumns)*newBlock.width;
newBlock.y = (row - 1)*newBlock.height;
blockHolder.addChild(newBlock);
} else if (lvlArray[i] == 'MAIN'){
mcMain.x = (i-(row-1)*lvlColumns)*newBlock.width;
mcMain.y = (row-1)*newBlock.height;
}
}
row = 0;
}
Please help =(
Thanks!
First of all:
Turn on "permit debugging" in your publish settings. This will give you line numbers with your errors, so you can determine the exact location of your error.
Post the entire stack trace. That error by itself is not a lot to go on.
Given the error and the code you've posted, the error must be caused by your use of MovieClip(root). The root property does not always point to the main timeline in Flash, it will point to the Stage if the display object is added directly to the stage. For example:
trace(stage.addChild(new Sprite()).root) // [object Stage]
Documentation on root: http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/flash/display/DisplayObject.html#root

How to correctly load und unload swf galleries for PORTFOLIO web site?

My problem looks like: I have five buttons and four of them are loading four different swf galleries into the main swf, the last one unloads all galleries. Each of them has exactly the same code, the only differeces are the locations of jpg files and swf files. Also each o them has the listeners ADDTOSTAGE and REMOVEFROMSTAGE, because i'm using the stage inside the gallery/swfs.
So when I enter the menu and load the first swf i don't get any output errors, when I click the btn_back he unloads the swf properly with no errors. And so on but only when i load only one gallery.
But when I, after loading the first gallery, want to click on another button and load second gallery then i got this error in output, so many times as many pictures are loaded inside the loaded swf/gallery:
TypeError: Error #1009: Nie można uzyskać dostępu do właściwości lub metody dla odniesienia do obiektu null.
at DocObject/resizeMyDoc()
at DocObject/onResizeDoc()
at flash.events::EventDispatcher/dispatchEventFunction()
at flash.events::EventDispatcher/dispatchEvent()
at flash.display::Stage/dispatchEvent()
at Miniaturka/onAddedToStage()
at flash.display::DisplayObjectContainer/addChild()
at Miniaturki/callback()
at Function/http://adobe.com/AS3/2006/builtin::apply()
at Miniaturka/onLoadComplete()
So my conclusion is that the problem is not inside the loaded swf but in the code of loading and unloading code of the mine swf. The only solution that I can find is: that when I click the second button the code should first unload the current swf, and when the unload is complete then immidietly load the another one. But I don't know how to write it. Can anyone help? So here is the code.
import flash.events.MouseEvent;
import fl.transitions.Tween;
import fl.transitions.easing.*;
import fl.transitions.TweenEvent;
import flash.display.Loader;
var _mainLoader:Loader;
var _currentContent:DisplayObject;
var url:URLRequest;
var loaded:Boolean = false;
var a:Array = new Array(s1_1_arch, s1_2_arch, s1_3_arch, s1_4_arch);
var swfList:Array = ["A0101.swf", "A0102.swf", "A0103.swf"];
//Initiate Loader, do it only once
function initiateLoader():void {
_mainLoader = new Loader();
_mainLoader.contentLoaderInfo.addEventListener(Event.COMPLETE, onComplete);
}
function loadGallery(path:String):void {
//Check if content previously loaded, unload it
if (_currentContent != null) {
removeChild(_currentContent);
_currentContent = null;
_mainLoader.unloadAndStop();
}
//Now load another one
_mainLoader.load(new URLRequest[1]);
}
function onComplete(e:Event):void {
_currentContent = _mainLoader.content;
//Add root DisplayObject of the loaded content to the display list
stage.addChild(_currentContent);
}
function samClick(e:MouseEvent):void
{
switch (e.target)
{
case s1_1_arch :
break;
case s1_2_arch :
break;
case s1_3_arch :
break;
case s1_4_arch :
break;
}
}
function samOver(e:MouseEvent):void{
switch (e.target)
{
case s1_1_arch : var sam1Ou:Tween = new Tween(s1_1_arch, "alpha", Strong.easeOut, 1, 0.2, 0.5, true);
break;
case s1_2_arch : var sam2Ou:Tween = new Tween(s1_2_arch, "alpha", Strong.easeOut, 1, 0.2, 0.5, true);
break;
case s1_3_arch : var sam3Ou:Tween = new Tween(s1_3_arch, "alpha", Strong.easeOut, 1, 0.2, 0.5, true);
break;
case s1_4_arch : var sam4Ou:Tween = new Tween(s1_4_arch, "alpha", Strong.easeOut, 1, 0.2, 0.5, true);
break;
}
}
function samOut(e:MouseEvent):void{
switch (e.target)
{
case s1_1_arch : var sam1Ou:Tween = new Tween(s1_1_arch, "alpha", Strong.easeOut, 0.2, 1, 0.5, true);
break;
case s1_2_arch : var sam2Ou:Tween = new Tween(s1_2_arch, "alpha", Strong.easeOut, 0.2, 1, 0.5, true);
break;
case s1_3_arch : var sam3Ou:Tween = new Tween(s1_3_arch, "alpha", Strong.easeOut, 0.2, 1, 0.5, true);
break;
case s1_4_arch : var sam4Ou:Tween = new Tween(s1_4_arch, "alpha", Strong.easeOut, 0.2, 1, 0.5, true);
break;
}
}
for (var i:Number = 0; i < 4; i++)
{
a[i].addEventListener(MouseEvent.CLICK, samClick);
a[i].addEventListener(MouseEvent.MOUSE_OVER, samOver);
a[i].addEventListener(MouseEvent.MOUSE_OUT, samOut);
}
Just to add...
For something like this you are better of having all code in one place. A class file will save you other frustrations later on if you're still new. Just control all elements from one place. Also consider what Nicolas Siver said in his answer..
Just a quick guide on how to setup class file. If you save as Main.as and attach to your FLA then you can paste in your timeline code here.
package
{
//IMPORTS go here
//Declare your Class (name of .as file)
public class Main extends MovieClip
{
//VARS go here
//Declare main function of your Class (must have same name as Class (.as file)
public function Main()
{
// Your main program code go here
//Something(); //example: run a function called Something
}
public function Something();
{
// Do something code
}
} //End of Class
} //End of Package
You are on the right way. But you have many problems in your code, most of your switches are redundant. By default, all galleries, that you are loading, will be loaded in child ApplicationDomains, so they are suitable for garbage collection.
If you will use single Loader it will be easier for you to manage it, and load one section in time.
//Pseudo code
Create main loader (with onComplete event handler);
Create a function for loading content, and call it.
Check if previous load operation was successful (use your flag `loaded`), if Yes - unload previous content, remove previous content from the display list;
Load new content;
After loading is completed, add content to the main display list holder, designed to show gallery content.
Also I would recommend to use TweenLite, It's also more developer friendly.
Here for you several utility functions that will help you accomplish your task:
private var _mainLoader:Loader;
private var _currentContent:DisplayObject;
//Initiate Loader, do it only once
private function initiateLoader():void {
_mainLoader = new Loader();
_mainLoader.contentLoaderInfo.addEventListener(Event.COMPLETE, onComplete);
}
private function loadGallery(path:String):void {
//Check if content previously loaded, unload it
if (_currentContent != null) {
removeChild(_currentContent);
_currentContent = null;
_mainLoader.unloadAndStop();
}
//Now load another one
_mainLoader.load(new URLRequest(path));
}
private function onComplete(e:Event):void {
_currentContent = _mainLoader.content;
//Add root DisplayObject of the loaded content to the display list
addChild(_currentContent);
}
More concrete realisation, you could use it in your frame, but don't forget to do all imports, and create all necessary display object on the scene:
//Self explanatory
var currentContent:DisplayObject;
//Initiate Loader
var mainLoader:Loader = new Loader();
mainLoader.contentLoaderInfo.addEventListener(Event.COMPLETE, onComplete);
//Create some MovieClip and give it a name, you will place loaded content there, ex: contentHolder
//Add buttons in one container (MovieClip) and give it name, ex: buttonHolder
//Give buttons some names, ex: s1Arch, s2Arch, s3Arch
//Map our buttons to external links and prepare for mouse events
var links:Object = {"s1Arch": "A0101.swf", "s2Arch": "A0102.swf", "s3Arch": "A0103.swf"};
//Register listeners, you could do it in cycle
s1Arch.addEventListener(MouseEvent.CLICK, onClickButton);
s2Arch.addEventListener(MouseEvent.CLICK, onClickButton);
s3Arch.addEventListener(MouseEvent.CLICK, onClickButton);
//Mouse events, using events bubbling
buttonHolder.addEventListener(MouseEvent.CLICK, onClickButton);
function loadGallery(path:String):void {
//Check if content previously loaded, unload it
if (currentContent != null) {
contentHolder.removeChild(currentContent);
currentContent = null;
mainLoader.unloadAndStop();
}
//Now load another one
mainLoader.load(new URLRequest(path));
}
function onClickButton(e:MouseEvent):void {
//Explanation step-by-step
//Clicked button
var button: DisplayObject = DisplayObject(e.currentTarget);
//Path to the external file
var path: String = links[button.name];
//Utility function will handle another stuff
loadGallery(path);
}
function onComplete(e:Event):void {
currentContent = mainLoader.content;
//Add root DisplayObject of the loaded content to the display list
contentHolder.addChild(currentContent);
}

Error #1063 with removeEventListener

I'm making a game in AS3.
I've got in my main class this function :
public function tire(e:MouseEvent):void{
puzzle.removeListeners();
}
And in my Puzzle class that :
public function removeListeners():void{
var cocoUn;
var cocoDeux;
var cocoTrois;
var cocoQuatre;
var cocoCinq;
for (var i in Engine.usableItems){ // Ditto
if (Engine.usableItems[i].displayName == "COCOUN")
cocoUn = Engine.usableItems[i];
if (Engine.usableItems[i].displayName == "COCODEUX")
cocoDeux = Engine.usableItems[i];
if (Engine.usableItems[i].displayName == "COCOTROIS")
cocoTrois = Engine.usableItems[i];
if (Engine.usableItems[i].displayName == "COCOQUATRE")
cocoQuatre = Engine.usableItems[i];
if (Engine.usableItems[i].displayName == "COCOCINQ")
cocoCinq = Engine.usableItems[i];
}
cocoUn.removeEventListener(MouseEvent.CLICK, shoot, false, 0, true);
cocoDeux.removeEventListener(MouseEvent.CLICK, shootDeux, false, 0, true);
cocoTrois.removeEventListener(MouseEvent.CLICK, shootTrois, false, 0, true);
cocoQuatre.removeEventListener(MouseEvent.CLICK, shootQuatre, false, 0, true);
cocoCinq.removeEventListener(MouseEvent.CLICK, shootCinq, false, 0, true);
}
I want my 5 items to be unclickable when the function "tire" is called in my main class.
I've got an error in debug mode.
When I click on the stage, this error appear : Error #1063: Argument count mismatch on flash.events::EventDispatcher/removeEventListener(). Expected 2, got 5.
Do you know how I can correct that ?
Thank you very much,
Method removeEventListener has only three arguments:
function removeEventListener(type:String,listener:Function,useCapture:Boolean = false):void
It should be enough to ajdust your code in the following way:
cocoUn.removeEventListener(MouseEvent.CLICK, shoot);

In AS3 calling loadBytes 2 times in sequence wont work

While my code is pretty big and fragmented over many files to show here, i basically boils down to this:
I have a function which does this:
var loadedScript:Loader;
function load():void{
loadedScript = new Loader();
// loadedScript is initilized somewhere and other stuff
loadedScript.loadBytes(bytes, context);
loadedScript.contentLoaderInfo.addEventListener(Event.COMPLETE, scriptLoaded, false, 0, true);
}
when i call this function 2 times in a row in such a way that its called the 2. time before loadedScript.loadBytes(bytes, context); from the 1. time can finish, then the "scriptLoaded" method is called only for the 2. time, not the 1. time
So, is this intentional behaivor from the loadedScript.loadBytes(bytes, context); method, or a bug, cann i bypass this somehow?
you could ofcourse create a loader-queue, something like this:
var queue:Array = new Array();
function load(bytesToLoad:*):void {
queue.push(bytesToLoad);
processQueue();
}
function processQueue():void {
//check if items are on queue
if (queue.length > 0) {
//create a loader
var ldr:Loader = new Loader();
//add event for loading complete
ldr.contentLoaderInfo.addEventListener(Event.COMPLETE, scriptLoaded, false, 0, true);
//load the bytes (get first item from the queue, which contains the bytes to load)
ldr.loadBytes(queue.shift(), context);
}
}
function scriptLoaded():void {
//do stuff
//process next item in queue
processQueue();
}