Actionscript 3: Access movieclips from array with for-loop - actionscript-3

I am having some trouble with accessing the movieclips I've added as childs. From what I've read, a way to solve this is to add every new movieclip to an array, and then loop through this array when I want to change something about all the movieclips.
In my case I want to scale them.
This is how I've tried to implement this function:
package
{
import flash.display.MovieClip;
import flash.utils.Dictionary;
import flash.events.MouseEvent;
import Airport;
public class Main extends Sprite
{
public var tile:MovieClip
public var bg_image:Sprite;
public var airport:Airport;
public static var airportDict:Dictionary = new Dictionary();
public static var collectedAirportArray:Array = new Array();
public function Main()
{
addEventListener(Event.ADDED_TO_STAGE, init);
}
public function init(e:Event):void
{
removeEventListener(Event.ADDED_TO_STAGE, init);
// airportDict is being filled up here.. but that's not really relevant for my problem
bg_image = new Image();
addChild(bg_image);
bg_image.addEventListener(MouseEvent.CLICK, testfunction);
for (var k:Object in airportDict)
{
var airport:Airport = new Airport(k,airportDict[k]["x"], airportDict[k]["y"]);
collectedAirportArray.push(collectedAirportArray);
bg_image.addChild(airport);
}
}
private function testfunction(evt:MouseEvent):void
{
for each (tile in collectedAirportArray)
{
tile.scaleY = 2 * tile.scaleY;
}
}
}
}
This give me the error message TypeError: Error #1034: Type Coercion failed: cannot convert []#30977eb1 to flash.display.MovieClip.
at Main/testfunction() when clicking on the bg_image

You have an error in where you are making the array. You have there collectedAirportArray.push(collectedAirportArray); - you're basically pushing an array into itself. Nice snakey, they said. You should instead push the newly created airport in there:
collectedAirportArray.push(airport);
If your airport is any kind of DisplayObject your code should work.

What AirPort class is extending? If it extends MovieClip then try type casting like so:
private function testfunction(evt:MouseEvent):void
{
for each (tile in collectedAirportArray)
{
var myTile:MovieClip = MovieClip(tile);
myTile.scaleY = 2 * myTile.scaleY;
}
}

Related

Cast loaded SWF with designed interface in main class

