As3 - Function with unlimited arguments as Movieclip? - actionscript-3

I want to pass to a function an uncertain number of movieclips, sth like this
function Test(...args)
{
for(var item in args){
item.x = 100;
}
}
But using this method didn't work, any solution?

You're almost there, you just need to use a for each loop for your example to work correctly:
function Test(...args)
{
for each(var item:MovieClip in args)
// ^^^^
{
item.x = 100;
}
}
Better however would be to accept an Array or Vector holding the MovieClips. This will greatly improve readability of your code later on:
function Test(list:Vector.<MovieClip>)
{
for each(var item:MovieClip in list)
{
item.x = 100;
}
}

Use arguments; see the Adobe Reference docs (for AS3), the MDN (for JS) or this example jsfiddle (for a working example).
[NB: question originally tagged as JS, leaving javascript/jsfiddle in there for reference]

Related

insertAt and removeAt vector methods missing from cs4 but present in documentation

I seem to be having some trouble with the vector methods removeAt and insertAt, they are both present in the documentation for as3:
"https://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/Vector.html#insertAt()"
And yet in adobe flash cs4 they are undefined and throw up errors. Does cs4 not have these methods or is the documentation poor?
Well, there's an option other then upgrading to newest version of Flash Player and SDK. It is not a better option, not very elegant, yet simpler. Unless you are running some huge datasets, performance of the VPlus.insertAt(...) implementation is not an issue too.
Implementation:
package
{
public class VPlus
{
static public function deleteAt(V:*, index:int):void
{
// The method cuts a portion of Vector and returns it.
// We need just the "cut" part, yet it suffices.
V.splice(index, 1);
}
static public function insertAt(V:*, index:int, element:*):void
{
// Fix for from-the-end-and-backwards indexing.
if (index < 0)
{
index = V.length - index;
}
// Add one more element to the end of the given Vector.
V.push(null);
// Shift the Vector's elements from index and on.
for (var i:int = V.length - 2; i >= index; i--)
{
V[i+1] = V[i];
}
// Put the new element to its designated place.
V[index] = element;
}
}
}
Usage:
// myVec.deleteAt(10);
VPlus.deleteAt(myVec, 10);
// myVec.insertAt(15, "Hello World!");
VPlus.insertAt(myVec, 15, "Hello World!");

as3 - Detect if object is moved to another object using timer

I was creating a shooting game that the target is appear and disappear any seconds. I don't know how use the codes and I don't know how to detect the object to object when using if statement.
Here's my codes:
import flash.utils.Timer;
var dummySX: Number = dummyS.x;
var dummySY: Number = dummyS.y;
var targetTimeStart: Timer = new Timer(1000);
targetTimeStart.start();
targetTimeStart.addEventListener(TimerEvent.TIMER, targetTimeStartNow);
function targetTimeStartNow(e: TimerEvent): void {
target.x = dummySX;
target.y = dummySY;
targetTimeStart.stop();
}
function detect(): void {
if ((target.x == dummySX) && (target.y == dummySY)) {
trace("DETECTED");
}
}
or
function detect(): void {
if (target.hitTestObject(dummyS)==true) {
trace("DETECTED");
}
}
Thanks!!
There are several ways to detect hit between objects.
Let's say you have 2 display objects (do1 and do2)
First of all there is the do1.hitTestObject(do2). This method is very fast and works well for rectangular objects. You can get some visually "false" results if the rectangular objects are rotated or you use it with other than rectangular objects.
Next there is the do1.hitTestPoint(do2.x, do2.y, shapeFlag). This method is slower but more accurate, especially when you set the shapeFlag to true. It tests the hit against a single point (x & y coordinates).
There is a third method (hitTest) that implies you use BitmapData objects and that is even more precise and is specific the the BitmapData class, but much harder to process (being able to ignore transparent pixels).
A fourth option would be to do a custom implementation of the hit detection, but it seems a bit off to detail that here.
Going over your code, I see you create a 1 second Timer object and start it, after which you add a listener with the targetTimeStartNow function. In that function you move the target object to the same position of the dummyS object and after that you stop the targetTimeStart Timer object. That means that you have only 1 entry in that function.
The detect function seems that is never called from your code, although both calls should trace DETECTED.
The first detect function that you've written mimics the hitTestPoint version.
function detect(): void {
if ((target.x == dummySX) && (target.y == dummySY)) {
trace("DETECTED");
}
}
This is the same with:
function detect():void {
if(target.hitTestPoint(dummySX, dummySY, false)) {
trace("DETECTED");
}
}
It'd be better to use this variant (in order to use the shape of the object, and not their bounding box):
function detect():void {
if(target.hitTestPoint(dummySX, dummySY, true)) {
trace("DETECTED");
}
}
The second detect function uses the hitTestObject and it's ok. You could use it without explicitly == true:
function detect(): void {
if(target.hitTestObject(dummyS)) {
trace("DETECTED");
}
}
The only thing is to call it in code in order to get it executed. So somewhere after the target object was moved just add:
detect();
Depending on your needs, you could add it to the targetTimeStartNow method (or add an EnterFrame listener, or create another Timer object):
function targetTimeStartNow(e: TimerEvent): void {
target.x = dummySX;
target.y = dummySY;
targetTimeStart.stop();
detect();
}

