object.hitTestObject(other_object) is executed too many times - actionscript-3

Why is that happening?
when two objects are touching each other, then i put trace, and they show me many messages:
private function onEnterFrame(e:Event):void{
e.target.x += 4;
var moved = false
if(!moved)
{
for(var i = 0; i < arrows.length; i++)
{
if(e.target.hitTestObject(arrows[i]))
{
trace('boom');
moved = true;
break;
}
}
}
so here it writes the word "boom" to many times instead of 1 time, how to fix this?

You are checking hit test on every frame, so everytime it gives you the value of hit test. I think your problem is that you define your boolean variable "moved" inside the loop and it loses it's purpose because it's always false when it comes into loop. Try defining your boolean variable outside the function. That will help solve the problem :)

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.

Actionscript 3.0 Splice/RemoveChild issue

I have rocks and enemy ninjas moving off the screen. The ninjas are supposed to disappear after being hit by a shuriken once, however they are taking two hits to disappear, although the shurikens disappear every time fine. The code is nearly identical for the shurikens and enemy ninjas, however the ninjas don't seem to work properly. I also get the occasional ninja getting stuck somewhere on the screen and shurikens pass through it.
//If a rock moves off the screen, it is spliced.
if (rock[j].x <= -301){
removeChild(rock[j]);
rock.splice(j, 1);
rockNum--;
}
}
for (var q=shurikens.length - 1; q >= 0; q--){
for (var w=enemy.length - 1; w >= 0; w--){
//If the shurikens hit the ninjas.
if ((shurikens[q]).hitTestObject(enemy[w])){
removeChild(enemy[w]);
enemy.splice(w, 1);
ninjaNum--;
removeChild(shurikens[q]);
shurikens.splice(q, 1);
break;
}
}
}
}
How to fix your code, and give you some performance tips:
This code is very "bug friendly": you are modifying the "length" property of array within a for/in loop that relied on the same property, this is really not a wise thing to do.
The way I would do it:
// Create an array where to store colliding ninjas and shurikens
var depleted:Array = [];
// Create local references to make code more readable and fast (querying arrays all the time is slow).
var ninja:DisplayObject;
var shuriken:DisplayObject;
// Loop in shurikens (no need to check if shurikens have been depleted, since you query only once each of them
for (var q:int=shurikens.length - 1; q >= 0; q--){
shuriken = shurikens[q]; // Assign a shuriken to our variable so you avoid to call further shurikens[q];
for (var w:int=enemy.length - 1; w >= 0; w--){ // Loop in ninjas
ninja = enemy[w]; // Assign ninja
//If the shurikens hit the ninjas. (only if ninjas have not yet been removed)
// This is the core of our improvement, before calling hitTest that is slow, you first check that ninjas have not already been killed
if (depleted.indexOf(ninja) == -1 &&
shuriken.hitTestObject(ninja)){
// It's a hit with a live ninja. I just add both objects to depleted list.
depleted.push(ninja);
depleted.push(shuriken);
break; // Breaking the loop, makes sure shuriken cannot hit 2 ninjas
}
}
}
// Then, you loop in the list of killed ninjas and depleted shurikens, and remove them from arrays and display list
for each (var depletedObj:DisplayObject in depleted) {
// First remove object from the relevant array
if (shurikens.indexOf(depletedObj) != -1) shurikens.splice(shurikens.indexOf(depletedObj), 1); // If it was in the shurikens array remove from there
else if (enemy.indexOf(depletedObj) != -1) enemy.splice(enemy.indexOf(depletedObj), 1); // If it was in the ninjas array remove from there
// The do all necessary stuff to remove object from DisplayList (end eventually add it to an object pooling list)
removeChild(depletedObj);
}
ninjaNum = enemy.length; // Update number of ninjas
Another hint, in case you run this for loops on each frame, if you place ninjas and shurikens in 2 different DisplayObjectContainer, you can first hitTest the 2 large containers, and once they collide, you can run the loops to check fine collisions. Also, in numeric for/in loops, declare variables always as :int. Typing variable makes it faster to access than an untyped variable. You can of course improve this code to make it faster, i.e.: adding a "alive = true" property to shurikens and ninjas, so you do not need to query a third array, etc.

