Swapping specific movieclips after event? - actionscript-3

I've created a drag and drop "puzzle" that has 16 unique pieces, each with their own instance name.
The problem I have is that there are 4 target zones made up of arrays (4 pieces per zone, but the order of the pieces in the zone does not matter). When a piece gets dropped into its correct zone, I would like to "switch" it with another movieclip and have that new movieclip be in the drop target area.
So, for each of the 16 puzzle pieces, I also have 16 unique companion pieces that need to somehow be paired up so that when the visible puzzle piece gets dropped, it is both removed from view, but also replaced with its companion piece.
Any ideas on how to do this?

MovieClip is a dynamic class, meaning that you can add properties to it at runtime. Leveraging this, you can assign a property to your original pieces and call it something like pairedPiece. In this property, you'll store the appropriate value (the name of its pair in the library).
var firstMovieClip:MovieClip;
// do whatever you need to set up your firstMovieClip, attach listeners, etc
firstMovieClip.pairedPiece = "SecondMovieClip";
// the following will occur when the piece is dropped and you need to swap it
var secondMovieClip:MovieClip = new (getDefinitionByName(firstMovieClip.pairedPiece) as Class)() as MovieClip;
secondMovieClip.x = firstMovieClip.x;
secondMovieClip.y = firstMovieClip.y;
firtMovieClip.parent.addChildAt(secondMovieClip, firstMovieClip.parent.getChildIndex(firstMovieClip));
firstMovieClip.parent.removeChild(firstMovieClip);

Related

AS3: how to pass by "object"

