AS3: Vector item isn't spliced - actionscript-3

Hello I am creating a system with a gun that shoots bullet.
The update function is processed this way:
var b:Bullet;
var l:uint = bulletList.length;
var i:uint;
for (i = 0; i < l; i++) {
b = bulletList[i];
b.sprite.x += b.vx;
b.sprite.y += b.vy;
if (b.sprite.x > 1200 || b.sprite.x < -100 || b.sprite.y < -1000) {
deleteBullet(b);
bulletList.splice(i,1);
}
}
public function deleteBullet(b:Bullet) {
b.sprite = null;
b = null;
}
When I shoot a bullet and it goes of the edge it generates an error, and sometimes it creates a new one but that one doesn't have any motion at all. This is the error I get:
RangeError: Error #1125: The index 1 is out of range 1.

You're getting that error because you're splicing your array while in a for loop.
instead of using 'l' as your parameter for the for loop, use bulletList.length directly as every iteration it will look at the CURRENT length which will reflect anything spliced out of it. You'll also need subtract your iterator when splicing as that shifts all future indexes down by one.
for (i = 0; i < bulletList.length; i++) {
b = bulletList[i];
b.sprite.x += b.vx;
b.sprite.y += b.vy;
if (b.sprite.x > 1200 || b.sprite.x < -100 || b.sprite.y < -1000) {
deleteBullet(b);
bulletList.splice(i,1);
i--;
}
}

Related

Different objects with the same function IF Error -Actionscipt