How to add a movieclip to the stage then make it move down?

I am making a game where i want to spawn zombies then make them move down the screen. I also want to have multiple on the screen at once. I have tried multiple ways now but none of them have worked.
Here is my code
if ((zombie == 1)||(zombie == 3)||(zombie == 5)||(zombie == 7))
{
var Z = new Z;
Z.x = 403.25;
Z.y = -86.9;
Z.rotation = 90;
addChild(Z)
zombie += 1;
}
//Functions
function startzombie(event)
{
trace("start zombies")
zombie = 1;
addEventListener(Event.ENTER_FRAME,zombiemove)
}
function zombiemove(event:Event)
{
Z.y += 1;
}
Z is the zombie
You need an Array that will hold references to your zombie instances, instead of a single variable of class Zombie, whatever it is name. Then, in the part of code that moves zombies, you iterate through that array and advance all zombies in there. Also you need to check if some zombies are already defunct so that you need to remove them from screen and from array, and act accordingly.
var za:Array; // initialize elsewhere
....
function addZombie():void {
var z:Zombie=new Zombie();
z.x=Math.random()*600; // position that zombie
z.y=-20; // somewhere
addChild(z);
za.push(z);
}
function advanceZombies():void {
for (var i:int=za.length-1;i>=0;i--) za[i].walkABit();
}
You call a method in Zombie class specially designed for moving the zombie. They can be say stunned or slowed so that this move might not occur, and it's of no deal to your main class to control each and every zombie's alterable behavior, this is what's named "black box concept". You say "move", they move if they are able.

ActionScript 3.0 HitTestObject on an Array

