Supplied DisplayObject must be a child of the caller - actionscript-3

I get this error once in a while for a specific object. For some reason, this issue seems to start when I spawn 2 of this object instead of one. I basically have enemies that drops coins and one enemy drops 2. When I pick them up at the same time I start getting this error.
public function removeCoin(){
removeEventListener(Event.ENTER_FRAME, moveCoin);
if(this.parent){
this.parent.removeChild(this);
}
parentMC.level.spawnedCoins.splice(this, 1);
}
This is the function called by the collision check when there is a collision between the player and the coin. The issue usually starts when I pick up two coins at once from this function.
var dropCoin:Number = Math.random() * 100;
if(dropCoin > 40){
var newCoin1:coin = new coin(parentMC);
var newCoin2:coin = new coin(parentMC);
newCoin1.x = x+7;
newCoin1.y = y;
parentMC.level.levelObjects.addChild(newCoin1);
parentMC.level.spawnedCoins.push(newCoin1);
newCoin2.x = x-7;
newCoin2.y = y;
parentMC.level.levelObjects.addChild(newCoin2);
parentMC.level.spawnedCoins.push(newCoin2);
}
Edited the code.

That error means that the item you're trying to remove from the display list (by calling removechild) either isn't on the display list, or isn't a child of the object your calling removeChild on.
Without analyzing all your code, a quick check can fix your problem likely.
Change you existing chunk of code:
if(this != null){
parentMC.lvl1.levelObjects.removeChild(this);
}
to this:
if(this.parent){
this.parent.removeChild(this);
}
This checks if 'this' has a parent, if so, it removes itself from it's parent.

I think your problem might be:
parentMC.level.spawnedCoins.splice(this, 1);
If spawnedCoins is just an array then splice should take 2 integer args startIndex and deleteCount relevant adobe help page
By passing an object what I think is happening is that it is casting the object to an int, value of '1' (i.e. not null).
What I think you want instead is parentMC.level.spawnedCoins.splice(parentMC.level.spawnedCoin.indexOf(this), 1);

Related

Actions with a list of instances - AS3

what is the best way to do an action with many instances at the same time?
Lets say I have 50 movieclip instances called A1 to A50, and I want to run an action with only A20 to A35.
For example:
(A20-A35).gotoAndStop(2)
You want an algorithm operation called loop. You are not able to abstractly address things in bunches at once, but you can loop and iterate the bunch one by one which produces basically the same result. Please read this: https://en.wikipedia.org/wiki/Control_flow#Loops When you need to do a quantity of similar operations it is always loop.
With regard to your problem:
// Loop iterator from 20 to 35 inclusive.
for (var i:int = 20; i <= 35; i++)
{
trace("");
// Compose the name of the MovieClip to retrieve.
var aName:String = "A" + i;
trace("Retrieving the MovieClip by name", aName);
// Retrieve the instance by its instance name.
var aChild:DisplayObject = getChildByName(aName);
// Sanity checks about what exactly did you find by that name.
if (aChild == null)
{
// Report the essence of the failure.
trace("Child", aName, "is not found.");
// Nothing to do here anymore, go for the next i.
continue;
}
else if (aChild is MovieClip)
{
// Everything is fine.
}
else
{
// Report the essence of the failure.
trace("Child", aName, "is not a MovieClip");
// Nothing to do here anymore, go for the next i.
continue;
}
// Type-casting: tell the compiler that the child is actually
// a MovieClip because DisplayObject doesn't have gotoAndStop(...)
// method so you will get a compile-time error even if you are
// sure the actual object is a valid MovieClip and definitely has
// the said method. Compile-time errors save us a lot of pain
// we would get from run-rime errors otherwise, so treasure it.
var aClip:MovieClip = aChild as MovieClip;
trace(aClip, "is a MovieClip and has", aClip.totalFrames, "frames.");
if (aClip.totalFrames < 2)
{
// Nothing to do here anymore, go for the next i.
continue;
}
// Now you can work with it.
aClip.gotoAndStop(2);
}
Now that you understand the while idea step by step, if you are sure all of them are present and all of them are MovieClips you can go for a shorter version:
for (var i:int = 20; i <= 35; i++)
{
(getChildByName("A" + i) as MovieClip).gotoAndStop(2);
}
UPD: You can as well address children with square bracket access operator.
for (var i:int = 20; i <= 35; i++)
{
// You can skip type-casting as this["A" + i] returns an untyped reference.
this["A" + i].gotoAndStop(2);
}
Yet there are differences and complications. Method getChildByName(...) always returns a DisplayObject with the given name (or null if none found). Square brackets operator returns an untyped OOP field of the current object.
It will not work with dynamically added children (unless you pass their references to the respective fields).
It will not work if "Automatically Declare Stage Instances" publish option is off.
Finally, this["A" + 1] and A1 are not exactly the same because the latter could refer to a local method variable rather than object member.
I'm not saying that square brackets are evil, they're as fine, yet, as always, programming is not a magick thus understanding what you are doing is the key.