Clearing tiles in tile based as3 game

I been searching for a way to handle this for hours but have found nothing. In my BuildMap function, I instantiate new MovieClips (Tile) with the instance name cell. Their frame is based on my 2d array (protoMap). The problem is that the cells are instantiated in the function. I don't know how to access them outside of it. I want to removeChild(cell) but the only way I know how is within the function that it's instantiated in.
public function BuildMap()
{
for (var i:int=0; i < mapHeight; i++)
{
for (var u:int=0; u < mapWidth; u++)
{
var cell:MovieClip = new Tile();
cell.gotoAndStop(protoMap[i][u]+1);
cell.x = tileSide*u;
cell.y = tileSide*i;
addChild(cell);
var currCellLabel:String = cell.currentFrameLabel;
mapLabels[i].push(currCellLabel);
}
}
}
I want a function called ClearMap() that loops through again and does removeChild(cell). I thought about doing a clearTiles:Boolean and in BuildMap() do
if(clearTiles == false)
{
build the map;
}else{loop again and removeChild(cell)}
but that didn't work... so then I tried to pass cell as an argument to BuildMap() but when I tried to remove it, it wasn't an object of the caller... or something like that. I was also thinking to put cell into its own array, but I don't want to waste memory unnecessarily. Any solutions for the noob?
Create a class instead of using functions.
The point of a class is to isolate concerns.
In your case, you want to deal with all those tiles. (create them according to protoMap and be able to delete them all.
Extending a DisplayObjectContainer such as Sprite, will allow you to add all the tiles to the map object, then you can add and remove the map easily.
Your BuildMap function becomes the constructor.
Here's some non working pseudo code that illustrates the idea:
package
{
public class Map extends Sprite
{
public function Map (data, width, height)
{
for (var i:int=0; i < height; i++)
{
for (var u:int=0; u < width; u++)
{
var cell:MovieClip = new Tile();
cell.gotoAndStop(data[i][u]+1);
cell.x = tileSide*u;
cell.y = tileSide*i;
addChild(cell);
var currCellLabel:String = cell.currentFrameLabel;
mapLabels[i].push(currCellLabel);
}
}
}
public function clear():void
{
removeChildren();
}
}
}
The whole map is contained in that class. You'd have to add the labels, but you didn't specify what they are or what they do, so I left them out.
You can use it like so:
var awesomeMap:Map = new Map(protoMap, mapWidth, mapHeight);
addChild(awesomeMap);
//later
awesomeMap.clear();
//or
removeChild(awesomeMap);
I'd like to point out that building tile maps with MovieClips this way is a bad idea. MovieClips are horrible for performance, because they carry the overhead of their timeline.
Removing all children is very wasteful, too.
So if you have performance problems, try reusing objects and/or employing a technique called "blitting"
See this article: http://www.adobe.com/devnet/flash/articles/blitting_mc.html

How do you detect collisions between two arrays?

For future refrence this was the final code
for each (var bullet:Bullet in bulletList)
{
for each (var zombie:Zombie in zombieList)
{
if (zombie.hitTestObject(bullet))
{
stage.removeChild(bullet);
zombie.zombieShot(50);
}
}
}
Original Question Below
for each (var bullet:Bullet in bulletList)
{
if (zombieList.length > 0)
{
if (zombieList[h].hitTestObject(bullet))
{
bulletList[i].deleteBullet();
zombieList[h].zombieShot(50);
}
}
}
This is the code I have but it only detects the first zombie I spawn in, any help is appreciated.
if (countMePls<10)
{
countMePls++;
var zombie:Zombie = new Zombie(stage,Math.random() * stage.width,Math.random()*stage.height);
zombie.addEventListener(Event.REMOVED_FROM_STAGE,zombieRemoved,false,0,true);
zombieList.push(zombie);
stage.addChild(zombie);
}
and then...
function shootBullet():void
{
var bullet:Bullet = new Bullet(stage,Main.player.x,Main.player.y,Main.player.rotation - 90);
bullet.addEventListener(Event.REMOVED_FROM_STAGE,bulletRemoved,false,0,true);
bulletList.push(bullet);
stage.addChildAt(bullet,1);
}
this last bit is in Bullet.as
public function deleteBullet():void
{
this.parent.removeChild(this)
}
I think your issue comes from some basic confusion about for and for each. With for each you have no index variable, each iteration yields a new instance of the type in the collection which is referred to by the name you declare in the loop. In this case that is;
foreach (var bullet in bulletsList)
{
// do something with bullet
}
You probably actually want a nested loop, something that checks every bullet against each zombie to see if there was a hit, that would actually look like this;
foreach (var bullt in bulletsList)
{
foreach (var zombie in zombiesList)
{
if (zombie.hitTestObject(bullet))
{
bulletList.Remove(bullet);
zombie.zombieShot(50);
}
}
}
In your code you have the foreach loop giving you the current bullet object but then you never reference it within the loop, that doesn't make sense. This may not be exactly what you want but hopefully it will get you moving in the right direction. If you want to use those indexers then you need something like;
for (int i = 0; i < bulletsList.Length; i++)
{
for (int h = 0; h < zombiesList.Length; h++)
{
// do stuff with bulletsList[i] and zombiesList[h]
}
}
Note: this was originally tagged as C# and the code I've posted uses C# syntax. The explanation I've provided most likely applies either way, the OP's code doesn't really makes sense in any language I know of and the reasons are the same.

Creating anonymous functions in loop with not the same arguments

I want to make in loop set of buttons, and add to them some events, but anonymous functions is the same. I write example code:
for(var i:int=0;i<5;i++)
{
var button:SimpleButton = new SimpleButton(...);
...
button.addEventListener(MouseEvent.CLICK, function(event:MouseEvent):void
{
trace(i);
});
}
...
And I want to trace 0,1,2,3.. from click buttons instead of 4,4,4,4 ..
Do you know how can I make this ?
The problem you are running into is that ActionScript does not support closures.
In other words, the variable i does not get copied into it's own context per function. All functions refer to the same instance of i.
More information here:
http://flex.sys-con.com/node/309329
In order to do this, you need a function that generates a function:
public function makeFunction(i:int):Function {
return function(event:MouseEvent):void { trace(i); }
}
Now, you create new instances of the function with their own context:
button.addEventListener(MouseEvent.CLICK, makeFunction(i));