What is causing this glitch? - actionscript-3

I am currently coding a game, and have encountered an annoying glitch. Occasionally, when you kill an enemy in game, it will drop extra currency, and another enemy onscreen will be removed from the enemylist. However, this second enemy, the one removed from the enemylist, will still be on screen, and will still shoot at the player. Below are code snippets from the collision formula, the enemies destruction sequence, and the bullet hitcheck sequence.
Collision formula:
public function testCollision(enemy:Entity):Boolean{
var eX:Number = enemy.collision.xPos
var eY:Number = enemy.collision.yPos
var eSL:Number = enemy.collision.SideLength/2
if(eX-xPos<(SideLength/2)+eSL && eY-yPos<(SideLength/2)+eSL && eX-xPos>-(SideLength/2)-eSL && eY-yPos>-(SideLength/2)-eSL){
return true
}else{
return false
}
}
Enemy destruction sequence:
if(deathVar){
view.transparency -= 1/20
if(view.transparency<0.1){
var cur = new PixelCurrency(2)
cur.collision.xPos = collision.xPos
cur.collision.yPos = collision.yPos
entityCreated.dispatch(cur)
destroy()
}
}
Bullet hitcheck:
for each (var enemy:Entity in Game.entities){
if(enemy.allies == Pixapocalypse.EnemyFaction){
if(collision.testCollision(enemy)){
if(enemy.life){
enemy.life.changeHealth(-2)
this.sound.playSound(new basicHitSound())
this.destroy()
break
}
else{
}
}
}
}
If you need any extra info, please tell me, I am greatly appreciative of your help.

What happens when Enemy.life < 0 ?

Related

Objects spawn randomly in Flash actionscript 3.0

I am creating a game that a sheep need to jump or crouch to avoid obstacles.
I had create 2 types of obstacles but they come in a constant speed.
Sometimes the 2 obstacles come together make it impossible to avoid. Is there any way to change this?
Can I make the 2 obstacles come in randomly in a random speed?Here is my code.`
hole_mc.visible = false;
bird_mc.visible = false;
playhotarea_btn.addEventListener(MouseEvent.CLICK, removeInstructionBox);
function removeInstructionBox(event:MouseEvent):void
{
playhotarea_btn.removeEventListener(MouseEvent.CLICK, removeInstructionBox);
instructionbox_mc.visible = false;
instructiontext_mc.visible = false;
playbtn_mc.visible = false;
playbtntext_mc.visible = false;
sheep_mc.sheepIN_mc.visible = false;
sheep_mc.gotoAndPlay("sheepwalk");
treebg_mc.gotoAndPlay("bgloop");
hole_mc.visible = true;
bird_mc.visible = true;
timer.start();
}
hole_mc.addEventListener(Event.ENTER_FRAME, holeMove);
function holeMove(event:Event):void {
if (hole_mc.x>= -200) {
hole_mc.x -=7;
}else{
hole_mc.x=1080;
trace("hole loops");
}
}
bird_mc.addEventListener(Event.ENTER_FRAME, birdMove);
function birdMove(event:Event):void {
if (bird_mc.x>= -200) {
bird_mc.x -=10;
}else{
bird_mc.x=1080;
trace("bird loops");
}
}
stage.addEventListener(Event.ENTER_FRAME, hitHole);
function hitHole(event:Event):void{
if (sheep_mc.hitTestObject(hole_mc)){
gotoAndStop("GameOver");
hole_mc.removeEventListener(Event.ENTER_FRAME, holeMove);
stage.removeEventListener(Event.ENTER_FRAME, hitHole);
bird_mc.removeEventListener(Event.ENTER_FRAME, birdMove);
stage.removeEventListener(Event.ENTER_FRAME, hitBird);
}
}
stage.addEventListener(Event.ENTER_FRAME, hitBird);
function hitBird(event:Event):void{
if (sheep_mc.hitTestObject(bird_mc)){
gotoAndStop("GameOver");
bird_mc.removeEventListener(Event.ENTER_FRAME, birdMove);
stage.removeEventListener(Event.ENTER_FRAME, hitBird);
hole_mc.removeEventListener(Event.ENTER_FRAME, holeMove);
stage.removeEventListener(Event.ENTER_FRAME, hitHole);
}
}
var currentSecond:Number = 0;
var delay:Number = 1000;
var timer:Timer = new Timer(delay);
timer.addEventListener(TimerEvent.TIMER, timerEventHandler);
function timerEventHandler(event:TimerEvent):void
{
currentSecond++;
trace(currentSecond);
score_txt.text = String(currentSecond + " s");
}
`
You can always avoid spawning things in the same place by testing for "collision" before you create (or in your case, move) the new object. You should do the testing in whatever class has a reference to all the objects on the screen. In this case, the class you posted has references to the hole and bird, which are the two objects you want to NOT have the same x value.
Try something along the lines of:
function birdMove(event:Event):void {
if (bird_mc.x>= -200) {
bird_mc.x -=10;
}else{
if(hole_mc.x > 1000){ // See notes below
bird_mc.x=1080;
trace("bird loops");
}}}
I put 1000 under the assumption that you would want a little bit of space between the two obstacles. If you're only worried about them being EXACTLY on top of each other, then you can just use if hole_mc.x = 1080. But by using > some number, you can guarantee a little bit of room between them.
You should add a similar line to the hole mover as well so that it doesn't matter which one gets called first. They both check before they respawn.

