Parent is NULL...runtime error [AS3] Solved

19 posts

Flag Post

This Destroy() function is giving me a runtime error I don’t understand.

	public class MainMenuWaterEffect extends Sprite
	{
		private var theBMD:BitmapData;				//the BitmapData for Perlin
		private var clouds:BitmapData;				//the BitmapData of the clouds inverted vertically
		private var cloudsAsBitmap:Bitmap;			//same as clouds, but a Bitmap (not inverted vertically)
		private var theImageToReflect:Bitmap;		        //the final image for reflection
		private var onTop:BitmapData;				//after getting the correct clouds, put this on top
		private var copiedBMD:BitmapData;			//the area of the clouds for reflection
		private var zeroPoint:Point;
		private var i:int;
				 
		public function MainMenuWaterEffect(onTop:BitmapData, clouds:BitmapData, cloudsAsBitmap:Bitmap) 
		{
			var theDispl:DisplacementMapFilter;
			var rect:Rectangle = new Rectangle(0.0, 0.0, 700.0, 219.0);
						
			this.onTop = onTop;
			this.clouds = clouds;
			this.cloudsAsBitmap = cloudsAsBitmap;
			
			copiedBMD = new BitmapData(700.0, 219.0, true, 0x00000000);
			zeroPoint = new Point(0.0, 0.0);
												
			copiedBMD.copyPixels(clouds, rect, zeroPoint); 
			theImageToReflect = new Bitmap(copiedBMD);
			theImageToReflect.bitmapData.draw(onTop);
									
			theBMD = new BitmapData(700.0, 219.0, false, 0x000000);
			theDispl = new DisplacementMapFilter(theBMD, new Point(0.0, 0.0), 1, 2, 10, 35);
			theImageToReflect.filters = new Array(theDispl);
			
			i = 1;
			addChild(theImageToReflect);
		}
		
		
		private function MoveWater(e:Event):void
		{
			var filterList:Array;
			var rect:Rectangle = new Rectangle(cloudsAsBitmap.x * -1.0, 0.0, 700.0, 219.0);
			
			copiedBMD.copyPixels(clouds, rect, zeroPoint);
			theImageToReflect = new Bitmap(copiedBMD);
			theImageToReflect.bitmapData.draw(onTop);
			
			filterList = theImageToReflect.filters;
			theBMD.perlinNoise(55, 5, 3, 50, true, false, 7, true, new Array(new Point(i, i / 8)));
			filterList.mapBitmap = theBMD;
			theImageToReflect.filters = filterList;
			++i;
		}
		
		public function StartWater():void
		{
			addEventListener(Event.ENTER_FRAME, MoveWater);
			
trace(theImageToReflect, theImageToReflect.parent);			
			
		}
		
		public function StopWater():void
		{
			removeEventListener(Event.ENTER_FRAME, MoveWater);
		}
		
		public function Destroy():void
		{
			
trace(theImageToReflect, theImageToReflect.parent);			
			
			this.removeChild(theImageToReflect);
			removeEventListener(Event.ENTER_FRAME, MoveWater);
			if (this.parent) this.parent.removeChild(this);
		}
	}
}

When the cursor is removed from the game window, I stop the water effect…and restart when the cursor returns. The first time I start the effect, the parent is non-NULL, but when I restart the water effect the parent is NULL and is causing a runtime error when the Destroy() function gets called.

[Starting debug session with FDB]
[object Bitmap] [object MainMenuWaterEffect]
[object Bitmap] null
[object Bitmap] null
[object Bitmap] null
[object Bitmap] null
[Fault] exception, information=ArgumentError: Error #2025: The supplied DisplayObject must be a child of the caller.

Also, if anyone sees a place I could make this code a bit more lightweight I would appreciate the suggestion. It makes my Main Menu run a little bit laggy.

 
Flag Post

this.removeChild(theImageToReflect);

So perhaps you are removing it and not readding it?

 
Flag Post

I’m not sure what you mean by “reading it”…When Destroy is called, theImageToReflect is defined ([object Bitmap] in the final trace before the error is thrown). The effect continues to work correctly on the Main Menu up until Destroy gets called…so I don’t see any place where I might be removing it prematurely. Destroy is called from the Main Menu Class…but I don’t see how that could be the problem.

“This” is clearly the parent of theImageToReflect right after the constructor runs. But once MoveWater runs, the parent property is NULL. It must have something to do with theImageToReflect = new Bitmap(copiedBMD); in MoveWater()…but why would I loose the reference to the parent just because I instantiate a new Bitmap. It would seem like if the parent prop is NULL then the water effect would fail..but it just keeps on rolling on the Main Menu until a button is clicked or the cursor is removed from the stage.

