removed object not disappearing and 2025 error, as3 - actionscript-3

I'm trying to make a turn based strategy game on a grid map. To move your characcters around, I'm using click detection to see which direction the player intends. The code I'm using removes the old object, I presume, the adds the new one in one space to the right. The problem is that when I remove my object the image of the object remains where it began, while the actual objects reappears in the correct position. Then, when I try again, flash throws a 2025 error. Here is the code.
if(s == 0){
sarray[s] = [main, maincharacter, mc, 3]
enemyiden = "gwr"
}
else if(s == 1){
sarray[s] = [mcaxe, mainaxe, mcaxeman, 3]
enemyiden = "gar"
}
else if(s == 2){
sarray[s] = [mchorse, maincav, mccavalry, 5]
enemyiden = "gcr"
}
for(var g:int = 0; g < tilepositions.xcoords.length; g++){
if(tilepositions.xcoords[g] == Allies[s][0] - 12.5){
break
}
}
for(var h:int = 0; h < tilepositions.ycoords.length; h++){
if(tilepositions.ycoords[h] == Allies[s][1] - 12.5){
break
}
}
if(mouseX < Allies[s][0] && Math.abs(mouseX - Allies[s][0]) < 37.5 && Math.abs(mouseY - Allies[s][1]) < 12.5 ){
trace("test1")
this.removeChild(sarray[s][0])
sarray[s][0] = new Sprite
this.addChild(sarray[s][0])
trace(this.contains(sarray[s][0]))
sarray[s][1] = new sarray[s][2]
sarray[s][0].addChild(sarray[s][1])
sarray[s][0].x = tilepositions.xcoords[g-1]
sarray[s][0].y = tilepositions.ycoords[h]
Allies[s][0] = sarray[s][0].x + 12.5
movecurr[s] += 1
break
}
"s" is a for loop variable that counts upward. Any help would be much appreciated.

The problem is within this line: this.removeChild(sarray[s][0]). And the error says it all: ArgumentError: Error #2025: The supplied DisplayObject must be a child of the caller, which means that sarray[s][0] is not child of this. There's no magical way to tell you why, as we don't know, but if you fix this the problem will be resolved :)

Related

AS3 Null Object Reference After Null Check