Removing multiple children from the stage if the item is on stage

Good afternoon.
I'm making a game, and in games as you know, when you destroy an object, you must remove it from the stage.
My enemies have been added dynamically, via code and if the user goes back the enemy would still be in the display list.
I have attempted to remove enemies by trying this code.
removeChild(character); /removes player
removeChild(ground); // removes ground
childrenOnStage is a number which equals this.numChildren
for (var b:int = 0; b < childrenOnStage; b++)
{
if (getChildAt(b).name == "enemy")
{
removeChild(getChildAt(b));
}
}
When the user goes back to the main menu from the game, it runs this code.
The code loops though the all of the children on the stage, and those that have the name enemy should be removed.
But I get an error
[Fault] exception, information=RangeError: Error #2006: The supplied index is out of bounds.
My question is, how can I remove these enemies?
What if the enemy has been removed, this will cause more errors such as "null object" i.e enemy is not on stage, so why should I remove this enemy if it's not on?
Thank you.
Update due to Chernivs answer
//after adding all of the children, this must be updated last
childrenOnStage = this.numChildren;
private function fromLevtoStart(e:MouseEvent):void
{
if (e.target == backBtn1)
{
stage.removeEventListener(Event.ENTER_FRAME, level1)
stage.addEventListener(Event.ENTER_FRAME, mainGameLoop)
//container.removeChild(_character);
removeChild(character);
removeChild(ground);
for (var b:int = 0; b < childrenOnStage; b++)
{
if (getChildAt(b).name == "enemy")
{
removeChild(getChildAt(b));
//childrenOnStage --;
//update the variable below
childrenOnStage = this.numChildren;
}
}
this.gotoAndStop("Start");
}
}
This still doesn't remove all of the enemies, but only one for some reason.
it should loop through all of the children on stage that have the name enemy and remove them, but it doesn't.
After trying the asnwer below I can say that it only removes goblin1 which is named "enemy" and typ Goblin.
goblin1 = new Goblin();
goblin1.name = "enemy";
goblin2 = new Goblin();
goblin2.name = "enemy";
When I kill goblin1 it gets removed, then when I go back to the start screen goblin 2 doesn't even though that's the only goblin left with the name "enemy" it appears only goblin1 is targetted for some reason.
Instead of checking the name property, check the type of the display object. For example:
if(getChildAt(b) is Goblin) {
//remove
}
Basically, decreasing the variable should work. But if you get any problems with what you've removed, you can do this:
var enemies:Array = new Array();
for (var b:int = 0; b < childrenOnStage; b++) {
var child:DisplayObject = getChildAt(b);
if (child.name == "enemy") {
enemies.push(child);
}
}
trace(enemies);
See what you get. Loop through enemies and remove each of them - this way you won't need to decrease any variable nor think about numChildren - simply remove everything that is 'marked' as an enemy.

Creating collision detection on more than one variable in a for loop?