I was actually looking for a way to pass by reference in AS3 but then it seemed that adobe and lots of people's understanding of pass by reference is different from what I have been taught at the university. I was taught java was pass by value and C++ allowed pass by reference.
I'm not trying to argue what pass by value and reference are. I just want to explain why I'm using pass by object in the question...
Back to the question, I would like to do something like:
public function swapCard(cardA:Cards, cardB:Cards) {
var temp:Cards = cardA;
cardA = cardB;
cardB = temp;
}
...
swapCard(c1, c2);
EDIT: adding two examples on how I'm using the swapCard function
1) in the process of swaping a card between player1 and player2's hand
swapCard(player1.hand[s], player2.hand[t]);
2) in the process of swaping a card between player1's hand and deck
swapCard(player1.hand[s], player1.deck[rand]);
In C++, we only need to add a symbol before the parameters to make it work (and we call THIS pass by reference). But in AS3, cardA and cardB are just pointers to the formal parameters. Here in the function, changing the pointers does not do anything to the formal parameters :(
I have been searching for hours but I couldn't find a way to without knowing all the properties of the Cards.
If I have to change the properties of the cards one by one then maybe I should change swapCard to a static function in class Cards? (because I don't want to expose everything to another class) I'm not sure if this is a good practice either. This is like adding a swap_cars function into class Cars. If I let this happen, what will be next? Wash car, lend car, rent car... I really want to keep the Cards class clean and holds only the details of the card. Is there a possible way to do this properly in AS3?
The kind of swap function that you're trying to implement is not possible in AS3. The input parameters are references to the input objects but the references themselves are passed by value. This means that inside the function you can change the cardA and cardB but those changes will not be visible outside the function.
Edit: I added this portion after you edited your question with sample usage.
It seems like you're trying to swap two objects in 2 different arrays at given array positions in each - you can create a function for this in AS3 but not the way you attempted.
One possible implementation is to pass the arrays themselves and the positions that you're trying to exchange; something like this:
// Assumes arrays and indices are correct.
public function SwapCards(playerHand:Array, playerCardIndex:int,
playerDeck:Array, playerDeckIndex:int):void
{
var tempCard:Card = playerHand[playerHandIndex];
playerHand[playerHandIndex] = playerDeck[playerDeckIndex];
playerDeck[playerDeckIndex] = tempCard;
}
Note that you still exchange references and the arrays themselves are still passed by reference (and the array references are passed by value - you could, if you wanted, change the arrays to new arrays inside this function but you wouldn't see new arrays outside). However, because the array parameters refer to the same arrays inside and outside the function, you can make changes to the contents of the array (or other array properties) and those changes will be visible outside.
This solution is faster than cloning the card because that involves allocating memory for a new Card instance (which is expensive) and that temporary instance will also have to be freed by the garbage collector (which is also expensive).
You mentioned in a comment that you pass cards down to lower levels of code - if you don't have a back reference to the arrays (and the positions of the cards), you will not be able to easily swap cards - in AS3, all input parameters are copies (either the copy of the value for primitive types or the copy of the reference for complex objects - changes to the input parameters in a function will not be visible outside).
EDIT: renaming the function from clone to copyFrom as pointed out by aaron. Seems like clone is supposed to be used as objA = objB.clone()
At this point, I'm adding a copyFrom() function in the Cards class such that
var temp:Cards = new Cards(...);
var a:Cards = new Cards(...);
...
temp.copyFrom(a);
...
temp will be copying everything from a.
public function swapCard(cardA:Cards, cardB:Cards) {
var temp:Cards = new Cards();
temp.copyFrom(cardA);
cardA.copyFrom(cardB);
cardB.copyFrom(temp);
}
I will wait for a week or so to see if there are any other options
You have some good answers already, but based on the comments back-and-forth with me, here's my suggestion (I use "left" and "right" naming because it helps me visualize, but it doesn't matter):
function swapCard(leftCards:Array, leftCard:Card, rightCards:Array, rightCard:Card):void {
var leftIndex:int = leftCards.indexOf(leftCard);
var rightIndex:int = rightCards.indexOf(rightCard);
leftCards[leftIndex] = rightCard;
rightCards[rightIndex] = leftCard;
}
Now you can swap the cards in the two examples you posted like this:
swapCard(player1.hand, player1.hand[s], player2.hand, player2.hand[t]);
swapCard(player1.hand, player1.hand[s], player1.deck, player1.deck[rand]);
However, note that while this swaps the cards in the arrays, it does not swap direct references to the cards in those arrays. In other words:
var a:Card = player1.hand[0];
var b:Card = player2.hand[0];
swapCard(player1.hand, a, player2.hand, b);
// does not change the references a and b, they still refer to the same card
a == player2.hand[0];
a != player1.hand[0];
b == player1.hand[0];
b != player2.hand[0];
Typically, this sort of thing is handled by dispatching a "changed" event so that any code that cares about the state of a player's hand array will know to re-evaluate the state of the hand.
There's a deep misunderstanding going on here. The question is about object reference but the PO is not trying to swap any Object reference at all.
The problem comes from the fact that the PO does not understand the difference between variable and objects. He's trying to swap variable/object reference which is not dynamically possible of course. He wants with a function to make the variable holding a reference to Object A, swap its object reference with another variable. Since Objects can be passed around but not variables (since they are just holders (not pointers)) the task is not possible without a direct use of the given variable.
To resume:
variables are not Objects!
variables hold a reference to an object.
variables cannot be passed in function or referenced in functions because THEY ARE NOT OBJECTS.

method .attachMovie() is no longer supported .Flash to AS3 conversion

I am completing an online tutorial and manipulating it suit my website. I've come across this code...
`// Create a menu item movie clip in the menu_mc instance on the main timeline
// for each item element offsetting each additional further down the screen
var item_mc = menu_mc.attachMovie("movieitem","item"+item_count, item_count);
item_mc._x = item_count * item_spacing;
item_count++;`
The following line gives me a problem (the method is no longer supported)
var item_mc = menu_mc.attachMovie("movieitem","item"+item_count, item_count);
How can i achieve this?
I've tried the following with no joy. message too many arguments?
var mItem:movieitem = new movieitem;
var item_mc = menu_mc.addChild(mItem,mItem+item_count, item_count);
addChild() only accepts 1 argument, which is the display object itself. Also, it looks like you're missing brackets when you create your object and by convention, class names are capitalised.
var mItem:movieitem = new movieitem();
Edit based on my comment
Looking at the documentation for attachMovie() for AS2 (wow, been awhile since I've looked at this), it takes in 3 arguments:
id:String, name:String, depth:Number
Now the id is used to grab a movieclip from the library. This is no longer needed as you've already created a movieclip object from your library in the line before:
var mItem:Movieitem = new Movieitem();
The second argument name is used to create a unique instance name for the created moviclip from the library. You don't really need this. In the line where you create the movieclip (see above), you already have a unique reference you can use to access the movieclip. Interestingly, attachMovie() also returns a reference -I've never ever found a use for the instance names given with the 'name' argument. I just use the reference returned to access it, which you are already doing.
The third argument depth determines which depth the movieclip is placed at. In your case, I am guessing that ' item_count' is just a number that increases, which effectively puts that movie clip at the highest depth when that line is executed. By default, addChild() will automatically do this for you and put the display object (your movieclip) at the highest depth within the parent at the time it is added. So, unless you wanted it at a specific depth/overlapping order, you don't really need to pass this in either. If you did want to add something at a specific depth, look at addChildAt()
Hence as mentioned before, you can just pass in the reference to your movieclip/display object in to addChild().

Having problems with hitTestObject-ing a child

I've just started to learn actionscript for a month or so, and I've been stuck on this part of the code for about 2 days now ><
So basically, I wanted to write a hitTestObject in a movieclip ("fire_mc") to detect if it overlaps the the child I added on stage (enemy)
So here's what I wrote in the main stage...
var newtarget:enemy=new enemy();
stage.addChild(newtarget);
newtarget.x=40;
newtarget.y=30;
and every time I traced the newtarget in the fire_mc, it turns out to be NULL...
So should I be tracing enemy or newtarget? (Cuz I tried both and nothing works).
And how can I hitTestObject in the fire_mc movieclip?
Is it possible for me to hitTestObject newtarget if I were to create multiple newtarget-s?
If not, what should I do?
And can someone tell me the difference between root, and MovieClip(root) (Because sometimes in a movieclip I have to use the former one to work, and sometimes the latter, and I have no idea why cuz I'm all referencing to the same place)
Sorry for these simple and lengthy questions, I'm really just a noob and I tried looking up for answers in the web for hours, but nothing helpful turned up><
Thanks in advance :)
In this statement
var newtarget:enemy=new enemy();
var - keyword used to define varibles, newtarget - variable name in which pointer to new class instance stored, :enemy - data type (the class name), new - keyword used to create new class instances, and finally enemy is class constructor (by the way there is a rule of good manners by which class names are capitalized)
So answer for you question which should you use when you want check is some is overlapping is 'newtarget'.
Now about hit test - all you need do to check if two objects hit each other is correctly use their references from the part of project where your code is writen.
For example if you have your fire_mc on MainTimeline created by IDE, and code of creation of you enemy instance stored in newtarget sameplace, then if you check will be placed in frame of MainTimeline where both object present it will be like this
fire_mc.hitTestObject(newtarget);
or this
newtarget.hitTestObject(fire_mc);
All these statements give you same result - if objects intersect each other you have true returned.
If you created 'newtarget' on MainTimeline and checks will be from fire_mc (which is on MainTimeline to) frame, then code will something like that
this.hitTestObject(MovieClip(root).newtarget);
Now about root. Primarily it is a property of DisplayObject in which reference to the top-most display object in swf-file structure is stored. So as such it is inherited by all child classes. So here's the trick. When you try get your objects in swf structure using root here the differences are based on the fact that root always return object as DisplayObject and that means what if you object contains something that DisplayObject shouldn't you can't get it. So you must convert returned reference to need data-type usign MovieClip(...).

Actionscript 3 - How to include current movieclip/root location to this.currentFrame?

I am trying to make save progress thingie with sharedobjects, I´ve figured out how those work, but now I should just try to figure out how to make my "save progress" button to not just remember "this.currentFrame", but also if this is on main timeline, or if it is not, which movieclip this is in so that when I upload the sharedObject, it goes to right location, in main timeline or movieclip.
thanks!
I have a feeling SharedObjects may not be able to hold actual movie clip references and be able to restore them reliably (correct me if I am wrong on this), but you could just store objects with key-data pairs in the SharedObject instead of just the frame number alone. Or if you have an unknown number of frame numbers (and movie clips) to remember, store an array of key-data pair objects and loop through them when it comes time to load.
Example of a way to store the data for each clip:
var someClip:Object = new Object();
// some id that refers to the clip (maybe an array/dictionary index)
// (or try replacing with the actual reference to the movie clip to see if it works)
someClip.id ="clipName";
someClip.frameNumber = 1; // the frame number of the clip to remember
Then just store the 'someClip' object into an array in the SharedObject data.
Maybe a more 'compact way' is to do away with having a temporary object and store the key-data pair as just a string with a delimiter. For example, you could just store the string "clipName,1", then when it comes time to load, split along the comma to get the clip id and parse the frame number back to an int.
Or I guess you could also store the frame numbers in a clip id index'ed dictionary and store that in the SharedObject data (as it may save the int parsing step on loading).

this[String] can't be added into a MovieClip

Lets say I have a MovieClip called "Box", and "String" is actually 'box' just that its not a MovieClip
The problem I'm facing now is I can't use something like circle.addChild(this[String])
I've tried tracing this[Strin]==Box and the result returns true.
And when I remove circle.addChild(this[String]), it does not add the Box into it.
BUT, when I traced is there any new object added to circle, the amount still remains the same.
Any idea what seems to be the problem here?
If I understood you, you are trying to add a MovieClip called (has a instance name of) String inside another MovieClip called Box, right?
Well, you can't give the name String to an object, because String is a class name in ActionScript 3.0
You can't name any object with any class name or protected keyword, such if, for, class, Boolean...
Those are words that ActionsScript uses globally (they are Top Level keywords).
Also, note that although you can name an object (give it an instance name of) MovieClip, Sprite, Loader, Stage and such, all theses names are used by ActionScript as class names, inside packages, that can be imported into your animation/application, and Flash will automatically import almost every one of them for you.
If you name your objects with those words you run some risks, like codes that don't work properly or don't compile at all...
As pointed by #Bosworth99 and #merv, you may note, as well, the naming conventions used by ActionScript, the UpperCamelCase and lowerCamelCase.
Classes are (by convention) written with UpperCamelCase (with the first letter capitalized), which indicates that name is a class name.
Objects are, generally, written with lowerCamelCase (with lower first letter and every new composed word with upper case), indicating that word is a object name (or another keyword, which may be reserved already).
Your syntax is a little odd - you say you have a MC called 'Box'. Are you creating this in the flash ide - or programatically? either way - you appear to be referencing Classes, and not instances of a class (an object). Try:
var _circle:MovieClip;
var _box:Sprite;
private function createDisplayObjects():void
{
_circle = new MovieClip();
this.addChild(_circle);
_box = new Sprite();
_circle.addChild(_box);
}
And - just as a generally agreed upon practice, class names are capitalized, and instance name are lowerCamelCase. I like underscores prefixing private vars, as well.
NemoStein is absolutely correct - reserved keywords will bork your code everytime...
good luck