Good afternoon everyone!
I have a small problem in ActionScript 3.0 with hitTestObject.
I would like to chechk if my character hits a platform (I'm making a simple platform game.).
I have a platform object exproted for action script , and i add childs frim this to an array.
Until this point everything goes well, i can put them on stage etc.
I have written a cycle to chechk if my caharacter hits the platform but it doesn't works correctly. My character falls throught the first platforms and only stop's falling when it hits the last platform. (So for the last in the array it works well.)
And now here is this part from my code, i hope someone can help me with it. :)
import flash.events.Event;
import flash.geom.Rectangle;
stop();
var vy:Number=0;
var gv:Number=1;
var sebesseg:Number=4;
var jumped:Boolean=false;
var stay:Boolean=false;
var level:Array=new Array ;
var gravity:Number=2;
var velocity:Number=1.1;
var platform0:MovieClip=new platform ;
level.push(addChild(platform0));
level[0].x=200;
level[0].y=450;
var platform1:MovieClip=new platform ;
level.push(addChild(platform1));
level[1].x=700;
level[1].y=650;
var platform2:MovieClip=new platform ;
level.push(addChild(platform2));
level[2].x=1000;
level[2].y=800;
stage.addEventListener(Event.ENTER_FRAME, cameraFollowCharacter);
function cameraFollowCharacter(evt:Event) {
root.scrollRect=new Rectangle(PORK1_mc.x-(stage.stageWidth/2)+320,PORK1_mc.y-(stage.stageHeight/2)-50,stage.stageWidth,stage.stageHeight);
}
stage.addEventListener(KeyboardEvent.KEY_DOWN,gomb);
function gomb(k:KeyboardEvent):void {
trace(k);
if (k.keyCode==37) {
if (sebesseg==2) {
sebesseg=sebesseg+0;
} else {
sebesseg-=1;
}
} else if (k.keyCode==39) {
sebesseg+=1;
} else if (k.keyCode==Keyboard.ESCAPE) {
stop();
root.scrollRect=new Rectangle (stage.x,stage.y,stage.stageWidth,stage.stageHeight);
gotoAndStop(2);
} else if (k.keyCode==Keyboard.F1 && stay==false) {
stage.frameRate=0;
stay=true;
} else if (k.keyCode==Keyboard.F1 && stay==true) {
stage.frameRate=24;
stay=false;
}
}
stage.addEventListener(Event.ENTER_FRAME, megy);
function megy(e:Event):void {
PORK1_mc.x+=sebesseg;
gravity*=velocity;
PORK1_mc.y+=gravity;
//trace(velocity);
}
THIS PART IS THE PROBLEM, HITTESOBJECT ONLY WORKS FOR THE LAST PLATFORM
PORK1_mc.hitPork_mc this is my character (hitPork_mc is an invisible rectangle for better HitTestObject. So as i wrote my char. falls throught the platfroms until the last one, he falls on tha last and stops falling, so for the last platform in the array it works perfectly.
I wouldn't like to change a lot on my code, only on the hitTest part if its possible.
this.addEventListener(Event.ENTER_FRAME, handleEnterFrame);
function handleEnterFrame(e:Event):void {
for (var i:int = 0; i < level.length; i++) {
if (level[i].hitTestObject(PORK1_mc.hitPork_mc)) {
velocity=0;
} else {
velocity=1.1;
gravity=4;
}
}
}
Thanks for every help in advance!
With your original code, you do the following for each platform:
If there is a collision, set velocity to zero.
If not, set velocity to 1.1.
Since you do this in order, here's what happens when you collide with platform 1 but not 2 or 3:
The collision with 1 sets your velocity to zero.
The collision with 2 sets your velocity to 1.1.
The collision with 3 sets your velocity to 1.1.
Since these happen all in a row in the same frame, the end result is that your character never stops unless the collision is with platform 3.
By returning after setting velocity to zero, you are breaking out of the loop, preventing your collision work from being undone. What you are doing will work, but you can also do it another way which is less concise but may make more sense:
function handleEnterFrame(e:Event):void
{
var collided:Boolean = false; //This will record if you collided or not.
for (var i:int = 0; i < level.length; i++)
{
if (level[i].hitTestObject(PORK1_mc.hitPork_mc))
{
collided = true;
}
}
if(collided) velocity = 0;
else
{
velocity=1.1;
gravity=4;
}
}
Note that here we're not applying velocity and gravity inside the for loop because we don't want it to happen for each object we might collide with. Instead, we set it once, after we've determined if we collide with any objects.
This also helps show that you are setting gravity every frame you don't collide even though there doesn't seem to be any point at which you set it to zero; I suspect this is something you want to fix.

Detect position?

I'm trying to create a simple game with a boat moving between left and right by the keys. The moving is OK, but when I try to detect the left och right end it doesn't work at all. Below is a part of the code. What could be wrong?
stage.addEventListener(Event.ENTER_FRAME,moveBoat);
function moveBoat(event:Event):void {
if(! boat.x >= 700){
if(moveLeft) {
boat.x -= 5;
boat.scaleX = 1;
}
if (moveRight) {
boat.x += 5;
boat.scaleX = -1;
}
}
}
If you've solved your collisions problem, here's an answer for your dropping bombs problem. Doing it by having 5 boolean variables would be a rather unrefined way of doing it; instead simply use an integer to record how many bombs your boat has left to drop, and each time it drops one, reduce this value by 1. Here's some example code:
//Create a variable to hold the number of bombs left.
var bombsLeft:int = 5;
//Create an event listener to listen for mouse clicks; upon a click, we'll drop a bomb.
addEventListener(MouseEvent.CLICK, dropBomb);
//The function dropBomb:
function dropBomb(event:MouseEvent):void
{
if (bombsLeft > 0)
{
//Create a new instance of the Bomb class; this could be an object in your Library (if you're using the Flash IDE), which has a graphic inside it of a bomb.
var newBomb:Bomb = new Bomb();
//Position the bomb.
newBomb.x = boat.x;
newBomb.y = boat.y;
//Add it to the stage
addChild(newBomb);
//Reduce the number of bombs you have left.
bombsLeft--;
}
//At this point you could check if bombsLeft is equal to zero, and maybe increase it again to some other value.
}
This doesn't include code to then move the bomb downwards, but you can do that fairly simply using an update loop. If you're struggling to do that, let me know and I'll give you another example.
Hope that helps.
debu