Essentially I am trying to create a game, where the player has to dodge certain items, so far I have a piece of code that randomly adds 3 sharks to the stage.
The idea is that once the player has hit a shark he/she returns to the start location, I have an Action Script file that contains the speed,velocity etc of the shark, the every time the program is run the sharks will appear in a different location.
However, when I attempt to do a collision test of the sharks only one of the sharks respond, I cannot figure out how to make it that all 3 sharks effect the player (square_mc). Any help would be greatly appreciated.
//Pirate game, where you have to avoid particular object and get to the finish line to move onto the final level.
stage.addEventListener(KeyboardEvent.KEY_DOWN, moveMode );
function moveMode(e:KeyboardEvent):void {
//movements for the pirate ship, this will allow the ship to move up,down,left and right.
if (e.keyCode == Keyboard.RIGHT) {
trace("right");
square_mc.x = square_mc.x + 25;
}
else if (e.keyCode == Keyboard.LEFT) {
trace("left");
square_mc.x = square_mc.x - 25;
}
else if (e.keyCode == Keyboard.UP) {
trace("up");
square_mc.y = square_mc.y - 25;
}
else if (e.keyCode == Keyboard.DOWN) {
trace("down");
square_mc.y = square_mc.y + 25;
}
}
//for.fla
//this program uses a for loop to create my Sharks
//a second for loop displays the property values of the sharks
function DisplayShark():void{
for (var i:Number=0;i<3;i++)
{
var shark:Shark = new Shark(500);
addChild(shark);
shark.name=("shark"+i);
shark.x=450*Math.random();
shark.y=350*Math.random();
}
}
DisplayShark();
for(var i=0; i<3;i++){
var currentShark:DisplayObject=getChildByName("shark"+i);
trace(currentShark.name+"has an x position of"+currentShark.x+"and a y position of"+currentShark.y);
}
//here we will look for colliosion detection between the two move clips.
addEventListener(Event.ENTER_FRAME, checkForCollision);
function checkForCollision(e:Event):void {
if (square_mc.hitTestObject(currentShark))
{
trace("The Square has hit the circle");
square_mc.x=50
square_mc.y=50 //these lines of code return the square back to it's original location
}
}
Just move your for loop into the ENTER_FRAME:
addEventListener(Event.ENTER_FRAME, checkForCollision);
function checkForCollision(e:Event):void {
for(var i=0; i<3;i++){
var currentShark:DisplayObject=getChildByName("shark"+i);
if (square_mc.hitTestObject(currentShark))
{
trace("The Square has hit the circle");
square_mc.x=50;
square_mc.y=50;
}
}
}
You can't just go through the for loop once and set the currentShark variable - you'll just end up testing against one shark every time you perform the collision test. Rather, every time you want to check collisions you must loop through all the sharks and do the collision testing.

AS3 Stop character from moving through walls

