AS3 Error 1009 - Where am i going wrong? - actionscript-3

This is the error I'm getting:
TypeError: Error #1009: Cannot access a property or method of a null object reference. at ICA_v7_fla::MainTimeline/addEgg()
at Function/http://adobe.com/AS3/2006/builtin::apply()
at SetIntervalTimer/onTimer()
at flash.utils::Timer/_timerDispatch()
at flash.utils::Timer/tick()
Cannot see where I am going wrong - could someone point out where I'm trying to refer to a null object reference please :)
The rest of my AS reads:
import flash.events.Event;
import flash.utils.Timer;
import flash.events.TimerEvent;
//Set as a variable so that it can be changed at a later
//date so that the user level can change (if necessary).
var eggMoveSpeed=6;
var Score=0;
var ScoreValue=10;
var Level=1;
var NextLevel=100;
var EggCount=0;
var EggMax=15;
btn.addEventListener(MouseEvent.CLICK, scorer);
function scorer(e:Event):void{
//Add to the score.
Score=Score+10;
//Display score in text box.
scoreText.text=Score;
}
var eggAdd = setInterval(addEgg,2000);
function addEgg(){
//Add to the egg count (to ensure maximum is not exceeded).
EggCount=EggCount+1;
if (EggCount<=EggMax){
//Create an object of the egg_mc from the egg class.
var egg:egg_mc = new egg_mc();
//Set the Max and Min WIDTH positions.
var maxWidth = 452;
var minWidth = 98;
//Randomize the position of the egg on the screen - with thanks to http://www.kirupa.com/developer/actionscript/tricks/random.htm
var positionX = Math.floor(Math.random()*(1+maxWidth-minWidth))+minWidth;
//Position the egg on the stage, and add it to the screen
egg.y=400;
egg.x=positionX;
//Add the egg to the stage.
stage.addChild(egg);
//Add a moving loop to the egg.
egg.addEventListener(Event.ENTER_FRAME, loop);
}else{
clearInterval(eggAdd);
}
function loop(e:Event):void{
//Move the egg up the screen.
egg.y-=eggMoveSpeed;
//Check to see if egg has got to the top of the screen - if so, then move the object to the bottom.
if (egg.y<-100){
egg.y=400;
}
}
//Add an event listener to the egg, to see if it has been clicked.
egg.addEventListener(MouseEvent.CLICK, clickedEgg);
function clickedEgg(e:Event):void{
//http://asgamer.com/2009/flash-game-design-basics-adding-library-objects-to-stage-with-as3
//Create an object of the brokenEgg_mc from the broken egg class.
var brokenEgg:brokenEgg_mc = new brokenEgg_mc();
//Position the brokenEgg image wherever the egg Image was.
brokenEgg.y=egg.y;
brokenEgg.x=egg.x;
//Add brokenEgg to stage, and remove the egg image.
stage.addChild(brokenEgg);
stage.removeChild(egg);
//Add to the score.
Score=Score+ScoreValue;
//Display score in text box.
scoreText.text=Score;
//Set LevelCheck Variable to 0 - to recalculate correctly.
var LevelCheck = 0;
//Check the level that the user is currently on by dividing the score by the next level required score.
LevelCheck=Score/NextLevel;
//Setup a variable to use for the actual level to display.
var ActualLevel=0;
//Check to see if the LevelCheck variable has come back less than 1 (i.e. 0.1 or a score of 10).
if (LevelCheck < 1){
//If yes, then set the level to 1, as user is still on level 1.
ActualLevel=0;
} else {
//If not, then round down to the lowest level (1.9 is still level 1!).
ActualLevel=Math.floor(LevelCheck);
}
//Display the Lowest Level to the user.
levelText.text=ActualLevel;
}}
Obviously, the timer event loops until there are 15 eggs on the stage, where the interval should then stop running - but this produces the error.