E: Code from my Main Menu Class that is called when a button is clicked…

		private function Leaving(e:MouseEvent):void
		{
			if (SFX) SoundEffects.PlaySFX("Button Clicked");
			waterEffect.StopWater();
			destFrame = e.target.name;
			RemoveBtnListeners();
			fadeMask.alpha = 0.0;
			addChild(fadeMask);
			fadeMask.addEventListener(Event.ENTER_FRAME, MaskFadeIn);
		}
		
		private function MaskFadeIn(e:Event):void
		{
			if (fadeMask.alpha > 0.5) fadeMask.alpha += 0.1; else fadeMask.alpha += 0.15;
			if (fadeMask.alpha > 1.0)
			{
				fadeMask.alpha = 1.0;
				fadeMask.removeEventListener(Event.ENTER_FRAME, MaskFadeIn);
				waterEffect.Destroy();
				RemoveEverything();
				ChangeState(destFrame);
			}
		}

		private function RemoveEverything():void
		{
			stageRef.removeEventListener(Event.MOUSE_LEAVE, MouseHasLeftTheStage);
			stageRef.removeEventListener(Event.MOUSE_LEAVE, MouseExit);
			removeEventListener(Event.ENTER_FRAME, MoveClouds);
			
			if (infoSprite)
			{
				while (infoSprite.numChildren) infoSprite.removeChildAt(0);
				infoSprite.graphics.clear();
				infoSprite.removeEventListener(Event.ENTER_FRAME, FadeAndRemoveInfoSprite);
				infoSprite = null;
			}
			
			interactiveObjectArray = [];
			while (numChildren) removeChildAt(0);
			if (this.parent) this.parent.removeChild(this);
			
box.removeEventListener(MouseEvent.CLICK, ChangeGraphicsSetting);	//remove after FGL
box2.removeEventListener(MouseEvent.CLICK, ChangeRankSetting);		//remove after FGL

			
		}
 
Flag Post

He said “readding” as in adding again, not “reading”.

 
Flag Post

lol…thanks Senekis93. I never remove theImageToReflect until Destroy gets called.

 
Flag Post

I do not necessarily believe that statement. I claim that you have called Destroy() at least once before any null errors appear and then you call it again. Likely, you call it on an object multiple times by accident in some other code.

 
Flag Post

But I only instantiate MainMenuWaterEffect once. And that is the only way theImageToReflect is addChilded at all (at the end of the constructor). So if I remove it somewhere there is no way it can get back on the display list without reinstantiation.

Here is the effect if anyone is interested. Feel free to use the code. Just use it under whatever license UG uses. Ignore those additional shadows on the water on the left….it is something from my MainMenu that shadows the surface of the water.

 
Flag Post

Well, you instantiate an object – okay, you store it as your parent object’s field, like private var waterEffect:MainMenuWaterEffect; and then you don’t need to reinstantiate it, as removeChild() does not destroy the object, it only breaks the link to it in the DisplayList of the parent. And, you should not destroy it, only stop it from changing so that it does not get updated while off screen.

 
Flag Post

E: My statement is misleading…I do instantiate MainMenuWaterEffect every time the player returns to the Main Menu. But the error gets thrown the first time the MainMenu is displayed. At the start of the game…not upon subsequent returns to MainMenu. Right now I can’t leave the MainMenu without a runtime error.

For this game, I remove everything once the mask is fully opaque. Reinstantiating MainMenuWaterEffect every time the player returns to the Main Menu is no great burden since the player is just navigating within the game rather than actually playing the game. Good point though…why reinstantiate the MainMenu Class at all….just as long as that PerlinNoise is shut down.

 
Flag Post

A simple fix would be: if (contains(waterEffect)) removeChild(waterEffect);
Use this whenever you are unsure if the requested object is a child of current object. Practically, you can do this everywhere.

 
Flag Post

or if(waterEffect.parent) waterEffect.parent.removeChild(waterEffect);

 
Flag Post

I changed theImageToReflect = new Bitmap(copiedBMD); to theImageToReflect.bitmapData = copiedBMD; and that change solved the problem. The parent is no longer null. But I would like to understand why that was happening…it seems like instantiating a new Bitmap sets the parent property to a default of NULL rather than retaining the already established parent value.

@vesper…if you instantiate everything for the game and then only put on the display list what is currently relevant (Main Menu, Options, Credits, whatever) then what stops the GC from collecting those unused instances you have hanging around in the background? Because, like you say, using removeChild only destroys the link…but once links are destroyed the instance becomes available for GCing. And I think that is why I opted for reinstantiation in the design of this game rather than a singleton of each Class waiting in the background.

E: @UG I would usually use that check prior to removal, but in this case the Class added the Bitmap in the constructor. So it has the responsibility of cleaning it up and there was no reason to believe the Bitmap had lost it parent property value.

 
Flag Post