AS3 Isometric game Sorting Object Index

I am creating an isometric game, and I would like to know how to correctly position the (Z-index) of objects when the player goes behind them and infront of them.
I have been using
if(y>stage.stageHeight/2){
parent.setChildIndex(this,parent.getChildIndex(this)+1);
gotoAndStop(2);
} else if (y<stage.stageHeight/2){
parent.setChildIndex(this,parent.getChildIndex(this)+1);
gotoAndStop(1);
}
So far yet I have been receiving this error
RangeError: Error #2006: The supplied index is out of bounds.
My logic for this is; "If the object is UNDER the player then move its index up so that it is over the player, but if the object is ABOVE the player, then decrease it's index so that is under the player."
Any ideas on how I could improve this code so that it works without giving me errors?
Firstly if you're going to be swapping many objects it's best to work from outside the object. For example a Level class that contains an array of all your swappable objects. It will also be responsible for looping through each object and swapping it according to its y value.
public var objectArray:Array = [];
for (var ob1:Object in objectArray){
for (var ob2:Object in objectArray){
if(ob1 == ob2) continue;
swap(ob1, ob2);
}
}
//...
public function swap(a:Object, b:Object):void {
if (a.y > b.y != a.parent.getChildIndex(a) > b.parent.getChildIndex(b)) {
a.parent.swapChildren(a, b);
}
}
Keep in mind this is not the most efficient way to do it, because you have to check each object against each other object O(n^2).
you have to keep in mind that the index you provide is the index of the object in the container's children array. it cannot be less than zero and it cannot be more or equal to the actual childCount of the container.
so parent.getChildIndex(this)+1 is very unsafe
var nextIndex:int = parent.getChildIndex(this)+1;
if(nextIndex < parent.numChildren && nextIndex >= 0)
parent.setChildIndex(this, nextIndex);

AS3 Psuedo 3D Z-Indexing