First of all I read linked topic but unfortunately any of the tips didn't work for me: Casting a loaded SWF as an interface
My problem indeed is similiar, I have got created SWF (Content.swf) where Content.as which is document class there:
package {
import flash.display.MovieClip;
public class Content extends MovieClip implements IGame {
public function igameMethod():void {
trace("implemented method of IGame");
}
}
}
IGame interface (in the same directory as Content.as) contains sole method 'igameMethod'
package {
public interface IGame {
function igameMethod():void;
}
}
and such compiled SWF is loaded by Loader class in LoadSWF where Main.as is document class and it is something like that:
public class Main extends MovieClip {
private var swfLdr:Loader;
private var game:IGame;
public function Main():void {
if (stage) init(); else addEventListener(Event.ADDED_TO_STAGE, init);
}
private function init(e:Event = null):void {
removeEventListener(Event.ADDED_TO_STAGE, init);
swfLdr = new Loader();
var r:URLRequest = new URLRequest("../lib/Content.swf");
var defaultLoaderContext:LoaderContext = new LoaderContext(true, ApplicationDomain.currentDomain);
//none of settings for ApplicationDomain doesnt' work (with checkPolicy or with or without ApplicationDomain.currentDomain set)
swfLdr.load(r,defaultLoaderContext);
this.addChild(swfLdr);
swfLdr.contentLoaderInfo.addEventListener(Event.COMPLETE, onExternalSWFLoaded);
//tried Event.INIT too without success
}
private function onExternalSWFLoaded(e:Event) : void {
var Game:Class = ApplicationDomain.currentDomain.getDefinition( "IGame" ) as Class;
if (e.target.content is IGame) {
trace("Is IGame");
}else {
//output of this condition is false
//so e.target.content is not IGame
trace("No its not IGame");
}
var Game:Class = ApplicationDomain.currentDomain.getDefinition( "IGame" ) as Class;
//tried to cast as class without success too
var gameContent: IGame = IGame(swfLdr.content); //app breaks here
// TypeError: Error #1034: Type Coercion failed: cannot convert LoaderSWF#87f0089 to IGame
}
I've got also IGame interace (duplicated to LoadSWF project) with the same package structure (in the same folder as document class) as Main.as to let LoadSWF use igameMethod() on Content.swf, but i can not use it because of TypeError: Error #1034: Type Coercion failed: cannot convert LoaderSWF#87f0089 to IGame. So the compiler can't convert this Content to IGame.
Have you got any idea, which would help me with this topic?
Thanks for any tips in advance.
Kindest regards Pawel

Flash AS3 Loadmovie Error #1009 - External AS file

We have created a program in Flash that uses an external actionscript file.
I'm trying to load/import this swf in another flash file but I'm getting a 'Error #1009: Cannot access a property or method of a null object reference'
I know this is to a reference in my external .as file but I'm not sure how to fix this
This is my code to load (and scale) the swf
var ld:Loader = new Loader();
addChild(ld);
ld.load(new URLRequest("tatton.swf"));
ld.contentLoaderInfo.addEventListener(Event.COMPLETE,function(evt) {
var mc = evt.target.content;
mc.scaleX=0.7031;
mc.scaleY=0.7031;
});
Anyone have any ideas?
Thanks
Edit: here is the full error code: TypeError: Error #1009: Cannot access a property or method of a null object reference. at Tatton()
And below is my .as file code:
package
{
import flash.display.MovieClip;
import flash.events.Event;
import flash.events.MouseEvent;
import flash.utils.setTimeout;
import flash.utils.clearTimeout;
import flash.utils.getDefinitionByName;
public class Tatton extends MovieClip
{
public var active_clip:MovieClip;
public var to:Number;
public function Tatton()
{
// Create empty vector of movieclips;
stage.addEventListener(MouseEvent.CLICK, reset_timeout);
init();
}
public function reset_timeout(e:Event = null)
{
clearTimeout(to);
// 3 mins
to = setTimeout(fire_timeout, 150 * 1000);
}
public function fire_timeout():void
{
// Reset the system (and run attractor) if we're not on the attractor already.
if ( !(active_clip is Attractor))
{
init();
}
}
public function init()
{
// Reset globals
Globals.skip_menu_anim = false;
Globals.skip_staff_menu = false;
load_movie("Attractor");
reset_timeout();
}
public function load_movie(name:String):void
{
if (active_clip is MovieClip)
{
kill_active_movie();
}
active_clip = createInstance(name);
addChild(active_clip);
active_clip.startup();
}
public function kill_active_movie():void
{
active_clip.shutdown();
removeChild(active_clip);
active_clip = null;
}
public function createInstance(className:String):Object
{
var myClass:Class = getDefinitionByName(className) as Class;
var instance:Object = new myClass();
return instance;
}
}
}
try this
public function Tatton() {
addEventListener(Event.ADDED_TO_STAGE, stageAvailable);
}
private function stageAvailable(e:Event = null) {
removeEventListener(Event.ADDED_TO_STAGE, stageAvailable);
// Create empty vector of movieclips;
stage.addEventListener(MouseEvent.CLICK, reset_timeout);
init();
}
refer to this article to understand why
You need to add the event listener after you call Loader.load because contentLoaderInfo is null until load is called.

Adding eventlisteners in main document class for external classes

I have a small project I'm trying to help learn as3. It is a variation from the book Foundation Game Design with Actionscript 3.0. I am using an fla only to have a document class. All art is loaded within the as files. In the book, he just put all the code in the document class, I followed along and it worked as expected. I am trying to break out the code into separate classes to get a handle on OOP. One class makes a background - Background.as, one makes a character - Character.as, and one makes a button, which I instantiate 6 times for 6 different buttons - GameButton.as. And of course there is GameWorld.as which is the document class. Everything loads and shows up as expected. However when I try and add an eventListener for the buttons, I don't get any response. I have tried putting the eventListener in the GameButton.as and also tried it in the GameWorld.as neither of which has worked. Also I pass a reference to the stage when instantiating the various classes, because when I tried to addChild in the GameWorld.as, nothing would show up. I searched the site and found something similar, but it didn't seem to help. Thank you in advance for any advice you my have. Here is the code:
GameWorld.as
package
{
import flash.net.URLRequest;
import flash.display.Loader;
import flash.display.Sprite;
import flash.display.DisplayObject
import flash.events.MouseEvent;
import GameButton;
import Character;
import Background;
[SWR(width = "550", height = "400", backgroundColor = "#FFFFFF", frameRate = "60")]
public class GameWorld extends Sprite
{
//public variables
//Background
public var gameBackground:Background;
//Character
public var catCharacter:Character;
//Buttons
public var upButton:GameButton;
public var downButton:GameButton;
public var growButton:GameButton;
public var shrinkButton:GameButton;
public var vanishButton:GameButton;
public var spinButton:GameButton;
public function GameWorld ()
{
//Add the background to the stage
gameBackground = new Background("../images/background.png", stage);
//Add the character(s) to the stage
catCharacter = new Character("../images/character.png", stage);
//Set initial character position
catCharacter.CharacterPos(225, 150);
//Add the buttons to the stage
upButton = new GameButton("../images/up.png", stage, 25, 25);
downButton = new GameButton("../images/down.png", stage, 25, 85);
growButton = new GameButton("../images/grow.png", stage, 25, 145);
shrinkButton = new GameButton("../images/shrink.png", stage, 425, 25);
vanishButton = new GameButton("../images/vanish.png", stage, 425, 85);
spinButton = new GameButton("../images/spin.png", stage, 425, 145);
//Button event handlers
upButton.addEventListener(MouseEvent.CLICK, upButtonHandler);
}
public function upButtonHandler(event:MouseEvent)
{
trace("You clicked the up button!");
catCharacter.CharacterMove(15);
}
}
}
GameButton.as
package
{
import flash.net.URLRequest;
import flash.display.Loader;
import flash.display.Sprite;
import flash.display.Stage;
import flash.events.MouseEvent;
public class GameButton extends Sprite
{
//public variables
public var stageRef:Stage;
public var urlRequest:URLRequest;
public var gameButtonLoader:Loader;
public var gameButtonSprite:Sprite;
//Constructor
public function GameButton (urlRequest:String, stageRef:Stage, xPos:Number, yPos:Number)
{
this.stageRef = stageRef
this.urlRequest = new URLRequest();
gameButtonLoader = new Loader();
gameButtonSprite = new Sprite();
this.urlRequest.url = urlRequest;
gameButtonLoader.load(this.urlRequest);
gameButtonSprite.addChild(gameButtonLoader);
this.stageRef.addChild(gameButtonSprite);
gameButtonSprite.buttonMode = true;
gameButtonSprite.x = xPos;
gameButtonSprite.y = yPos;
}
}
}
Character.as
package
{
import flash.net.URLRequest;
import flash.display.Loader;
import flash.display.Sprite;
import flash.display.Stage;
public class Character
{
//private variables
private var stageRef:Stage;
private var urlRequest:URLRequest;
private var characterLoader:Loader;
private var characterSprite:Sprite;
//public variables
public var character_x_pos:Number;
public var character_y_pos:Number;
//Constructor
public function Character (urlRequest:String, stageRef:Stage)
{
this.stageRef = stageRef;
this.urlRequest = new URLRequest();
characterLoader = new Loader();
characterSprite = new Sprite();
this.urlRequest.url = urlRequest;
characterLoader.load (this.urlRequest);
characterSprite.addChild (characterLoader);
this.stageRef.addChild (characterSprite);
characterSprite.mouseEnabled = false;
}
//Set the position of the character
public function CharacterPos(xPos:Number, yPos:Number):void
{
characterSprite.x = xPos;
characterSprite.y = yPos;
}
//Move the position of the character
public function CharacterMove( yPos:Number):void
{
characterSprite.y -= yPos;
}
}
}
Background.as
package
{
import flash.net.URLRequest;
import flash.display.Loader;
import flash.display.Sprite;
import flash.display.Stage;
public class Background
{
//Private variables
private var stageRef:Stage;
private var urlRequest:URLRequest;
private var backgroundLoader:Loader;
private var backgroundSprite:Sprite;
//Constructor
public function Background (urlRequest:String, stageRef:Stage)
{
this.stageRef = stageRef;
this.urlRequest = new URLRequest();
backgroundLoader = new Loader();
backgroundSprite = new Sprite();
this.urlRequest.url = urlRequest;
backgroundLoader.load (this.urlRequest);
backgroundSprite.addChild (backgroundLoader);
this.stageRef.addChild (backgroundSprite);
backgroundSprite.mouseEnabled = false;
}
}
}
All art is loaded within the as files.
This is not an approach I recommend. There's a reason God gave us the Flash IDE--and it's not to write code! Any time you're spending on layout and viduals in code is just wasted, unless you have an actual requirement to change the visuals at runtime. The fact that your paths are all hard-coded suggests that you don't have that requirement.
So let's step back and imagine that you have a Symbol that contains 6 Symbols that you've created as just Flash buttons (when you select Button as the Symbol type). These will be SimpleButtons, but in the Class below we're just going to type them as DisplayObject. The Class doesn't care what they are, but using Simplebutton gives them up, over, down and hit states that require no code.
Note that the below assumes you have "automatically declare stage instances" off, which is IMO the best way to do things.
package view {
public class NavBar extends Sprite {
//because you put these on stage in the Symbol, they will be available in the constructor
public var upButton:DisplayObject;
public var downButton:DisplayObject;
public var growButton:DisplayObject;
public var shrinkButton:DisplayObject;
public var rotateButton:DisplayObject;
public var vanishButton:DisplayObject;
//makes it easier to do the same setup on all buttons
protected var allButtons:Vector.<DisplayObject> = <DisplayObject>([upButton, downButton, growButton, shrinkButton, rotateButton, vanishButton]);
public function NavBar() {
super();
for each (var btn:DisplayObject in allButtons) {
btn.buttonMode = true;
btn.mouseChildren = false;
btn.addEventListener(MouseEvent.CLICK, onButtonClick);
}
}
protected function onButtonClick(e:MouseEvent):void {
switch (e.target) {
case upButton:
dispatchEvent(new CharacterEvent(CharacterEvent.UP));
break;
case downButton:
dispatchEvent(new CharacterEvent(CharacterEvent.DOWN));
break;
case growButton:
dispatchEvent(new CharacterEvent(CharacterEvent.GROW));
break;
case shrinkButton:
dispatchEvent(new CharacterEvent(CharacterEvent.SHRINK));
break;
case rotateButton:
dispatchEvent(new CharacterEvent(CharacterEvent.ROTATE));
break;
case vanishButton:
dispatchEvent(new CharacterEvent(CharacterEvent.VANISH));
break;
default:
break;
}
}
}
}
Note that there's zero layout code. This code is dependent on a custom Event Class. I'm going to write that Event Class so that it always bubbles. That way, it can be dispatched anywhere on the display list and received at the top level:
package control {
class CharacterEvent extends Event {
public static var UP:String = 'characterUp';
public static var DOWN:String = 'characterDown';
public static var GROW:String = 'characterGrow';
public static var SHRINK:String = 'characterShrink';
public static var ROTATE:String = 'characterRotate';
public static var VANISH:String = 'characterVanish';
public function CharacterEvent(type:String) {
super(type, true, true);//always bubbles
}
public function clone():Event {
return new CharacterEvent(type);
}
}
}
Now, if you want to manually handle instantiation of the Symbol that has view.NavBar as its base class, it will look like this:
package {
public var navBar:NavBar;
class GameWorld {
public function GameWorld() {
try {
var navClass:Class = getDefinitionByName('NavBarSymbol') as Class;
} catch (e:Error) {
trace('You need to have a Library symbol called NavBarSymbol');
}
if (navClass) {
navBar = new navClass() as NavBar;
//unnecessary layout code here
//Note that this is NOT the responsibility of the child Class!
addChild(navBar);
}
//instantiate character
...
//by listening to the whole Document, you can add other things that
//dispatch Character events on the display list, like a keyboard listener
addEventListener(CharacterEvent.UP, moveCharacterUp);
//listeners for the rest of the character events...
}
public function moveCharacterUp(e:CharacterEvent):void {
//character move logic
}
//other handlers
}
}
Personally, I'd just add the navBar to the stage, and then there's no need to manage it at all (not even reference it with a variable), simply add the event listeners for the various character events.
The root of your problem doesn't seem to be the character code. However, I'm going to give you a few "best practice" pointers about it.
The convention in AS3 is for Class members (properties and methods) to be camel case starting with a lower case letter. So, characterPos() and characterMove().
Your Class already contains character in the name, so really these should just be pos() and move() (though there's no need now to shorten position()).
The only thing your child Classes are doing with their references to the parent are adding themselves. They don't need and shouldn't have a reference to the parent for this purpose. It is the parent's responsibility to add the Children (or the responsibility of the Flash Player if you use the stage).
That said, you could give your Character a reference to the parent Class typed as IEventDispatcher and allow the Character to listen to this channel. This concept is called an event bus.
Note that the reason that so many people do what you're doing is that Adobe failed to document how to properly use OOP with the timeline. Unfortunately, by the time a few of us started documenting that around late 2009/early 2010, the damage was done and everyone assumed that if you wanted to write good code you had to pretend the timeline and stage didn't exist.
I know I've covered a lot of ground, and probably most of what I said directly contradicts what you thought you knew, so please don't hesitate to ask any questions you might have.

Initialize Assets after Preload

Whenever I export the .swf file of my Flash game, I am receiving "TypeError: Error #1009: Cannot access a property or method of a null object reference.", along with a Runtime Shared Library Preloading Warning for my preloader. I have my timeline organized so that the first and third frames are both empty along with a stop(); command in the Actions layer. The second frame contains a single MovieClip that contains all of my exported assets, which are going to be initialized in the third frame of the timeline. None of my assets, except for the preloader, are exported in the first frame. What changes should I make to my Document Class for it to initialize the assets in the third frame?
Document Class:
package com.gameEngine.documentClass
{
import flash.events.*;
import flash.display.*;
import flash.geom.Point;
import com.gameEngine.assetHolders.*;
import com.gameEngine.assetHolders.Levels.*;
public class Document extends MovieClip
{
private static var _document:Document;
private var preloader:Preloader;
public var mcMain:Player;
public var restartButton:RestartButton;
public var spawnArea:SpawnArea;
public var level_1:Level_1;
public var level_2:Level_2;
public var level_3:Level_3;
public function Document()
{
addEventListener(Event.ADDED_TO_STAGE, init);
_document = this;
preloader = new Preloader(390, this.loaderInfo);
this.addChild(preloader);
preloader.addEventListener("loadComplete", loadAssets);
preloader.addEventListener("preloaderFinished", showLogo);
mcMain = new Player(this);
restartButton = new RestartButton(this);
spawnArea = new SpawnArea();
level_1 = new Level_1(this);
level_2 = new Level_2(this);
level_3 = new Level_3(this);
this.addChild(restartButton);
this.addChild(spawnArea);
this.preloader.x = 400;
this.preloader.y = 250;
restartButton.x = 822.95;
restartButton.y = 19;
spawnArea.x = 400;
spawnArea.y = 250;
trace ("Document Class Initialized");
// constructor code
}
public static function getInstance():Document
{
return _document;
}
private function loadAssets(event:Event):void
{
this.play();
}
private function showLogo(event:Event):void
{
this.removeChild(preloader);
}
public function init(event:Event)
{
if (stage.contains(spawnArea))
{
addChild(mcMain);
}
mcMain.x = spawnArea.x;
mcMain.y = spawnArea.y;
}
}
}
Preloader Class:
package com.gameEngine.assetHolders
{
import com.gameEngine.documentClass.*;
import flash.display.*;
import flash.events.*;
public class Preloader extends MovieClip
{
private var fullWidth:Number;
public var loaderInfo:LoaderInfo;
public function Preloader(fullWidth:Number = 0, loaderInfo:LoaderInfo = null)
{
this.fullWidth = fullWidth;
this.loaderInfo = loaderInfo;
addEventListener(Event.ENTER_FRAME, checkLoad);
}
private function checkLoad (event:Event):void
{
if (loaderInfo.bytesLoaded == loaderInfo.bytesTotal && loaderInfo.bytesTotal != 0)
{
dispatchEvent(new Event("loadComplete"));
phaseOut();
}
updateLoader(loaderInfo.bytesLoaded / loaderInfo.bytesTotal);
}
private function updateLoader(num:Number):void
{
progressBar.width = num * fullWidth;
}
private function phaseOut():void
{
removeEventListener(Event.ENTER_FRAME, checkLoad);
progressBar.gotoAndPlay(2);
if (progressBar.currentFrame == progressBar.totalFrames)
{
phaseComplete();
}
}
private function phaseComplete() : void
{
dispatchEvent(new Event("preloaderFinished"));
}
}
}
You have a lot of race conditions going on here. Many of these events could occur at relatively random times in relation to one another . . . you have to think asynchronously. That is, there can be no assumption that any object exists. E.g., in Document.init(), you check is if the spawnArea exists, but it is almost guaranteed not to at that point, and you never check for it again.
Without making any specific changes, I can recommend a generic solution. For any object (objB) you want loaded after another object (objA) is loaded, have objB created in the objA's ADDED_TO_STAGE handler. A simple example would be:
var objA:Whatever;
var objB:WhateverElse;
[...]
objA = new Whatever();
objA.addEventListener(Event.ADDED_TO_STAGE, objAAddedHnd);
[...]
public function objAAddedHnd(event:Event)
{
// remove the event, if no longer needed:
objA.removeEventListener(Event.ADDED_TO_STAGE, objAAddedHnd);
objB = new WhateverElse();
objB.addEventListener(Event.ADDED_TO_STAGE, objBAddedHnd);
}
[...]
public function objBAddedHnd(event:Event)
{
// remove the event, if no longer needed:
objB.removeEventListener(Event.ADDED_TO_STAGE, objBAddedHnd);
// and so on . . .
}
At this point, it shows that you would need to plan the timeline of object creation.

Error #1034, with a MouseEvent

I am making a basic point n' click game and I came upon this error:
TypeError: Error #1034: Type Coercion failed: cannot convert 3 to cem.mouvement.
Here's my script:
package cem {
import flash.events.Event;
import flash.display.MovieClip;
import cem.microjeux.events.InfoJeuEvent;
import cem.mouvement;
import flash.events.MouseEvent;
public class monterJeu extends MovieClip
{
private static var pType:String = "type";
private static var pNom:String = "testNom";
private static var pCourriel:String = "test#hotmail.com";
private static var pDifficulte:int = 0;
private static var pLangue:int = 0;
private static var pTitre:String = "Veuillez sortir";
private static var pVersion:String = "1.5";
private static var pCoordonnees:Number;
private var environnementJeu:environnement = new environnement();
private var personnageJeu:personnage = new personnage();
public function monterJeu():void
{
jouer(pNom,pDifficulte,pLangue);
dispatchEvent(new InfoJeuEvent(pType,pNom,pCourriel,pTitre,pVersion));
stage.addEventListener(MouseEvent.CLICK, test);
}
public function jouer(PNom:String,PDifficulte:int,PLangue:int):void
{
addChild(environnementJeu);
addChild(personnageJeu);
}
function test(e:MouseEvent){
pCoordonnees = stage.mouseX;
trace(pCoordonnees);
mouvement(3);
}
}
}
And on mouvement();
package cem
{
public class mouvement {
public function mouvement(blabla) {
trace(blabla);
}
}
}
I searched everywhere I could, and didn't find anything. I have no instances on the stage. Everything is imported on the first frame. I am kind of a beginner (let's say i'm no good at programming), so you can notify at the same time if you something that needs to be corrected. (BTW, the strange words are in french ;D)
Thanks!
The error is due to you trying to cast 3 to mouvement.
I think what you want is something like
function test(e:MouseEvent){
pCoordonnees = stage.mouseX;
trace(pCoordonnees);
var mouve:mouvement = new mouvement(3);
}
Notice that you have to have new in order to create a new instance of a class.
On another note, you should capitilize classes so they stand out better. So I would name the class Mouvement.
You are trying to cast 3 to the class mouvement into the test function:
function test(e:MouseEvent){
pCoordonnees = stage.mouseX;
trace(pCoordonnees);
new mouvement().mouvement(3); // <-- here your error
}
If you have only a function into your class you don't need to create a class but you can put on the function alone:
package cem
{
public function mouvement(blabla):void {
trace(blabla);
}
}
and now you can call the mpuvement function normally into you test function:
function test(e:MouseEvent){
pCoordonnees = stage.mouseX;
trace(pCoordonnees);
mouvement(3);
}