Well, that happens because it’s a completely different object.

It’s like doing:

var p:Point=new Point(3,0);
p=new Point();
trace(p.x); // 0

E:
Further explanation: The original bitmap is already added to the display list; changing its bitmapdata will update the information, but the bitmap is the same object it was before.

When doing new Bitmap, you’re creating a new instance with no parent.

 
Flag Post

And yet the Bitmap retains its’ display list link (it is still on the screen) even though it is a new object…but it doesn’t retain its’ parent property link. Good stuff to know! Thanks ya’ll…

 
Flag Post
Originally posted by Mond:

I changed theImageToReflect = new Bitmap(copiedBMD); to theImageToReflect.bitmapData = copiedBMD; and that change solved the problem. The parent is no longer null. But I would like to understand why that was happening…it seems like instantiating a new Bitmap sets the parent property to a default of NULL rather than retaining the already established parent value.

@vesper…if you instantiate everything for the game and then only put on the display list what is currently relevant (Main Menu, Options, Credits, whatever) then what stops the GC from collecting those unused instances you have hanging around in the background? Because, like you say, using removeChild only destroys the link…but once links are destroyed the instance becomes available for GCing. And I think that is why I opted for reinstantiation in the design of this game rather than a singleton of each Class waiting in the background.

E: @UG I would usually use that check prior to removal, but in this case the Class added the Bitmap in the constructor. So it has the responsibility of cleaning it up and there was no reason to believe the Bitmap had lost it parent property value.

You seem to misunderstand how does instantiation work. Given you have theImageToReflect already existing, when you do theImageToReflect = new Bitmap(copiedBMD); you actually re-instantiate your image holder completely, and any new instance has its “parent” property being null. Also this action makes you lose the direct link to your previous instance (note, you seemingly had added that previous instance to your main menu via addChild() somewhere before, so there are now TWO objects with the same bitmapData), but it remains accessible through displayList and likely you can find it iterating through getChildAt() of your image’s parent, and when you’re using removeChild(theImageToReflect), removing the newly instantiated Bitmap, the second does not get removed. You don’t see it apparently as you hide your MainMenu somehow, but it’s still there. Now, as you did theImageToReflect.bitmapData = copiedBMD;, you change the existing Bitmap’s bitmapData property, while not affecting its parent, so you reach both goals at once – you don’t spoil the display list of your parent, and you get rid of the error.

Now, what does stop GC from purging my objects. You see, I have a master class added to the scene (as do all the FlashDevelop’s projects without a preloader), which has a single instance of my Game class added as child. That instance has a lot of properties of various types, be it buttons, shapes, battlefield objects, text fields etc etc, of course some of those are not displayed. Also there are several static objects (namely background and some more) that are not stored as properties, but are added and not removed from display list. The GC sees my Game class instance and sees it having active links to some objects that are not on screen, and since these are active links (they are actually located in the allocated memory), GC does not try to purge those hidden objects. GC affects only those objects that have exactly zero links accessible from allocated memory. DisplayList counts as a set of active links, so my background is actually referenced, and is not affected by the GC as well.

 
Flag Post
Originally posted by Mond:

And yet the Bitmap retains its’ display list link (it is still on the screen) even though it is a new object…but it doesn’t retain its’ parent property link. Good stuff to know! Thanks ya’ll…

It retains everything. The one which is still on the screen is the old one, which you put there with an addChild somewhere in your code and never removed – which still has a parent and a position in the display list. The new one which you were instantiating has no parent and no position in the display list because it is never added to a parent. You seem confused about what new does: it creates a completely new object which is unrelated to whatever was assigned to that variable before.

 
Flag Post

E: @Bob The first time theImageToReflect is set up as a reference to a new Bitmap (in the constructor) it is placed on the display list (well, it is actually a child of a MainMenuWaterEffect instance, but that instance is placed on the display list over in the MainMenu Class). Later, (in MoveWater) theImageToReflect is set up as a reference to a new Bitmap…but, like you said, that new Bitmap is never placed on the display list. So then how does the water effect continue to work if the new Bitmap created in MoveWater never gets on the display list? The new Bitmap must get on the display list via the fact that theImageToReflect is on the display list, so is it unreasonable to expect that the parent property of the new Bitmap should be [object MainMenuWaterEffect]?

The GC will collect objects that still retain links through mark sweeping. As discussed in this article objects that retain no link to the Document Class instance that is placed on the stage when the SWF loads should be collected even though local links may exist. That is my understanding.