Your egg variable is function-wide, namely it's defined in addEgg() function, but you refer to egg inside clickedEgg and loop, which don't have actual access to egg's value. Also, if there is more than a single egg, how do you plan on tracking which egg was clicked, etc? At the very least, use e.target in clickedEgg function to find out what egg was clicked, and don't forget clearing the event listener.
function addEgg():void {
if (EggCounter>=EggMax) return; // why adding eggs if there are too many?
var egg:egg_mc=new egg_mc();
// rest of code intact
}
function loop(e:Event):void {
var egg:egg_mc=(e.target as egg_mc);
if (!egg) return; // loop is for eggs only
// rest of function intact
}
function clickedEgg(e:MouseEvent):void {
var egg:egg_mc=(e.target as egg_mc);
if (!egg) return;
// rest of function intact, as we now have a valid egg link
egg.removeEventListener(Event.ENTER_FRAME,loop);
egg.removeEventListener(MouseEvent.CLICK,clickedEgg);
// clear listeners. These are persistent until removed manually!
}

Related

How to "restart" my as3 code

I've got some AS3 codes on my 4th frame.
var words:Array = new Array; //a mini database to hold the words
var current_word:String; //used to hold the current word being tested
var ques_num:int; //used to track the question number
var tileArray:Array = new Array; //array of tile objects
var targetArray:Array = new Array; //array of target areas
var scramble_Array:Array = new Array; //array used to scramble the word
getword();
function getword() {
words=["screen"];
current_word=words[ques_num];
setTiles(current_word.length);
ques_num++;
}//getword
function setTiles(a) {tileArray=[ ];
for(i=0;i<a;i++){
var tempclip:Tile =new Tile;addChild(tempclip);
tempclip.x=150+(i*120);tempclip.y=500;tempclip.tag=i;
tempclip.original_posx=tempclip.x;
tempclip.original_posy=tempclip.y;
tileArray[i]=tempclip;
var tempclip2:Placeholder =new Placeholder;addChild(tempclip2);
tempclip2.x=150+(i*120);tempclip2.y=620;
targetArray[i]=tempclip2;
}//for i
scramble_word(a);
}//setTiles
I wanted to put a button where the user can click in order to go back to frame 1.
goBack1.addEventListener(MouseEvent.CLICK, gotoOne, false, 0, true);
function gotoOne(event:MouseEvent):void{
gotoAndStop(1);
}
When I do so, we can still see the child of the 4th frame..
So I removed them.
trace("clear_board");
removeChild(checker);
pauser=false;
for(i=0;i<tileArray.length;i++){
removeChild(tileArray[i]);
removeChild(targetArray[i]);
}
}
But now, when the user is going back to the 4th frame, an error is displayed :
TypeError: Error #1009: Cannot access a property or method of a null object reference.
at RECUPERE_jeumuseev1_fla::Jules_108/getword()[RECUPERE_jeumuseev1_fla.Jules_108::frame4:95]
The line 95 is this :
setTiles(current_word.length);
I suppose that the error come from the fact that I've "removed" tileArray[i] but I'm not sure..
What do you suggest I could do to simply "restart" the all code from this frame, like the first time the user is coming to the 4th code ?

AS3 shuffling movieclips