I am trying to create a drag and drop game.
I used an array and it works with drag and drop, but doesn't work with the IF function.
Instead, it shows me this error:
"TypeError: Error #1010: A term is undefined and has no properties.
at
Test_game_fla::MainTimeline/drop()[Test_game_fla.MainTimeline::frame1:38]"
Here is my code:
var s = 0;
score.text = s;
var mixed:Array = new Array;
mixed.push(orange);
mixed.push(cheese);
mixed.push(lobbio);
mixed.push(meat);
mixed.push(fish);
for (var i:uint = 0; i < mixed.length; i++) {
mixed[i].addEventListener(MouseEvent.MOUSE_DOWN, drag);
mixed[i].addEventListener(MouseEvent.MOUSE_UP, drop);
function drag(e)
{
e.target.startDrag();
}
function drop(e)
{
e.target.stopDrag();
if( (cheese.y > 50 && cheese.y < 150) && (cheese.x > 480 && cheese.x < 570) )
{
cheese.x = -50;
cheese.y = -50;
s = s + 10;
score.text = s;
}
if( (mixed[i].y > 50 && mixed[i].y < 150) && (mixed[i].x > 480 && mixed[i].x
< 570) )
{
mixed[i].y = -50;
mixed[i].x = -50;
s = s + 10;
score.text = s;
}
}
}
To explain what your actual problem is, it's the use of inline functions.
Inside your for loop, you define some functions (those are referred to as inline functions, and as mentioned in VC.One's answer, this is not a good practice - please follow their other advice as well).
Since those functions are defined inside the for loop block, every iteration you are actually creating a whole new function/object. When you create those functions, the ones called drop are referencing your iterator (i). BUT, those functions are not actually called until you MOUSE_UP. This mouse up trigger will happen long after the for loop has finished. At which point i will have a value of 5. Why 5? (as it only iterates from 0 - 4), because after every iteration i is incremented, so it will end up 1 higher than the last iteration.
Since there is no element at position 5 in your mixed array, you get the error.
To rectify the situation, follow the code example from the other answers (which break those functions out of the for loop)
Instead of referencing i, use the event's currentTarget property (which is a reference to the object that you attached the listener to).
Sprite(e.currentTarget).y
U can fix it like this.
var s = 0;
score.text = s;
var mixed:Array = new Array;
mixed.push(orange);
mixed.push(cheese);
mixed.push(lobbio);
mixed.push(meat);
mixed.push(fish);
for (var i:uint = 0; i < mixed.length; i++) {
mixed[i].addEventListener(MouseEvent.MOUSE_DOWN, drag);
mixed[i].addEventListener(MouseEvent.MOUSE_UP, drop);
}
function drag(e)
{
e.currentTarget.startDrag();
}
function drop(e)
{
var mix:Sprite = Sprite(e.currentTarget);
mix.stopDrag();
if( (cheese.y > 50 && cheese.y < 150) && (cheese.x > 480 && cheese.x < 570) )
{
cheese.x = -50;
cheese.y = -50;
s = s + 10;
score.text = s;
}
//Error in mixed[i] ; at last i = mixed.length mixed[i] is null.
//Event has a property "currentTarget" this can get u select target
if( (mix.y > 50 && mix.y < 150) && mix.x > 480 && mix.x < 570) )
{
mix.y = -50;
mix.x = -50;
s = s + 10;
score.text = s;
}
}
Some things to fix :
(1) Give your variables a data type. Saying var s = 0; suggests that s is a numerical variable, right? Well score.text expects a String not Number so you must use casting to convert. Try :
var s : int = 0; //define a numerical variable
score.text = String(s); //cast number into String type (for usage as text)
(2) Don't put your functions inside a For-loop!!! Don't even put functions inside other functions (unless you know what an Anonymous Function is, and you can justify needing it).
(3) You can shorten some code typing by :
Incrementing? Use s += 10 to avoid longer s = s + 10;. Can also be -=, /= etc.
If setting same value to multiple variables just chain like so: cheese.x = cheese.y = -50;
(4) Don't try to access mixed[i] by mixed.length. Since length is 5 at some point the compiler sees an instruction like : mixed[5].addEventListener...
However an array starts at zero for first item so you should understand the fifth item is really at mixed[4].addEventListener....
That other mixed[5].something (same as : mixed[mixed.length].something) does not exist.
PS : I would have thought that setting i < mixed.length would have protected against going over the array size, since if i must be smaller than .length then i == 5 could never happen.
Anyways... This code below is untested (no AS3 compiler here) but try the following :
var s : int = 0;
score.text = String(s);
var mixed:Array = new Array;
mixed.push(orange); mixed.push(cheese);
mixed.push(lobbio); mixed.push(meat); mixed.push(fish);
for (var i:uint = 0; i <= (mixed.length-1); i++)
{
mixed[i].addEventListener(MouseEvent.MOUSE_DOWN, drag);
mixed[i].addEventListener(MouseEvent.MOUSE_UP, drop);
} //end "For" loop
function drag(e) : void { e.target.startDrag(); }
function drop(e) : void
{
e.target.stopDrag();
if( (cheese.y > 50 && cheese.y < 150) && (cheese.x > 480 && cheese.x < 570) )
{
cheese.x = cheese.y = -50;
s += 10; //# achieves same thing as... s = s + 10;
score.text = String(s);
}
if( (Sprite(e.currentTarget).y > 50 && Sprite(e.currentTarget).y < 150) && (Sprite(e.currentTarget).x > 480 && Sprite(e.currentTarget).x < 570) )
{
Sprite(e.currentTarget).x = Sprite(e.currentTarget).y = -50;
s += 10;
score.text = String(s);
}
} //end Function "drop"

How to walk a game character using mouse event in Adobe Animate CC AS3?