I have been looking for a solution to this for a while now but cannot find anything.
This problem is very odd so I'll give some background. I am making a tower defense game, and while implementing status effects on enemies (slowed, on fire, etc.) I ran into a weird problem. When the tower kills the enemy with a projectile everything is fine, but for some reason when the enemy is killed by a status effect, I get a null object reference error (both are handled with the method "Enemy.damage(damage:int)"). The weird thing about this is that the status effects are stored in an array that is only referenced in 3 spots. In the ENTER_FRAME event (one ENTER_FRAME event is used to process every aspect in the Game), in the constructor (status = new Array();) and in the destroy method.
One weird part is that if the "status" variable was null, other variables from the destroy method would have given me errors a long time ago. The really weird part is that I have a null check right before I get the error.
Here is where I get the error:
//STATUS EFFECTS
if (status == null) {
trace("test");
}
if (status != null && status.length != 0) {
for (var i:int = 0; i < status.length; i++) {//LINE 101
status[i].applyEffect();
}
}
Here is the error:
TypeError: Error #1009: Cannot access a property or method of a null object reference.
at game.enemies::Enemy/update()[C:\flash\game\enemies\Enemy.as:101]
"test" never gets printed, and even with the null check I still get the error
EDIT: Here is the Enemy class: http://pastebin.com/LAyMMB1P
The StatusEffect & FireEffect classes: http://pastebin.com/GTGDmjt8 (I can only post 2 links, each is in its own file)
status variable is referenced in StatusEffect.destroy() and here:
override public function onHit(e:Enemy) {
if (upgradeLevel >= 1) {
var onFire = false;
for (var i:int = 0; i < e.status.length; i++) {
if (e.status[i].name_ == "fire") {
onFire = true;
}
}
if (!onFire) {
e.status.push(new FireEffect(e));
}
}
if (upgradeLevel >= 2) {
if (enemiesHit.indexOf(e) == -1) {
enemiesHit.push(e);
e.damage(1);
}
if (upgradeLevel == 2 && enemiesHit.length == 2) {
destroy();
}
} else {
e.damage(super.damage);
destroy();
return;
}
}
This is happening because you can potentially destroy your enemy in the middle of the for loop on line 101.
Destroying the enemy sets the status array to null, so on the next iteration when it checks if i < status.length, status is null, thus the error.
So to illustrate, stepping through the code, let's assume at line 101 there are two items in the status array:
First loop, item at index 0 gets applyEffect() called on it.
inside applyEffect() the specificApplication() method can be called.
inside specificApplication() the super.enemy.damage(1); method is called.
inside the enemy.damage() method, you can potentially call the enemy.destroy method
inside the enemy.destroy method, you set status = null.
NOW the loop from line 101 loops, but status is now null.
Fix this by breaking out of your loop if the enemy is destroyed.
for (var i:int = 0; i < status.length; i++) {//LINE 101
status[i].applyEffect();
if(!status) break;
}
You also have some issue with splicing:
enemy.status.splice(enemy.status.indexOf(this), 1);
While there is nothing wrong with that line on it's own, let's step through the code:
line 101 of Enemy class:
for (var i:int = 0; i < status.length; i++) {
You loop through the status array. For this example, let's say you have two items in the array.
Item A is at position 0. Item B is at position 1 in the array.
First loop, i has a value of 0.
You call status[i].applyEffect(), since i == 0, that is item A.
Inside applyEffect let's say your condition is met so you call destroy (on the StatusEffect class)
Inside the StatusEffect.destroy() method you splice the status array.
Now status has only one item, item B, and item B now has an index of 0 (instead of 1).
Your loop loops, now i == 1.
However, there is no longer any item at index 1 so the loop exits, skipping item B.
To solve it, iterate backwards instead, then your indices won't get out of whack when you splice:
var i:int = status.length
while(i--){
status[i].applyEffect();
}
As an aside, it would be much cleaner if you used var status:Vector.<StatusEffect> instead of an var status:Array. You'd get compile time checking and type safety that way.

uiresume (gcbf) worked in the function, but failed when the function being called

I want to let the user select multiple ROI's on a picture. I used a while loop. I paused and let the user select an ROI. After that the while loop would continue unless the user clicked another button on the toolbar to terminate the while loop. The code worked on single pictures. I made the code a function. When I looped and called the function in another script, it failed to proceed. I pressed Ctrl + C and it showed that "Operation terminated by user during uiwait". Apparently the uiresume didn't work.
Please let me know where the problems are. Thanks! My code:
% Below was basically a copy of the example given in R2014a.
% It created an interactive toolbar with 2 pushbuttons.
fh = figure('ToolBar', 'none'); hold on;
h_im = imshow(rgb2gray (I));
tbh = uitoolbar(fh);
[X_OK, map_OK] = imread(fullfile(matlabroot,...
'toolbox','matlab','icons','greenarrowicon.gif'));
[X_rect, map_rect] = imread(fullfile(matlabroot,...
'toolbox','matlab','icons','tool_rectangle.gif'));
% Convert indexed image and colormap to truecolor
icon_OK = ind2rgb(X_OK, map_OK);
icon_rect = ind2rgb(X_rect, map_rect);
% Create 2 uipushtools in the toolbar
% I introduced 2 variables, leave and draw, to facilitate the control of the
% later while-loop.
pth_OK = uipushtool(tbh, 'CData',icon_OK,...
'TooltipString','Toolbar push button',...
'ClickedCallback',...
'leave = 1; uiresume (gcbf)');
pth_rect = uipushtool(tbh, 'CData',icon_rect, 'Separator','on',...
'TooltipString','Toolbar push button',...
'ClickedCallback',...
'draw = 1; uiresume (gcbf)');
% The loop for ROI selection.
ii = 1;
% Maximum the use can select 30 ROI's.
while ii < 31;
draw = 0;
uiwait;
if leave == 1;
% If the user pressed "OK" button, leave will be 1.
break;
end;
if draw == 1;
% If the user pressed the "rect" button, draw will be 1.
h = imrect;
wait (h);
gui_Mask = createMask(h, h_im);
greyImg (~gui_Mask) = 255;
ii = ii + 1;
continue;
end;
end;
I saw someone used set/waitfor instead.
I tried to change to set/waitfor. Now it works both in the function and when the function being called.
But I still want to know why uiresume didn't work.

Save Number with Shared Object and Add to that Saved Number.

Hey everyone so basically what I am trying to accomplish is saving a number with the Shared Object which the the coins that the player collects in the game and if they player quits out of the game and comes back to play again the amount of coins he had when he quit will still show and he will be able to add to that amount if the player picks up more coins. I had this working for a bit but then the textfield started displaying "NAN".
Here is how I have it set up.
The Variable private var nCoins:Number;
In the constructor function I have:
sharedObjectCoins = SharedObject.getLocal("CoinsData");
nCoins = 0 + sharedObjectCoins.data.tCoins;
if (sharedObjectCoins.data.tCoins == null)
{
sharedObjectCoins.data.tCoins = nCoins;
}else
{
trace("Save data found."); // if we did find data...
loadDataTimeAttack(); // ...load the data
}
and in the games Enter.Frame Loop I have the function saveDataCoins which is setup like so:
private function saveDataCoins():void
{
if (nCoins > sharedObjectCoins.data.tCoins )
{
sharedObjectCoins.data.tCoins = nCoins;
}
coinsGraphic.coinsText.text = " " + sharedObjectCoins.data.tCoins;
sharedObjectCoins.flush();
}
not sure if you need the function to where the hitTest takes place between the coins and player but here it is:
private function checkPlayerHitCoins():void
{
for (var i:int = 0; i < aCoinsArray.length; i++)
{
//get current point in i loop
var currentCoins:mcCoin = aCoinsArray[i];
//test if player is hitting current point
if(player.hitTestObject(currentCoins))
{
nCoins += 1;
updatecoinsTextScore();
updateCoinsPauseScreen();
//Add points sound effects
var coinsSEffect:Sound = new coinsSound();
coinsSEffect.play();
//remove point on stage
currentCoins.destroyCoins();
//remove points from array
aCoinsArray.splice(i, 1);
trace("Hit: " + aCoinsArray.length);
}
}
}
Please if anyone could help me with this maybe point something out that I am doing wrong. This code worked perfect one time and when I closed the screen and came back to re test it the textfield displayed NAN and thats it when I hitTest the coins sometimes the NAN switches to a number for like a second but then goes back to NAN.
The first time (or rather every time it creates a new shared object) you will be trying to add undefined to 0, which will result in either a runtime error or NaN.
You need to check if the value exists before attempting to do addition with it.
if(sharedObjectCoints.data && sharedObjectCoins.data.tCoins && !isNaN(sharedObjectCoins.data.tCoins)){
nCoins = Number(sharedObjectCoins.data.tCoins); //there's not point in adding 0
trace("Save data found."); // if we did find data...
loadDataTimeAttack(); // ...load the data
}else{
sharedObjectCoins.data.tCoins = nCoins;
}
Also, if you don't manually set a value to a number var, it will start off life as NaN. eg var nCoins:Number will be NaN until you set it to something.
That said, working with the sharedObject directly like this is a very sloppy way to code your program. Really you should just use shared object to load and save the value, and everything in between use a strongly typed variable.
var nCoins:int = 0;
var tCoins:int = 0;
sharedObjectCoins = SharedObject.getLocal("CoinsData");
if(sharedObjectCoins.data && sharedObjectCoins.data.tCoins && !isNaN(sharedObjectCoins.data.tCoins){
tCoins = int(sharedObjectCoins.data.tCoins);
}else{
//no shared object, use default value for tCoins
tCoins = 0; //or whatever it should start off as.
}
Then write a save function
private function saveSharedObject():void {
sharedObjectCoins.data.tCoins = tCoins;
sharedObjectCoins.flush();
}
Then replace all other instances of sharedObjectCoins.data.tCoins with the var tCoins
It's probably best not to flush the shared object every frame for performance purposes.
Also, shared objects may or may not actually save, depending on user preferences, storage space available, etc. They should not be relied upon for critical data retention.
You can listen for problems with the shared object with AsyncErrorEvent.ASYNC_ERROR I believe (It's been a while since I've worked with AS3 Shared Objects)

Error 1010 Actionscript 3

i get Error #1010 when i use this while loop:
while (pos.length>0)
{
coo = pos.splice(Math.floor(Math.random() * pos.length),1)[0];
(pos_array[index]).x = coo.x;
(pos_array[index]).y = coo.y;
index++;
}
The error says: A term is undefined and has no properties.
What is wrong with my loop because I used the same loop for other programms and i got no such error.
Thank you for your attention.
Your while loop is breaking.
pos.length will never change and eventually pos_array[index] will be out of bounds.
When when you are out of bounds it is undefined.
So basically you are doing.
undefined.x = coo.x;
And just like the error says undefined has has no properties.
I can't see how this loop ever worked.
Try this instead much cleaner
var savedX:Number = 0
for each( var obj:Object in pos_array ){
coo = new MovieClip()
coo = pos.splice(Math.floor(Math.random() * pos.length),1)[0];
obj.x = savedX;
obj.y = 0;
savedX += coo.width;
}
Without knowing what the collection contains, I'm assuming it is filled with DisplayObjects or an object that has x and y properties?
Cast the reference so the compiler understands what the collection contains. For example:
DisplayObject(pos_array[index]).x = coo.x;
DisplayObject(pos_array[index]).y = coo.y;
...or whatever type your collection contains.
Perhaps pos.length and pos_array.length are not equal when the loop starts.
Try this:
while (pos.length>0)
{
coo = pos.splice(Math.floor(Math.random() * pos.length),1)[0];
if (pos_array[index])
{
(pos_array[index]).x = coo.x;
(pos_array[index]).y = coo.y;
}
index++;
}

What does this mean in AS3?

Hello I've started to learning AS3 from one book and found something I don't understand.
Ellipse(_board[row][column]).fill = getFill(row, column);
_board is two dimensional array of Ellipse type, so I just dont understand why is Ellipse(Ellipse object) used when it apparently works without it, or I haven't seen any changes when I omitted it.
Ellipse(_board[row][column]) is a type cast Type(object)
Because you can push anything into an Array the compiler doesn't know what kind of objects are stored within the Array. So it is good style to cast objects if you retrive them from an Array to their correct type.
This has several benefits:
if such an object is not of the type you expect or null, you will know when you typecast instead of getting an error later somewhere far away
the code executes a bit faster if you are explicit about the types
if you use a good IDE it can provide you with autocompletion when it knows the types
_board is a multidimensional array filled first with Arrays.
In the BoardDisplay.mxml
(Hello! Flex 4 : Chapter 3. Hello Spark: primitives, comp... > FXG and MXML graphics—building a game.. - Pg. 80) ,
<Graphic version="1.0" viewHeight="601" viewWidth="701"
xmlns=" library://ns. adobe. com/flex/spark"
xmlns:fx=" http://ns. adobe. com/mxml/2009"
xmlns:mx=" library://ns. adobe. com/flex/halo"
initialize="createBoard()"
click=" clickHandler(event)">
initialize calls createBoard().
private function createBoard():void {
newGame();
_board = new Array(6);
for (var row:int = 0; row < 6; row++) {
_board[row] = new Array(7);
for (var col:int = 0; col < 7; col++) {
_board[row][col] = addEllipse(row, col); //Magic Happens!
}
}
}
addEllipse returns Ellipse to each entry in _board
private function addEllipse(row:int, col:int):Ellipse {
var ellipse:Ellipse = new Ellipse();
ellipse.x = 4 + col*100;
ellipse.y = 5 + row*100;
ellipse.width = 90;
ellipse.height = 90;
ellipse.fill = getFill(row,col); // Magic Found !
ellipse.stroke = new SolidColorStroke(0x000000, 1, 1.0, false,"normal", null, "miter", 4);
boardGroup.addElement(ellipse);
return ellipse;
}
The author casted it as maxmx said but did not really need to as all the entries were of type Ellipse so
Ellipse(_board[row][column]).fill = getFill(row, column);
can work as
_board[row][column].fill = getFill(row, column);