I've added the basic targets and applying drag and drop for my puzzle pieces, now Im having trouble making the shuffling aspect. As in, after the player completes or opens up the fla, each time will start the puzzle pieces in random places of the stage. I understand using arrays for shuffling somehow but Im not sure exactly how to achieve this. I've stored the instance of my 19 puzzle pieces inside the array but now I have no idea what to do with this array. Other tutorials were abit out of my league and leaves my head scratching.
Just started doing coding for flash professional so yeah, any help with the shuffling movie clips ie the puzzles pieces would be greatly appreciated.
Heres's my code, Im not posting the whole thing since from P1 to P19 is basically copy pasting:
import flash.events.Event;
stage.addEventListener(Event.ENTER_FRAME, EntFrame)
function EntFrame(e: Event) : void
{
P1.addEventListener(MouseEvent.MOUSE_DOWN, fl_ClickToDrag);
function fl_ClickToDrag(event:MouseEvent):void
{
P1.startDrag();
}
stage.addEventListener(MouseEvent.MOUSE_UP, fl_ReleaseToDrop);
function fl_ReleaseToDrop(event:MouseEvent):void
{
P1.stopDrag();
}
if (T1.hitTestObject(P1.Tar1))
{
P1.x = 313.15;
P1.y = 242.75;
}
P19.addEventListener(MouseEvent.MOUSE_DOWN, fl_ClickToDrag_19);
function fl_ClickToDrag_19(event:MouseEvent):void
{
P19.startDrag();
}
stage.addEventListener(MouseEvent.MOUSE_UP, fl_ReleaseToDrop_19);
function fl_ReleaseToDrop_19(event:MouseEvent):void
{
P19.stopDrag();
}
if (T19.hitTestObject(P19.Tar19))
{
P19.x = 624.35;
P19.y = 455.60;
}
}
Here is what I hope is more holistic answer.
First, ditch those inline functions. Right now you make an ENTER_FRAME listener and inside that function you have inline function defined. This means every frame tick (which is tied to your frame rate, not the main timeline), those functions are going to get created again, and since you are adding them as handlers for listeners, they will stay in memory forever.
Here is a way you code this, showing ways to reduce redundancy and get rid of those memory leaks. This assumes the following:
You have 19 objects on the stage called T1 - T19, that represent the possible locations the pieces can go.
You have 19 pieces on the stage called P1 - P19, that, and the numbers correlate to the T locations as per the correct location of the piece.
//let's create a function to randomize the piece location
function seedPieces() {
//create an array consisting of the integers 1 - 19
var unusedSpaces:Vector.<int> = new Vector.<int>;
var i:int;
for (i = 1; i <= 19; i++) {
//populate that array
unusedSpaces.push(i);
}
var curLocation:DisplayObject; //helper var for the loop below
var curPiece:Sprite; //helper var for the loop below
//loop 19 times (from 1 - 19) - one iteration for each piece
for (i = 1; i <= 19; i++) {
curPiece = this["P" + i] as Sprite; //you can get the piece this way, or use an array if you've made one, like `pieces[i];`
trace(curPiece.name);
//splice removes and returns the item at the specified index (in this case a random number between 0 and arrays length less 1) - the second parameter is amount of items to remove (just 1 for this case)
curLocation = this["T" + unusedSpaces.splice(int(Math.random() * unusedSpaces.length), 1)] as DisplayObject;
trace(" ",curLocation.name);
//move the piece to the random location:
curPiece.x = curLocation.x;
curPiece.y = curLocation.y;
}
}
//NOW, as an aside, you should use a loop to add all your listeners for the sake of sanity - if you have them in an array, loop through that, or use the sloppy way like this:
for (var i:int = 1; i <= 19; i++) {
Sprite(this["P" + i]).addEventListener(MouseEvent.MOUSE_DOWN, fl_ClickToDrag);
}
//create a var to hold any piece that is currently being dragged, so you know which piece to stop drag on later
var currentDraggingItem:Sprite;
seedPieces();
function fl_ClickToDrag(event:MouseEvent):void
{
//assign this clicked item to the currentDraggingItem var
currentDraggingItem = event.currentTarget as Sprite;
//bring this one to the front
currentDraggingItem.parent.addChild(currentDraggingItem);
//you can use this one click handler for all pieces
//the piece that was actually clicked, is referenced by event.currentTarget
currentDraggingItem.startDrag();
//add the mouse up listener now that the mouse is currently DOWN
stage.addEventListener(MouseEvent.MOUSE_UP, fl_ReleaseToDrop);
//listen every frame while dragging
stage.addEventListener(Event.ENTER_FRAME, EntFrame);
}
function fl_ReleaseToDrop(event:MouseEvent):void
{
//if currentDraggingItem has a value, stop drag it
if (currentDraggingItem) {
currentDraggingItem.stopDrag();
//send to the back
currentDraggingItem.parent.addChildAt(currentDraggingItem,0);
}
//remove the mouse up and enter frame listener now that the mouse is UP
stage.removeEventListener(MouseEvent.MOUSE_UP, fl_ReleaseToDrop);
stage.removeEventListener(Event.ENTER_FRAME, EntFrame);
if(checkComplete()){
//game over, do something
}
}
function EntFrame(e: Event) : void
{
//this will snap the peice to the correct spot when the mouse is touching the correct spot
if(currentDraggingItem){
if (this[currentDraggingItem.name.replace("P","T")].hitTestPoint(mouseX,mouseY))
{
currentDraggingItem.x = this[currentDraggingItem.name.replace("P","T")].x;
currentDraggingItem.y = this[currentDraggingItem.name.replace("P","T")].y;
}
}
}
function checkComplete():Boolean {
//use a loop to go through all your pieces and check if they are in the right spot. Again, you could have them in an array, or do it the lazy way
for (var i:int = 1; i <= 19; i++) {
if (!this["T"+i].hitTestObject(this["P"+i]))
{
return false;
}
}
return true;
}
Well, in general you can shuffle with the following code:
var shuffledVector:Vector.<someClass> = new Vector.<someClass>;
while (originalVector.length > 0) {
shuffledVector.push(originalVector.splice(Math.random() * originalVector.length, 1)[0]);
}
Longer, explained version:
var shuffledVector:Vector.<someClass> = new Vector.<someClass>; //We will store our shuffled vector in here
var randomIndex:int; //Random index from the originalVector
var resultVector:Vector.<someClass>; //result from the originalVector.splice(...) function
var randomElement:someClass; //Random element from the originalVector
while (originalVector.length > 0) { //We will reduce the size of the originalVector until the originalVector is empty.
randomIndex = Math.random() * originalVector.length; //Calculate a random index within the range of the originalVector from 0 to originalVector.lenght-1 (note that the range decreases by one on every loop)
randomVector = originalVector.splice(randomIndex, 1); //Use splice to remove one element at the randomly choosen index, we will receive a vector with the removed element...
randomElement = randomVector[0]; //...so we need to access the element
shuffledVector.push(randomElement); //Add the randomly choosen element to our shuffled vector
}
I've written the code for a vector as i suggest to use a vector instead of an array, but the principle behind it is the same for an array.
In your case the originalVector is a vector filled with your P1-P19 Movieclips and someClass would be MovieClip. The originalVector is empty at the end and could be replaced with the shuffled one and of course it would make a lot more sense if you put the code in a seperate function like this:
function Shuffle(originalVector:Vector.<someClass>) : void {
var shuffledVector:Vector.<someClass> = new Vector.<someClass>;
while (originalVector.length > 0) {
shuffledVector.push(originalVector.splice(Math.random() * originalVector.length, 1)[0]);
}
originalVector = shuffledVector;
}
Offtopic, but important for further coding: Someone else already mentioned, that it is not good to add EventListeners on every frame, because it is absolutely unnecessary. You only need to add the Listeners once. Your code is very repetitive, you should use a function which accepts a MovieClip, x and y then call that function 19 times.
e.g.:
function setUpMovieClip(MC:MovieClip, x:int, y:int) : {
MC.addEventListener(MouseEvent.MOUSE_DOWN, clickToDrag);
//more code...
}
within the clickToDrag function you can access the MovieClip which was clicked via the event.target property:
function clickToDrag(e:MouseEvent) : {
e.target.startDrag();
//more code...
}
I hope you get the idea.