I want to stop the movieclips movement when it hits a wall (another movieclip).
The example below works, but after the collision the movieclip 'blocks' all movement to the left...
My question to you is, is this a good way and why isn't it working well?
There will be something wrong in this code, but i'm learning.
For now the example with the leftArrow key;
variables to check the key, if it's hitting the walls and if it's moving or not:
var leftArrow:Boolean;
var speed:int = 10;
var hitting:Boolean;
var ismoving:Boolean;
event listeners for the keys/movement and detecting collision:
stage.addEventListener(KeyboardEvent.KEY_DOWN, keyPressed);
stage.addEventListener(KeyboardEvent.KEY_UP, keyReleased);
stage.addEventListener(Event.ENTER_FRAME, walking);
stage.addEventListener(Event.ENTER_FRAME, detectHit);
detecting collision function:
function detectHit(e:Event) :void
{
if(char.hitTestObject(bounds))
{
hitting = true;
}
}
function to the left arrow key:
function keyPressed(event:KeyboardEvent):void
{
if (event.keyCode == Keyboard.LEFT)
{
leftArrow = true;
}
}
function keyReleased(event:KeyboardEvent):void
{
if (event.keyCode == Keyboard.LEFT)
{
leftArrow = false;
}
}
And the reason it's not working is probably here, but I don't understand why not:
function walking(event:Event):void {
if (rightArrow) {
char.x += speed;
}
if (leftArrow && ! hitting) {
char.x -= speed;
}
else
{
ismoving = false
}
if (leftArrow && ! hitting)
char will move if hitting is false. When char.hitTestObject(bounds) is true you are setting hitting to true. You are not setting hitting again to false anywhere. That's why once left wall is hit it stops left movement permanently. You need to figure out suitable condition to set hitting to false again.
Adding an else branch in detectHit should solve the problem.
function detectHit(e:Event):void {
if(char.hitTestObject(bounds))
{
hitting = true;
} else {
hitting = false; // add this
}
}
Allthough Taskinoor's method should work, I would suggest another way to do your hittests.
Since you probably are creating a game (character and bounds), you will have more than one bound. In that case, I would strongly suggest bitmap-hittesting. This way, you can create all your bounds in one movieclip and test for a hit.
I will explain this by using the example of a maze. The maze would then be some lines in a movieclip, randomly put together. If you use HitTestObject and you aren't hitting one of the lines, but your character is over the movieclip, hitTestObject will return true, even though you are not hitting a wall. By using bitmapHitTesting, you can overcome this problem (BitmapHitTest takes transparant pixels into account, whereas hitTestObject does not).
Below you can find an example of how to do bitmapHitTesting. Creating the bitmaps in this function is not necesarry if they do not change shape. In that case, I would suggest placing the code for the bitmapdata in a added_to_stage-method.
private var _previousX:int;
private var _previousY:int;
private var _bmpd:BitmapData ;
private var _physicalBitmapData:BitmapData;
private function walkAround(e:Event):void
{
var _xTo:int = //Calculate x-to-position;
var _yTo:int = //Calculate y-to-position;
//If your character doesn't change shape, you don't have to recalculate this bitmapData over and over.
//I suggest placing it in a ADDED_TO_STAGE-Event in that case.
_bmpd = new BitmapData(char.width, char.height, true, 0);
_bmpd.draw(char);
//If your bounds are static, you don't have to recalculate this bitmapData over and over.
//I suggest placing it in a ADDED_TO_STAGE-Event in that case.
_physicalBitmapData = new BitmapData(bounds.width, bounds.height, true, 0);
_bmpd.draw(bounds);
//The below line is the actual hittest
if(_physicalBitmapData.hitTest(new Point(0, 0), 255, _bmpd, new Point(char.x, char.y), 255))
{
char.x = _previousX;
char.y = _previousY;
}
else
{
char.x = _xTo;
char.y = _yTo;
}
_previousX = char.x;
_previousY = char.y;
}
Look at my hint,
function loop(Event)
{
if(isHit==false)
{
if(isRight==true){head_mc.x+=1}
if(isLeft==true){head_mc.x-=1}
if(isUp==true){head_mc.y-=1}
if(isDown==true){head_mc.y+=1}
}
if(head_mc.hitTestObject(build_mc))
{
isHit=true;
if(isRight==true){head_mc.x-=1}
if(isLeft==true){head_mc.x+=1}
if(isUp==true){head_mc.y+=1}
if(isDown==true){head_mc.y-=1}
}
else
{
isHit=false;
}
}
I use step back to opposite direction instead.

I'm trying to set up my collision detect so that it ignores a child of the target. (Actionscript 3)

So in my game, every 'enemy' movieclip creates a textfield that represents the name of the enemy. The problem is, I want my collision detect engine to detect a collision with the enemy itself, not the textfield.
This is the code that I have currently have on my collision class for detecting a collision with the enemy:
for(var i = 0;i < _enemies.length; i++)
{
if(CollisionEngine.isColliding(_laser, _enemies[i]._enemyNameTextField, _animation))
{
}
else if(CollisionEngine.isColliding(_laser, _enemies[i], _animation))
{
_enemies[i].kill();
_animation._killedEnemy = true;
}
}
The first if clause checks for a collision with the enemy's text field. The else if checks for a collision with the enemy.
The problem with this current implementation is that if the 'laser' hits the textfield first, passes through it, and hits the enemy, it's not detected as a collision.
Any idea on how I could work around this?
Change the order of the checks:
if(CollisionEngine.isColliding(_laser, _enemies[i], _animation))
{
if(!CollisionEngine.isColliding(_laser, _enemies[i]._enemyNameTextField, _animation))
{
_enemies[i].kill();
_animation._killedEnemy = true;
}
}
Note that the above code is equivalent to:
if(CollisionEngine.isColliding(_laser, _enemies[i], _animation) && !CollisionEngine.isColliding(_laser, _enemies[i]._enemyNameTextField, _animation))
{
_enemies[i].kill();
_animation._killedEnemy = true;
}
Alternatively, you can explicitly define a hitArea on all of your enemies so that collisions with the text field aren't considered collisions.
in your enemy have a method like:
public function getCollision():Sprite{
//Where this.body is the main animation for the clip/Hittest area
return this.body
}
or something that returns just the active hit area, then you can run:
if(CollisionEngine.isColliding(_laser, _enemies[i].getCollision(), _animation))