This question is related to my previous post, “TypeError: Error #1010: A term is undefined and has no properties” in AS3 because as I mentioned there, I'm creating an Android Game for our thesis. Now, I have a spritesheet of a character in the link: sprite character, I'm using this in the game. I'm researching on how to walk a character, I found one at a website, it actually works but unfortunately, it fails because the character didn't actually walk. I have no idea on what code will be place there. Either I will walk a character by clicking mouse or I will create a button then click on it to walk a character. What would be the code can I use for that? Any help will be appreciated.
P.S. In my previous post, I'm creating a code from timeline but now I transfer it to Actionscript file because of some errors.
EDIT:
Here's my code of the character:
forward.addEventListener(MouseEvent.CLICK, ppap);
function ppap(event:MouseEvent):void{
gril.x += mouseX;
gril.y += mouseY;
gril.gotoAndStop('i');
gameloop();
}
function gameloop(): void {
for (var o = 0; o > 5; o++) {
if (linya.hitTestObject(gril)) {
o++;
gotoAndStop(2);
scorer.visible = true;
timer.visible = true;
}
}
}
And the line: gril.gotoAndStop('a'); where the character is standing.
The gril is the instance name of a character. When it reaches to linya, the question will appear. Thanks!
Let's walk through your broken game loop
function gameloop(): void {
for (var o = 0; o > 5; o++) { //sets o to 0, loops as long as o > 5 (which it isn't, since we just set it to 0;
if (linya.hitTestObject(gril)) {
o++; //this also adds 1 to o
gotoAndStop(2);
scorer.visible = true;
timer.visible = true;
}
// if this part ever executed, it would add 1 to o
}
}
Do you see the problem? This for loop will not execute even once since 0 < 5
Instead it should be
function gameloop(): void {
for (var i = 0; i < 5; i++) {
if (linya.hitTestObject(gril)) {
gotoAndStop(2);
scorer.visible = true;
timer.visible = true;
break;
}
}
}
So here we have a functional (but pointless) for loop. It will work, but the first time through the loop it is going to result in the exact same thing as the second and third and fourth and fifth because changing the variable value by 1 isn't actually changing anything at all. You just telling the program to check the collision state 5 times. Well it does this 5 times before anything else can change. It checks it 5 times every game loop. Well I promise you nothing is moving while that for loop is running so why check it 5 times? I suggest stepping back and getting some help from your teacher or something because this seems way off. Sorry.
Right again #NealDavis and thank You for the comment!!!
I wrote my comment too quickly
Ascending loop from 0->4 (5 items):
for (var i:uint = 0; i < 5; i++){
trace("ascending = " + i);
}
Output:
ascending = 0
ascending = 1
ascending = 2
ascending = 3
ascending = 4
Descending loop from 4->0 (5 items):
for (var j:int = 4; j>=0; j--){
// j must be an int in this case, second mistake!
trace("descending = " + j)
};
Output:
descending = 4
descending = 3
descending = 2
descending = 1
descending = 0
My mistake. SRY
This is well explained in Looping reference
And in ActionScript 3 fundamentals: Loops reference
Shame on me!!! ;)
I'm a noob! :D
So I deleted my comment ;)
WOW, I'm so sorry about this mistake!!!
// You may also create Vectors to store the values.
var ascending:Vector.<uint> = new Vector.<uint>;
var descending:Vector.<uint> = new Vector.<uint>;
for (var k:uint = 0; k < 5; k++){
ascending.push(k+1);
}
trace("ascending Vector.<uint> = " + ascending);
// Output : ascending Vector.<uint> = 1,2,3,4,5
for (var l:int = 4; l >= 0; l--){
descending.push(l+1);
}
trace("descending Vector.<uint> = " + descending);
// Output : descending Vector.<uint> = 5,4,3,2,1
Or in an ascending loop :
trace("\nascending Vector.<uint> loop : ")
for(var m:String in ascending){
trace(m + " = " + ascending[m]);
}
Output :
ascending Vector.<uint> loop :
0 = 1
1 = 2
2 = 3
3 = 4
4 = 5
Or in a descending loop :
trace("descending Vector.<uint> loop : ")
for(var n:String in descending){
trace(n + " = " + descending[n]);
}
Output:
descending Vector.<uint> loop :
0 = 5
1 = 4
2 = 3
3 = 2
4 = 1

Flash - Adding objects to a scene randomly, at certain set points