How to get the width of a MovieClip for a different frame instantly?

Is there a way to get the width of a MovieClip (that does have a name) on a different frame? I have tried to using .width and .getBounds(null).width, however, both of them will give me only the width of the current frame. I have tried to do gotoAndStop(frameiwant), but the information doesn't seem to be correct until at least the next frame
I would like to get the width of the frame instantly so I don't have to wait until the next frame for the width.
The only way I could think of doing this was to have an initial phase in your project which will:
Run through all of the frames in your timeline. Create an object which will hold information about the children in that frame. It can be called Frame.
Iterate over all the children that are added to the stage in that frame and add a definition object that describes that child. The description can be as basic or vast as you need. We can call this class an ObjectDefintion.
The downside of this process is that you need to wait for the FRAME_CONSTRUCTED event like #Larusso pointed out in his answer. This means that the frame actually has to finish rendering before you are able to get information about its children, which of course means you have to go through and render every single frame in your timeline during this phase. All you can really do to mitigate this problem is set the frameRate to something high and then set it back when you're done assessing all the frames.
I have set this up and it works well - I'll paste each class and try explain what they do.
So for your document class (or whichever MovieClip holds the frames you want to look at), I have this:
public class Main extends MovieClip
{
private var _userFrameRate:int;
private var _frames:Vector.<Frame> = new <Frame>[];
public function Main()
{
_userFrameRate = stage.frameRate;
stage.frameRate = 120;
addEventListener(Event.FRAME_CONSTRUCTED, _assess);
}
public function getFrame(index:int):Frame
{
return _frames[index - 1];
}
private function _assess(e:Event):void
{
var frame:Frame = new Frame(this);
_frames.push(frame);
if(currentFrame === totalFrames)
{
removeEventListener(Event.FRAME_CONSTRUCTED, _assess);
gotoAndStop(1);
stage.frameRate = _userFrameRate;
ready();
}
else play();
}
public function ready():void
{
// Start here.
// There is a MovieClip on frame 10 with the instance name 'test'.
// We can get the width of it like this.
trace( getFrame(10).define("test").property("width") );
}
}
This basically initializes the phase in which we will run over each frame in the MovieClip and assess its children. The ready() method is used as the entry point for your code post-assessment.
Next we have the Frame class, which serves to hold information about children related to a frame:
public class Frame
{
private var _main:Main;
private var _content:Object = {};
public function Frame(main:Main)
{
_main = main;
update();
}
public function update():void
{
_content = {};
for(var i:int = 0; i < _main.numChildren; i++)
{
var target:DisplayObject = _main.getChildAt(i);
// This will be explained below.
var definition:ObjectDefinition = new ObjectDefinition(target, "x", "y", "width", "height");
_content[target.name] = definition;
}
}
public function define(name:String):ObjectDefinition
{
return _content[name];
}
}
It's pretty straightforward - you give it a reference to Main so that it can check children that are existent within it each frame.
The ObjectDefinition class is also pretty straightforward, acting purely as a repository for data that you want to keep track of on each child of the frame:
public class ObjectDefinition
{
private var _definition:Object = {};
public function ObjectDefinition(target:DisplayObject, ...properties)
{
for each(var i:String in properties)
{
_definition[i] = target[i];
}
}
public function property(property:String):*
{
return _definition[property];
}
}
You'll notice that the constructor accepts the target DisplayObject that will be defined, as well as any amount of properties you want to keep track of as strings (see above within Frame for implementation).
Once complete, you can chain the methods Main.getFrame(), Frame.define() and ObjectDefinition.property() to get properties of children that will exist throughout the timeline. For example, if you have a MovieClip with the instance name square on frame 15 and you want to get its width and height, you can do this within .ready() like so:
var square:ObjectDefinition = getFrame(15).define("square");
trace(square.property("width"), square.property("height"));
Of course this process is not ideal - but unfortunately it is the only way I can see that what you want to achieve is possible.
You have to listen to a specific event before you can ask for the information.
clip.addEventListener(Event.FRAME_CONSTRUCTED, frameReadyHandler);
clip.gotoAndStop(frame);
function frameReadyHandler(event:Event):void
{
clip.removeEventListener(Event.FRAME_CONSTRUCTED, frameReadyHandler);
var width = clip.width;
}
The Frame constructed event is the first of several events that gets dispatched. It gets dispatches right before the frame script gets executed. You could also wait for the on enter frame event.
You could add an event listener for 1 millisecond and test if the previousWidth you had stored is different. If it is, there you go. If not, its probably listening to the same frame.
A 1 millisecond timer is not such a big deal, stop it if you don't need it, resume it if you do, else, keep it running constantly. When it changes, dispatch an event or whatever needs to happen.
If you know the maximum size of the MovieClip, you may try this:
// Create movie clip
var movie :MovieClip = new MovieClipWith3Frames();
// Move to second frame
movie.gotoAndStop(2);
// Create bitmap witch magenta background
var bd :BitmapData = new BitmapData(200, 200, false, 0xFF00FF);
// Draw second frame
bd.draw(movie);
// Found the bounds of shape
var movieBounds:Rectangle = bd.getColorBoundsRect(0xFFFFFF, 0xFF00FF, false);
trace(movieBounds); // (x=42, y=15, w=32, h=33)