I am attempting to make a really basic Pseudo 3D game in AS3. When I press certain keys my character moves up and down but what I want to happen is when the characters y position is above an objects y position then the character should appear behind the object.
Here is my code for an objects class at the moment:
package {
import flash.display.MovieClip;
import flash.utils.getTimer
import flash.events.Event;
public class bushMC extends MovieClip {
private var lastFrame:int = new int(0);
private var dt:Number = new Number();
private var main:Main;
public function bushMC(){
main = this.parent as Main;
stage.addEventListener(Event.ENTER_FRAME, update);
trace(main.getChildIndex(this));
}
private function update(e:Event):void{
dt = (getTimer() - lastFrame)/30;
lastFrame = getTimer();
if(main.char.y + 200 < this.y + 55 && main.getChildIndex(main.char) > main.getChildIndex(this)){
main.setChildIndex(this, main.getChildIndex(main.char)+1);
}
else if(main.getChildIndex(main.char) < main.getChildIndex(this)){
main.setChildIndex(this, main.getChildIndex(main.char));
}
}
}
}
I have tried editing loads of the values(+1, -1, equal to) for each calculation but I can't seem to find the right ones. One I tried almost works but instead when the char is supposed to be behind the object it simply flickers in-front and then behind continuously.
Thanks in advance, Kyle.
I just tried a little quick mock script based off your code. I got it working how I assume you are attempting to get it to work:
import flash.events.Event;
import flash.display.MovieClip;
var char:MovieClip = new MovieClip();
var bush:MovieClip = new MovieClip();
char.graphics.beginFill(0xFF0000);
char.graphics.drawCircle(0, 0, 30);
bush.graphics.beginFill(0x00FF00);
bush.graphics.drawEllipse(0, 0, 40, 80);
this.addChild(char);
this.addChild(bush);
bush.x = 100+(Math.random()*350);
bush.y = 100+(Math.random()*200);
this.addEventListener(Event.ENTER_FRAME, updateYPos);
function updateYPos(e:Event):void {
char.x = mouseX;
char.y = mouseY;
if(char.y < bush.y + 30 && this.getChildIndex(char) >= this.getChildIndex(bush)){
this.setChildIndex(bush, this.getChildIndex(char));
}
else if(char.y > bush.y + 30 && this.getChildIndex(char) < this.getChildIndex(bush)){
this.setChildIndex(bush, this.getChildIndex(char));
}
}
I hope this sample is enough to help you. All it needed was an extra condition on the else if and it works. :)
You should have a sorted list of bushes somewhere, which is then added via addChild() in the right order - uppermost bush has lowermost Z-position (child index 0 or the least of bushes, there could be other objects). Then, as your player moves, you track its position relative to list of bushes, so you don't run the full list check for z-ordering of player, but only check "nearest" bushes for possible change, then you set child index of player to found value. Note that if you're setting child index of player to bush's index, if you are moving player forwards (greater indexes), set to -1, as the bush will actually be one position lower because of player occupying a position in the display list, and if you are setting child index to lower values, set to equal. There is a more elegant version of this, using the fact that your bushes are continuous within display list, with only interruption being player, although it will run out of steam once more moving objects will appear.
And yes, you run update on the player or any other moving objects, not on the bush.
function updateZPos(e:Event):void {
// process coordinates change
var p:DisplayObjectContainer=this.parent;
// a bit faster to use function local var to get parent
var ci:int=p.getChildIndex(this); // changeable, get current index
var prev:DisplayObject=null;
if(ci>0) prev=p.getChildAt(ci-1);
var next:DisplayObject=null;
if(ci<p.numChildren-1) next=p.getChildAt(ci+1);
while(prev) {
if (this.y<prev.y) {
ci--;
p.setChildIndex(this,ci);
if (ci>0) prev=p.getChildAt(ci-1); else prev=null;
} else break;
while(next) {
if (this.y>next.y) {
ci++;
p.setChildIndex(this,ci);
if(ci<p.numChildren-1) next=p.getChildAt(ci+1); else next=null;
} else break;
}
}
This function was written with implication of display list of p being pre-sorted, and will maintain sorted state of it after moving of this, and is suitable for any moving object, not just the player. For this to work without messing up your other objects, separate everything moving into one container which will then get referenced as base for sorting display list. Otherwise your player might eventually get above all as the last element to check will be say score textfield with Y of 0. Also you will need to maintain coherent position of register point all over your set of moving objects' classes, so that say the base of a bush will be at Y=0 instead of being at Y=30, as implied in your code. The legs of a player should then also be at Y=0.

ArgumentError: Error #2025: The supplied DisplayObject must be a child of the caller.

Please, I need help trying to remove bullets and enemies from my stage. I am very new to programming. Thank you.
I receive the following error message:
ArgumentError: Error #2025: The supplied DisplayObject must be a child
of the caller. at flash.display::DisplayObjectContainer/removeChild()
at Main/fl_EnterFrameHandler() at
flash.utils::Timer/_timerDispatch() at flash.utils::Timer/tick()
The code where debug sent me.
package
{
import flash.display.MovieClip;
import flash.events.Event;
import flash.utils.Timer;
public class Main extends MovieClip
{
var enemyShipTimer:Timer;
var coinTimer:Timer;
var playerscore:Number = 0;
var enemies:Array;
var bullets:Array;
public function Main()
{
enemyShipTimer = new Timer(1000);
enemyShipTimer.addEventListener("timer", fl_EnterFrameHandler);
enemyShipTimer.start();
coinTimer = new Timer(1000);
coinTimer.start();
enemies = new Array ();
bullets = new Array ();
}
function fl_EnterFrameHandler(event:Event):void
{
var enemyinstance = new enemy_ks();
stage.addChild(enemyinstance);
enemies.push(enemyinstance);
trace(enemies.length);
for (var count=0; count<enemies.length; count++)
{
for (var bcount=0; bcount<bullets.length; bcount++)
{
if (enemies[count].hitTestObject(bullets[bcount]))
{
removeChild(enemies[count]);
enemies.splice(count, 1);
removeChild(bullets[bcount]);
bullets.splice(bcount, 1);
}
}
score_ks.text = " " + playerscore;
}
}
}
}
EDIT: Reread your code and noticed the real error is actually that you are adding to the stage but removing from your Main sprite. You need to match those up. You cannot remove an object from a parent if it is not actually a child of that object.
The points below still need to be addressed as well, otherwise you may end up with other errors.
Your issue is with your loop. In your loop, you adjust the array length on each successful hit test but you never adjust the count of the loop.
So think of it like this.
You start with:
count = 0;
length = 10;
Now say you run a loop for count < length and you splice at count == 4 and count == 7. In your current scheme, you will only hit the following objects (using the original index)
0 1 2 3 4 6 7 9
Notice that you don't hit index 5 or 8. When you modify the array like that and don't modify the count, you end up skipping over certain items. After splicing index 4, the original index 5 moves to 4 and everything else moves back one as well. So when you move to index 5, you are actually reading the original index 6.
Very simple fix for this is to just adjust your counts as you splice.
if (enemies[count].hitTestObject(bullets[bcount]))
{
removeChild(enemies[count]);
enemies.splice(count, 1);
count--; //subtract 1 from the count
removeChild(bullets[bcount]);
bullets.splice(bcount, 1);
bcount--; //subtract 1 from the bcount
}
That will ensure your count actually hits every object. You could also use a for each loop to handle this, though that type of loop is slower than a standard for loop depending on application.
Additionally, a DisplayObject can only be removed from a DisplayObjectContainer if it is actually a child of that container. If it is not, it will error out. So I believe you may also be running into an issue where your array does not fully line up with what is on the stage and you are trying to remove an object that doesn't actually exist as a child of the parent
As a minor aside, you should avoid adding children directly to the stage unless you have a real reason to do so. Instead, add it directly to the application object's display list using the this keyword, or simply addChild().

Splicing elements out of a Vector in a loop which goes through the same Vector (ActionScript 3)

I'm making a simple game and have a Vector full of enemies in order to do hit-checking on them from my "laser" object (it's a space shmup). Every laser loops through the Vector and checks if it's occluding the hit circle. The problem lies in when one laser destroys an enemy, the rest of the lasers try to also check the same Vector, only to go out of bounds since the enemy's already been spliced out and it's changed the size of the Vector.
for each (var enemy:Enemy in enemies){
var distanceX = this.x - enemy.x;
var distanceY = this.y - enemy.y;
var distance = Math.sqrt( (distanceX*distanceX) + (distanceY*distanceY) );
if (distance <= enemy.hitRadius) {
enemy.removeEnemy();
enemies.splice(enemies.indexOf(enemy),enemies.indexOf(enemy));
}
}
How would I go about collecting the index of individual elements in the Vector to be deleted, then only deleting them when every Laser object is finished its checking?
edit: here's my removeEnemy() function from my Enemy class, just in case:
public function removeEnemy(){
removeEventListener(Event.ENTER_FRAME, move);
parent.removeChild(this);
trace("removed Enemy", enemies.indexOf(this));
}
edit edit: I'm also getting a null reference pointer error to "parent" in my removeEnemy() function... but only sometimes. I have a feeling that if I fix one of these two problems, the other will also be fixed but I'm not sure.
I fixed it! The problem was actually in how I used the "splice()" method. Turns out that the second parameter isn't the end index of where to stop splicing, it's the number of elements to be spliced. So when I was trying to splice element 0, I wasn't splicing anything, and when I was trying to splice element 3, I was also splicing 4 and 5. I feel like such a dunce for not reading the API right and wasting a couple hours on this. Thanks to everyone who commented-- you guys helped me rule out what I thought the problem was.