I'm in the process of creating a simple Flash game using ActionScript 3.0 and have come across an issue when spawning my obstacles onto the scene. My aim is to have approximately 10 points across the x axis (remaining at the same y axis) and when spawning the obstacles into my scene it will pick 2-4 of those points randomly and spawn them on them.
I've got the obstacles to spawn randomly but cannot figure out how to make them spawn at random set points, from a list. If anyone could help, I would be much appreciative. Thanks
EDIT:
The code I have so far:
var a:Array = new Array();
for (var count=0; count< 5; count++) {
a[count] = new asteroidOne();
a[count].x = 100 * count + (Math.floor(Math.random() * 200));
a[count].y = 100;
addChild(a[count]);
}
// Asteroid obstacle spawning 2.0
player.addEventListener(Event.ENTER_FRAME, obstacleMove);
function obstacleMove(evt:Event):void {
for (var i=0; i< 5; i++) {
a[i].y += 5;
if (a[i].y == 480) {
a[i].y = 0;
}
if (player.hitTestObject(a[i])) {
trace("HIT");
}
}
}
Assuming you have your spawn points in an array, you could do the following:
var spawnPoints:Array = [100,200,250,300,450,500,600,800]; //your list of spawn x locations
spawnPoints.sort(randomizeArray); //lets randomize the spwanPoints
function randomizeArray(a:*, b:*):int {
return ( Math.random() < .5 ) ? 1 : -1;
}
var a:Vector.<asteroidOne> = new Vector.<asteroidOne>(); //the array for your astroids - changed to vector for possible performance and code hint improvement (basically the same as Array but every object has to be of the specified type)
for (var count:int=0; count < 5; count++) {
a.push(new asteroidOne());
a[count].x = spawnPoints.pop(); //pop removes the last element from the array and returns it
a[count].y = 100;
addChild(a[count]);
}
EDIT
To address you comments, here is a decent example:
import flash.events.Event;
import flash.events.TimerEvent;
import flash.utils.Timer;
var spawnTimer:Timer = new Timer(10000); //timer will tick every 10 seconds
spawnTimer.addEventListener(TimerEvent.TIMER, spawn, false, 0, true); //let's run the spawn function every timer tick
spawnTimer.start();
var spawnPoints:Array = [100,200,250,300,450,500,600,800]; //your list of spawn x locations
var spawnAmount:int = 5; //how many asteroids to have on the screen at once (you could increase this over time to make it more difficult)
var asteroids:Vector.<asteroidOne> = new Vector.<asteroidOne>(); //the array for your asteroids - changed to vector for possible performance and code hint improvement (basically the same as Array but every object has to be of the specified type)
spawn(); //lets call it right away (otherwise it will won't be called until the first timer tick in 10 seconds)
//calling this will spawn as many new asteroids as are needed to reach the given amount
function spawn(e:Event = null):void {
if(asteroids.length >= spawnAmount) return; //let's not bother running any of the code below if no new asteroids are needed
spawnPoints.sort(randomizeArray); //lets randomize the spwanPoints
var spawnIndex:int = 0;
var a:asteroidOne; //var to hold the asteroid every loop
while (asteroids.length < spawnAmount) {
a = new asteroidOne();
a.x = spawnPoints[spawnIndex];
spawnIndex++; //incriment the spawn index
if (spawnIndex >= spawnPoints.length) spawnIndex = 0; //if the index is out of range of the amount of items in the array, go back to the start
a.y = 100;
asteroids.push(a); //add it to the array/vector
addChild(a); //add it to the display
}
}
player.addEventListener(Event.ENTER_FRAME, obstacleMove);
function obstacleMove(evt:Event):void {
//this is the same as a backwards for loop - for(var i:int=asteroids.length-1;i >= 0; i--)
var i:int = asteroids.length;
while(i--){ //since we are potentially removing items from the array/vector, we need to iterate backwards - otherwise when you remove an item, the indices will have shifted and you'll eventually get an out of range error
asteroids[i].y += 5;
if (asteroids[i].y > stage.stageHeight || asteroids[i].x > stage.stageWidth || asteroids[i].x < -asteroids[i].width || asteroids[i].y < -asteroids[i].height) {
//object is out of the bounds of the stage, let's remove it
removeChild(asteroids[i]); //remove it from the display
asteroids.splice(i, 1); //remove it from the array/vector
continue; //move on to the next iteration in the for loop
}
if (player.hitTestObject(asteroids[i])) {
trace("HIT");
}
}
}
function randomizeArray(a:*, b:*):int {
return ( Math.random() < .5 ) ? 1 : -1;
}