Randomize Saved movieclips stored on children

I'm having a hard time with some code in actionscript 3.0. I don'y know how to randomize the movieclips stored on child and pick only 8 movieclips wherein there are 10 movieclips stored. I hope you'll be able to help me with this problem. thanks
Here is the code:
//start stage function
this.mainmc.addEventListener (Event.ENTER_FRAME, setupStage1);
this.waitingCounter=0;
//set up current stage
function setupStage1 (e:Event) {
//wait for timeline
if (this.waitingCounter<2) {
this.waitingCounter++;
//not ready yet, do nothing
return;
}
//Start the timer
timer.start();
//hide hint
this.mainmc.hintmc.visible=false;
//hide star animation
this.mainmc.starAnimation.visible=false;
//listener for hint button
this.mainmc.hintbut.addEventListener (MouseEvent.CLICK, showHint1);
//create objects array
this.obArr=[];
//count the objects on stage
for (var n=0; n<this.mainmc.numChildren; n++) {
//get the children
var ob=this.mainmc.getChildAt(n);
//only take movie clips
if (ob is MovieClip) {
//only count the movie clips that have name declared
if (ob.myname!=null) {
//push to array
this.obArr.push (MovieClip(ob));
}
}
}
on the code above, the code will store all the movieclips that are present in the stage. it stores them in a child. each 10 movieclips has a variable name "myname".
If you simply want to randomly sort items within an array, use the array.sort method and within your sort function, simply create a random number between 1 and 2. If it's 1, return true, if it's 2, return false. Here is an actionscript 2 snippet along with a link to a couple of tutorials:
var a:Array = new Array(“a”, “b”, “c”, “d”, “e”);
function shuffle(a,b):Number {
var num : Number = Math.round(Math.random()*2)-1;
return num;
}
var b:Array = a.sort(shuffle);
trace(b);
http://mrsteel.wordpress.com/2007/05/26/random-array-in-as2-as3-example-using-sort/
http://sierakowski.eu/list-of-tips/75-random-sort-function-comes-handy-when-building-applications-with-playlists.html
This is a much longer and more in-depth tutorial:
http://active.tutsplus.com/tutorials/actionscript/quick-tip-how-to-randomly-shuffle-an-array-in-as3/

Change positions of objects on the stage by click - ActionScript 3

I have somme mc's on the stage an I want thatto change on mc with antoher by clicking on them. For example if I click on mc1 and than on mc2 than they schould change the positions.
any ideea how o do that?
thank you for your time
You need to have a click event for the movieclip, and record it on a variable which movieclip was clicked, and then when the 2nd one is clicked, you just swap their positions. I'll give you a snippet of code that should work and should be enough to teach you how it's done.
import flash.events.MouseEvent;
// Variable that will be used to store the 1st clicked MC
var lastClickedSwapMC;
//First we define the function to be called
function clickEventSwapMcs(evt : MouseEvent) {
// Verify if a mc wasn't previously clicked
if(lastClickedSwapMC == null) {
// If it wasn't, it's the 1st time, so store the MC that was clicked
lastClickedSwapMC = evt.currentTarget;
} else {
// If it was, we just need to swap the positions of the stored one with the one just clicked
var savedX : Number = evt.currentTarget.x;
var savedY : Number = evt.currentTarget.y;
evt.currentTarget.x = lastClickedSwapMC.x;
evt.currentTarget.y = lastClickedSwapMC.y;
lastClickedSwapMC.x = savedX;
lastClickedSwapMC.y = savedY;
//After swaping their position, we clear the last clicked MC
lastClickedSwapMC = null;
}
}
//Now we register the click event on them so it calls the function
mc1.addEventListener(MouseEvent.CLICK, clickEventSwapMcs);
mc2.addEventListener(MouseEvent.CLICK, clickEventSwapMcs);