In my game design, lets say I’m on the MainMenu at the beginning of the game, my Game_Controller Class has instantiated a new instance of my MainMenu Class and has put it on the display list. When the player clicks a button to navigate elsewhere in the game, say to the Options Menu, I fade in a mask then call MainMenu::Destroy() to remove all listeners and all objects that were placed on the MainMenu Class instance and then the instance removes itself from the display list with a if (this.parent) this.parent.removeChild(this); and I believe I have released all the resources used to create the MainMenu Class instance and made them available for GCing. This design makes the Main Menu Class fully responsible for any instance it creates and any listeners it established.

For instance, myTextField = new TextField(); establishes a reference to a TextField Object through the variable name myTextField which is a private Class Variable of the MainMenu Class, and myTextField gets addChilded to the MainMenu Class instance in the constructor….but once the TextField Object is removed from the MainMenu Class instance I am expecting the GC to collect it through mark sweeping, even though I have set several properties of the TextField Object….so quite a few links still exist relevant to the TextField Object…and I still have a reference to the TextField Object through myTextField, those links exist only amongst each other in their own local space with no link to the display list or the MainMenu Class instance or the Document Class instance.

Then the Game_Controller Class instantiates an OptionsMenu Class instance and places it on the display list and fades out the mask. Now, let’s say the player returns to the Main Menu from the Options Menu….the Game_Controller Class will instantiate a MainMenu Class instance and myTextField = new TextField() will be executed again…but I am expecting the GC to have collected the previous TextField Object.

E:The MegaSWF link above that demonstrates the water effect was created while I was still using theImageToReflect = new Bitmap(copiedBMD);…I am creating new Bitmap Objects at 30FPS. I admit that was the wrong way to code the water effect, but shouldn’t that cause some kind of RAM stack overflow after 10 or 15 minutes of executing the SWF…or at least slow down the water effect over time? Especially if all those Bitmaps remain on the display list but have a z-ordering that prevents them from being seen.

 
Flag Post

The old bitmap keeps updating because you keep updating its bitmapdata.
Things don’t magically appear on the stage.

 
Flag Post

I was trying to find out if vesper was right when he suggested that using theImageToReflect = new Bitmap(copiedBMD); was leaving Bitmaps on the display list. And I found this recursive code for outputting children of DisplayObjectContainers…

public function ShowChildren(dispObj:*, indent:int):void
{
	var i:int;
	
	for (i = 0; i < dispObj.numChildren; i++)
	{
		var obj:* = dispObj.getChildAt(i);
		if (obj is DisplayObjectContainer)
		{
			trace(Indention(indent) + i + " " + obj.name + " " + obj + " " + obj.numChildren);
			ShowChildren(obj, indent + 1);
		}
		else trace(Indention(indent) + i + " " + obj);
	}
}
		
public function Indention(num:int):String
{
	var indent:String = '';
	
	for (var i:int = 0; i < num; i++) indent += "   ";
	return indent;
}

When I call ShowChildren(stageRef, 0) while my Main Menu is displayed I get this result…

0 root1 [object Preloader] 1
   0 instance120 [object Main] 0
1 instance126 [object CompleteGame] 2
   0 [object Shape]
   1 instance128 [object WG_MainMenu] 21
      0 instance130 [object MainMenuWaterEffect] 1
         0 [object Bitmap]
      1 [object Bitmap]
      2 [object Bitmap]
      3 instance133 [object MainMenuGunImage] 6
         0 [object Shape]
         1 [object Shape]
         2 instance136 [object TruckImage_5] 1
            0 [object Shape]
         3 instance138 [object TruckImage_5] 1
            0 [object Shape]
         4 instance140 [object DriveWheelImage_6] 1
            0 [object Shape]
         5 instance142 [object DriveWheelImage_6] 1
            0 [object Shape]
      4 instance144 [object MainMenuRailImage] 1
         0 [object Shape]
      5 instance146 [object CopyRightSymbol] 1
         0 [object Shape]
      6 [object TextField]
      7 [object Bitmap]
      8 [object TextField]
      9 [object TextField]
      10 [object TextField]
      11 [object TextField]
      12 [object TextField]
      13 [object TextField]
      14 [object TextField]
      15 [object Shape]
      16 [object TextField]
      17 [object Shape]
      18 instance164 [object Sprite] 3
         0 instance165 [object SponsorLogo] 4
            0 [object Shape]
            1 [object StaticText]
            2 [object StaticText]
            3 [object StaticText]
         1 instance170 [object SponsorLogo2] 4
            0 [object Shape]
            1 [object StaticText]
            2 [object StaticText]
            3 [object StaticText]
         2 instance175 [object SponsorLogo3] 4
            0 [object Shape]
            1 [object StaticText]
            2 [object StaticText]
            3 [object StaticText]
      19 instance186 [object MessageBox] 2
         0 [object TextField]
         1 [object TextField]
      20 [object TextField]

Which is an interesting utility to have (even though I think most debuggers will show this info also). Just thought I would share this code snippet in case anyone else needed it.