supplied DisplayObject must be a child of the caller fix

Im confused as to why my codes saying this,
My game creates a procedural generated map and the map is split into chuncks. The map works fine, i can switch areas and stuff no problem but when i delete a tree tile and re-add a grass tile then try to switch areas it tells me "The supplied DisplayObject must be a child of the caller." Iv gottn and fixed and slighty understand this problem, but i feel as if it IS a child of the calleer. idk :c
How my code is set up it creates a world class on my level class, then in that world a worldTiles sprite is created to place the tiles of the world into. This is where the tiles are originally added and deleted
This is where im pretty sure my problem is, the fucntion that deletes a tree tile and adds a grass tile
protected function mouseOnTile()
{
for (var i:int; i < world.tilesInWorld.length; i++)
{
if (mouse.hitTestObject(world.tilesInWorld[i]))
{
trace(world.tilesInWorld[i].Name);
if (world.tilesInWorld[i].Name == "tree")
{
var tx:int = world.tilesInWorld[i].x;
var ty:int = world.tilesInWorld[i].y;
world.worldTiles.removeChild(world.tilesInWorld[i]);
world.treePool.returnSprite(world.tilesInWorld[i]);
world.tilesInWorld.pop();
world.tile = world.tilePool.getSprite();
world.tile.width = world.TILE_SIZE;
world.tile.height = world.TILE_SIZE;
world.tile.x = tx;
world.tile.y = ty;
world.tilesInWorld.push(world.tile);
world.worldTiles.addChild(world.tile);
}
}
}
}
Im sure it has something to do with how im re-adding the grass tile into the worldTiles, but im confused on how else i could do it?
This is the function that deletes the tiles when you walk to a different screen section
public function deleteTiles()
{
if (tilesInWorld.length > 0)
{
for (var i:int = tilesInWorld.length - 1; i >= 0; i--)
{
worldTiles.removeChild(tilesInWorld[i]);
switch (tilesInWorld[i].Name)
{
case "water" :
waterPool.returnSprite(tilesInWorld[i]);
break;
case "shallow" :
shallowPool.returnSprite(tilesInWorld[i]);
break;
case "shell" :
shellPool.returnSprite(tilesInWorld[i]);
break;
case "sand" :
sandPool.returnSprite(tilesInWorld[i]);
break;
case "tree" :
treePool.returnSprite(tilesInWorld[i]);
break;
case "grass" :
tilePool.returnSprite(tilesInWorld[i]);
break;
case "rock" :
rockPool.returnSprite(tilesInWorld[i]);
break;
case "stone" :
stonePool.returnSprite(tilesInWorld[i]);
break;
}
}
tilesInWorld.length = 0;//empty array
generateTile();
}
}
This is where the tiles are generated onto the screen after being deleted
public function generateTile()
{
var Xcalc:int = (X + (800 / TILE_SIZE) / GlobalCode.MAP_SCALE);
var Ycalc:int = (Y + (600 / TILE_SIZE) / GlobalCode.MAP_SCALE);
for (var i:int = X; i < Xcalc; i++)
{
for (var j:int = Y; j < Ycalc; j++)
{
hm = heightmap[i][j];
if ((hm >= 0.84))
{
tile = waterPool.getSprite();
}
else if (((hm >= 0.8) && hm < 0.84))
{
tile = shallowPool.getSprite();
}
else if (((hm >= 0.79) && hm < 0.799))
{
tile = shellPool.getSprite();
}
else if (((hm >= 0.7) && hm < 0.8))
{
tile = sandPool.getSprite();
}
else if (((hm >= 0.35) && hm < 0.4))
{
tile = treePool.getSprite();
}
else if (((hm >= 0.2) && hm < 0.7))
{
tile = tilePool.getSprite();
}
else if (((hm >= 0.09) && hm < 0.2))
{
tile = stonePool.getSprite();
}
else
{
tile = rockPool.getSprite();
}
tile.width = TILE_SIZE;
tile.height = TILE_SIZE;
worldTiles.x = 0;
worldTiles.y = 0;
tile.x = TILE_SIZE * (i % 800);
tile.y = TILE_SIZE * (j % 600);
tilesInWorld.push(tile);
worldTiles.addChild(tile);
}
}
}
I believe your problem is here:
for (var i:int; i < world.tilesInWorld.length; i++)
{
...
world.worldTiles.removeChild(world.tilesInWorld[i]);
world.treePool.returnSprite(world.tilesInWorld[i]);
world.tilesInWorld.pop();
...
}
You are lopping forward through the array, using removeChild on an element in the array, and using "pop" which removes the last item of the array, not the item that was actually removed. You eventually will hit an item that was already removed. Additionally, your i pointer changes each time you pop, which means the loop will never hit every item and is fundamentally flawed.
The DisplayObject used as the argument in dO.removeChild() must be defined, non-null, and a child of dO (i.e. added with dO.addChild(). If it does not meet all of those requirements, it will error out.
To fix this, use splice() instead of pop() (which will allow you to remove a specific element in an array) and go backwards through the array (which will handle the i pointer issues)
for (var i:int = world.tilesInWorld.length - 1; i >= 0; --i)
{
...
world.worldTiles.removeChild(world.tilesInWorld[i]);
world.treePool.returnSprite(world.tilesInWorld[i]);
world.tilesInWorld.splice(i, 1);
...
}
You can also loop forward through the array, but you need to modify the pointer. This is slower and more error prone than going backwards, but can work just as well (when I say slower, we're talking microseconds difference unless you are doing massive computations).
for (var i:int; i < world.tilesInWorld.length; i++)
{
...
world.worldTiles.removeChild(world.tilesInWorld[i]);
world.treePool.returnSprite(world.tilesInWorld[i]);
world.tilesInWorld.splice(i, 1);
--i;
...
}
Additionally, and this is just a syntax/readability thing, you should never rely on a datatype's default value. int will default to 0, but you should still declare it as var i:int = 0 to make it easy to change in the future, standardized and easy to read, and so that you could easily change it to a Number, which has a much, much higher max value than int but defaults to NaN.
This does not make any sense. Since you are removing all element from the array you can jusr forget about pop or slice and do
world.tilesInWorld.length = 0;
after the loop.
Since you are removing all object from world.worldTiles don't bother removing them one by one and do:
world.worldTiles.removeChildren()
after the loop.
Finally you are left with a simple loop where you only do:
world.treePool.returnSprite(world.tilesInWorld[i]);
It's a case (in your case and in the answer given) where you both try very hard to make a simple code as complicated as possible.

AS3 hittest keeps hitting

I have the following problem:
I want to keep a score when i "hittest". I use the following code:
private function fnMoveMap():void
{
for (var i:int = 0; i < vPipeMax; i++)
{
var tmpPipe = _conMap.getChildAt(i);
//trace (tmpPipe.name);
if (tmpPipe._HIT.hitTestPoint(_P.x, _P.y, true))
{
tmpPipe.visible = false;
//stage.removeEventListener(Event.ENTER_FRAME, setScore);
vScores++;
txtScores.text = vScores.toString();
//break;
}
//reset pos
if (tmpPipe.x < 0)
{
//stage.addEventListener(Event.ENTER_FRAME, setScore);
tmpPipe.visible = true;
tmpPipe.x = 1050 - vXSpeed;
tmpPipe.y = randomRangeMC(minPipeY, maxPipeY);
//set score
//vScores++;
//txtScores.text = vScores.toString();
}
else
{
tmpPipe.x -= vXSpeed;
}
}
}
the var vScores keeps counts for 4 to 8 times.
How can i just count one?
The reason your vScores variable is incrementing by 4-8 is because you're looping multiple times with the for loop through vPipeMax.
You either need to restructure your code so that doesn't happen, or break out of the loop as soon as you increment the score.
Adobe's documentation on break: http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/statements.html#break