AS3 - How to put all instances in one code for hit testing? - actionscript-3

I have 2.5D game so I cannot put all collision objects in a movieclip container, because I need to keep them as separate display objects. I have multiple instances in the stage. I wouldn't want to go write all the hitTest code for all the objects:
if (player.hitTestObject(object1)
if (player.hitTestObject(object2)
if (player.hitTestObject(object2)... etc
So I would like to know how to hitTest all these instances in one code. I have them added on the stage with instance names, so they are not variables and not added by using the addChild code.

There are plenty of ways to make it less tedious. I'll show a few:
Make a container. You could make a container movie clip and put all the objects in that. Those objects are still individual objects after that (as per your reason in your question for not wanting to go this route). Then you can iterate over all the children of that movie clip:
var i:int = container.numChildren;
while(i--){
if(player.hitTestObject(container.getChildAt(i) as DisplayObject)){
//hit, do something
}
}
Put all the objects in an array, then iterate over that array:
//when you app starts:
var objectArray:Array = [object1,object2,object3]//etc.
//OR, if you have say object1 - object20, you could do something like this:
//vector is basically the same as an array except every item has to be of the same type (on inherit from it)
var objectArray:Vector.<DisplayObject> = new Vector.<DisplayObject>();
for(var i:int=1;i<=20;i++){
var obj:DisplayObject = this.getChildByName("object" + i) as DisplayObject;
if(obj) objectArray.push(obj);
}
//THEN, later, when you do your hit test:
var i:int = objectArray.length;
while(i--){
if(player.hitTestObject(objectArray[i])){
}
}

Related

AS3 I can't get removeChild to delete my items when they are listed in Array

using ActionScript 3 on Animate, I'm trying to delete a bunch of items from the stage using Array and for loop. I actually downloaded this code from this site, but it doesn't seem to work for me. It will only delete one item and won't delete the others. and when I redraw the stage it will then not delete anything at all. I have another function button down the road that will restart (redraw) the game, I'm using the gotoAndPlay() to redraw. FYI, the "squares" are sprites and the "myTFs" are text fields that are 'paired' together to become buttons. What am I doing wrong?
function mainFunc(): void {
var btnsArray: Array = new Array("square", "myTF3", "square2", "myTF2", "square4", "myTF4");
for (var ii = 0; ii < btnsArray.length; ii++) {
removeChildAt(btnsArray[ii]);
btnsArray.length = 0;
}
}
If you have an Array of DisplayObject's names you want to batch operate (e.g. remove from display list or something else) you can do the following:
var A:Array = ["square", "myTF3", "square2", "myTF2", "square4", "myTF4"];
// Iterate over items of the Array.
for each (var aName:String in A)
{
// Obtain a reference to the object by its instance name.
var aChild:DisplayObject = getChildByName(aName);
// Check if it is a valid instance before removing to avoid errors.
if (aChild)
{
removeChild(aChild);
}
}

AS3: how do i stop two of the same function from playing at once?

i am using AS3 to create a function that will automatically play a movieclip all the way through and then remove it. my project is going to have a lot of animated cutscenes, so id like to be able to call this function, use the cutscene id like as a parameter, and then move on to the next. the problem is, im trying to use the function multiple times in a row to play clips sequentially, but they're all playing at the same time. is there a fix, or a better way to do this altogether?
playClip(new a_walk); //find a way to make these stop playing at the same time
playClip(new a_door);
//a_walk and a_door are the AS linkage class names for the movieclips im referring to
function playClip (clip:MovieClip):void {
addChildAt(clip, 0);
clip.mask = myMask;
clip.x=412.4;
clip.y=244.5;
clip.addEventListener(Event.ENTER_FRAME, checkframes);
function checkframes(event:Event) {
if (clip.currentFrame == clip.totalFrames) {
//trace("wow! youre an idiot!");
if (clip.parent) {
clip.parent.removeChild(clip);
trace (100);
return;
}
}
}
}
Sounds like you want a mechanism to play a queue of MovieClips? If so, here is a way you can accomplish this:
//create an array with the clips you want to play (in order), in my example here, the items can be a MovieClip derived Class, or a MovieClip instance
var playQueue:Array = [a_walk, a_door];
//create a var to store the currently playing clip
var currentClip:MovieClip;
playNext(); //call this when you want the queue of clips to start playing
function playNext():void {
//if there was an item previously playing (currentClip has a value), stop it and remove it/dispose of it
if(currentClip){
currentClip.stop(); //stop it from playing
currentClip.addFrameScript(currentClip.totalFrames-1, null); //remove the frame script that was added
currentClip.parent.removeChild(currentClip); //remove it from the display
currentClip = null;
}
//check if there's anything left to play
if(playQueue.length < 1) return;
var nextItem:* = playQueue.shift(); //shift retrieves and removes the first item in the array;
if(nextItem is Class){
//if it's a class, instantiate it
currentClip = new nextItem();
}else{
currentClip = MovieClip(nextItem);
}
//initialize the movie clip
addChildAt(currentClip, 0);
currentClip.gotoAndPlay(1);
//this is just what you were doing before:
currentClip.mask = myMask;
currentClip.x=412.4;
currentClip.y=244.5;
//add a command on the last frame of the movie clip to play the next item in the queue
currentClip.addFrameScript(currentClip.totalFrames-1, playNext);
//addFrameScript is 0 based, so 0 would refer to the first frame. This is why we subtract 1 to get the last frame
}
I should note, that addFrameScript is an undocumented function. It serves as a nice shortcut so you don't have to have an ENTER_FRAME listener checking currentFrame vs. totalFrames. Being undocumented however, one can not count on it's continued existence in future versions of the Flash/AIR runtimes (though it's been around for a long long time)
note
This answer is a work in progress. I'm waiting on a response from the OP.
// playClip(new a_door); don't call this yet, or they will just both play.
var clipData:CustomClass = new CustomClass(); // add an instance of a custom class to hold the value of the movie
//clip being played (so that you can use this value later in the event handler.)
// it will also hold a value of the next clip
clipData._currentClip = a_walk;
clipData._nextClip = a_door;
playClip(new a_walk);
function playClip (clip:MovieClip):void {
addChildAt(clip, 0);
clip.mask = myMask;
clip.x=412.4;
clip.y=244.5;
clip.addEventListener(Event.ENTER_FRAME, checkframes);
}
function checkframes(event:Event) {
if (clipData._currentClip.currentFrame == clipData._currentClip.totalFrames) {
//trace("wow! youre an idiot!");
if (clipData._currentClip.parent) {
playClip(clipData._nextClip);
clipData._currentClip.parent.removeChild(clipData._currentClip);
clipData._currentClip = clipData._nextClip; // moves the clips down
//clipData._nextClip = here we have
//a problem. Do these clips play in a set
//order, first to last? Or do the play out of
//order jumping back and forth? If so, how
//are you planning on controlling which clip
//plays next?
trace (100);
return;
}
}
}
I haven't checked this in Flash yet to see if it works, but I noticed that you are defining a function inside another function, which I don't think is good practice, so this might clean things up for you. Give it a try and let us know.
I'll try to fix my code above when I get a chance. In the meantime, you answered my question about playing the clips in order, so a simple solution would be to put all the clips in an array and then play them by playClip(clipArray[i]) and then when the clip ends and gets removed, do i++ and call the same function playClip(clipArray[i]) which will play the next clip in the array.

Adding one sprite multiple times

I'm using a fairly standard piece of code to add a movieclip to the stage using a library link. It's fairly standard code:
var Beat:beat = new beat();
The trouble is, however that it only adds once, where I need it to add multiple times. How would I go about adding many seperate instances of the 'beat' movieclip to the stage, without making more of them/ more variables.
create them in the loop
var _nHowMany:int = 10;
for(var i:int = 0; i < _nHowMany; i++)
{
addChild(new beat() as DisplayObject);
}
also you can store the reference to them if you need to use it later in a list e.g. Vector.<beat> but if not needed then simply create and add to stage (or other container).
best regards
You can't have multiple instances of a movieclip on the stage without declaring multiple instances in your code, you can use a for loop and store all the movie clips in a single array though:
var numOfClips:Number = 5;
var mcArray:Array = new Array();
for(var i=0; i<numOfClips; i++)
{
var newMC:beat = new beat();
addChild(newMC);
mcArray.push(newMC);
}
Using the above code you end up with a single array to access all 5 movie clips (cleaner than having 5 completely seperate objects like beat1, beat2, beat3).
More info on arrays: http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/Array.html

I can't seem to access automatically named objects (instance##) placed on the stage in AS3, am I missing something?

I have a movieclip in the library that is added to the stage dynamically in the document class's actionscript. This movieclip contains many many child images that were imported directly from photoshop at their original positions (which must be preserved).
I do not want to manually name every single image instance, as there are dozens upon dozens.
I have already gone through and manually converted the images to symbols, as apparently flash won't recognize the "bitmap" objects as children of a parent movieclip in AS3 (numChildren doesn't see the bitmaps, but it sees the symbols).
I have an array filled with references to the dozens of children, and I loop through it, checking if each one is under the mouse when clicked. However, somehow, it is not detecting when I click over the items unless I manually name the child symbols (I tested by manually naming a few of them -- those ones became click-sensitive.)
I have already done trace() debugging all throughout the code, verifying that my array is full of data, that the data is, in fact, the names of the instances (automatically named, IE instance45, instance46, instance47, etc.), verifying that the function is running on click, verifying that the code works properly if I manually name the symbols.
Can any one see what's going wrong, or what aspect of flash I am failing to understand?
Here is the code:
//check each animal to see if it was clicked on
private function check_animal_hits():void
{
var i:int = 0;
var animal:Object = this.animal_container;
for (i=0; i<animal.mussels.length; i++)
{
if (this.instance_under_cursor(animal.mussels[i].name))
{
var animal_data = new Object();
animal_data.animal = "mussel";
this.send_data(animal_data);
}
}
}
Here is the code for the instance_under_cursor() method:
// Used for finding out if a certain instance is underneath the cursor the instance name is a string
private function instance_under_cursor(instance_name)
{
var i:Number;
var pt:Point = new Point(mouseX,mouseY);
var objects:Array = stage.getObjectsUnderPoint(pt);
var buttons:Array = new Array ;
var o:DisplayObject;
var myMovieClip:MovieClip;
// add items under mouseclick to an array
for (i = 0; i < objects.length; i++)
{
o = objects[i];
while (! o.parent is MovieClip)
{
o = o.parent;
}
myMovieClip = o.parent as MovieClip;
buttons.push(myMovieClip.name);
}
if (buttons.indexOf(instance_name) >= 0)
{
return true;
}
return false;
}
Update:
I believe I have narrowed it down to a problem with getObjectsUnderPoint() not detecting the objects unless they are named manually.
That is the most bizarre way to find objects under mouse pointer... There is a built-in function that does exactly that. But, that aside, you shouldn't probably rely on instance names as they are irrelevant / can be changed / kept solely for historical reasons. The code that makes use of this property is a subject to refactoring.
However, what you have observed might be this: when you put images on the scene in Flash CS, Flash will try to optimize it by reducing them all to a shape with a bitmap fill. Once you convert them to symbols, it won't be able to do it (as it assumes you want to use them later), but it will create Bitmpas instead - Bitmap is not an interactive object - i.e. it doesn't register mouse events - no point in adding it into what's returned from getObjectsUnderPoint(). Obviously, what you want to do, is to make them something interactive - like Sprite for example. Thus, your testing for parent being a MovieClip misses the point - as the parent needs not be MovieClip (could be Sprite or SimpleButton or Loader).
But, if you could explain what did you need the instance_under_cursor function for, there may be a better way to do what it was meant to do.

AS3 Targeting Unnamed Timeline Instances

Is there any way to control nested MovieClip instances placed on the timeline without naming them through the Properties panel? Are there any unique identifiers I can refer to?
Using instance names (like instance33, instance12, etc.) doesn't help since names are just properties, but how about references like sprite187_27 (as returned by trace(this) inside a MovieClip on the TimeLine)?
(Using ActionScript3, Automatically declare stage instances disabled)
If [object sprite187_27] is enough in identification, you can just loop through all the objects in your root timeline, like this:
var l:int = this.stage.numChildren;
for (var i:int = 0; i < l; i++)
{
var instance:DisplayObject = this.getChildAt(i);
trace(instance.toString()); // will output [object _whatever_]
}
Looping recursively just means checking if instance.numChildren > 0.