AS3 shuffling movieclips - actionscript-3

I've added the basic targets and applying drag and drop for my puzzle pieces, now Im having trouble making the shuffling aspect. As in, after the player completes or opens up the fla, each time will start the puzzle pieces in random places of the stage. I understand using arrays for shuffling somehow but Im not sure exactly how to achieve this. I've stored the instance of my 19 puzzle pieces inside the array but now I have no idea what to do with this array. Other tutorials were abit out of my league and leaves my head scratching.
Just started doing coding for flash professional so yeah, any help with the shuffling movie clips ie the puzzles pieces would be greatly appreciated.
Heres's my code, Im not posting the whole thing since from P1 to P19 is basically copy pasting:
import flash.events.Event;
stage.addEventListener(Event.ENTER_FRAME, EntFrame)
function EntFrame(e: Event) : void
{
P1.addEventListener(MouseEvent.MOUSE_DOWN, fl_ClickToDrag);
function fl_ClickToDrag(event:MouseEvent):void
{
P1.startDrag();
}
stage.addEventListener(MouseEvent.MOUSE_UP, fl_ReleaseToDrop);
function fl_ReleaseToDrop(event:MouseEvent):void
{
P1.stopDrag();
}
if (T1.hitTestObject(P1.Tar1))
{
P1.x = 313.15;
P1.y = 242.75;
}
P19.addEventListener(MouseEvent.MOUSE_DOWN, fl_ClickToDrag_19);
function fl_ClickToDrag_19(event:MouseEvent):void
{
P19.startDrag();
}
stage.addEventListener(MouseEvent.MOUSE_UP, fl_ReleaseToDrop_19);
function fl_ReleaseToDrop_19(event:MouseEvent):void
{
P19.stopDrag();
}
if (T19.hitTestObject(P19.Tar19))
{
P19.x = 624.35;
P19.y = 455.60;
}
}

Here is what I hope is more holistic answer.
First, ditch those inline functions. Right now you make an ENTER_FRAME listener and inside that function you have inline function defined. This means every frame tick (which is tied to your frame rate, not the main timeline), those functions are going to get created again, and since you are adding them as handlers for listeners, they will stay in memory forever.
Here is a way you code this, showing ways to reduce redundancy and get rid of those memory leaks. This assumes the following:
You have 19 objects on the stage called T1 - T19, that represent the possible locations the pieces can go.
You have 19 pieces on the stage called P1 - P19, that, and the numbers correlate to the T locations as per the correct location of the piece.
//let's create a function to randomize the piece location
function seedPieces() {
//create an array consisting of the integers 1 - 19
var unusedSpaces:Vector.<int> = new Vector.<int>;
var i:int;
for (i = 1; i <= 19; i++) {
//populate that array
unusedSpaces.push(i);
}
var curLocation:DisplayObject; //helper var for the loop below
var curPiece:Sprite; //helper var for the loop below
//loop 19 times (from 1 - 19) - one iteration for each piece
for (i = 1; i <= 19; i++) {
curPiece = this["P" + i] as Sprite; //you can get the piece this way, or use an array if you've made one, like `pieces[i];`
trace(curPiece.name);
//splice removes and returns the item at the specified index (in this case a random number between 0 and arrays length less 1) - the second parameter is amount of items to remove (just 1 for this case)
curLocation = this["T" + unusedSpaces.splice(int(Math.random() * unusedSpaces.length), 1)] as DisplayObject;
trace(" ",curLocation.name);
//move the piece to the random location:
curPiece.x = curLocation.x;
curPiece.y = curLocation.y;
}
}
//NOW, as an aside, you should use a loop to add all your listeners for the sake of sanity - if you have them in an array, loop through that, or use the sloppy way like this:
for (var i:int = 1; i <= 19; i++) {
Sprite(this["P" + i]).addEventListener(MouseEvent.MOUSE_DOWN, fl_ClickToDrag);
}
//create a var to hold any piece that is currently being dragged, so you know which piece to stop drag on later
var currentDraggingItem:Sprite;
seedPieces();
function fl_ClickToDrag(event:MouseEvent):void
{
//assign this clicked item to the currentDraggingItem var
currentDraggingItem = event.currentTarget as Sprite;
//bring this one to the front
currentDraggingItem.parent.addChild(currentDraggingItem);
//you can use this one click handler for all pieces
//the piece that was actually clicked, is referenced by event.currentTarget
currentDraggingItem.startDrag();
//add the mouse up listener now that the mouse is currently DOWN
stage.addEventListener(MouseEvent.MOUSE_UP, fl_ReleaseToDrop);
//listen every frame while dragging
stage.addEventListener(Event.ENTER_FRAME, EntFrame);
}
function fl_ReleaseToDrop(event:MouseEvent):void
{
//if currentDraggingItem has a value, stop drag it
if (currentDraggingItem) {
currentDraggingItem.stopDrag();
//send to the back
currentDraggingItem.parent.addChildAt(currentDraggingItem,0);
}
//remove the mouse up and enter frame listener now that the mouse is UP
stage.removeEventListener(MouseEvent.MOUSE_UP, fl_ReleaseToDrop);
stage.removeEventListener(Event.ENTER_FRAME, EntFrame);
if(checkComplete()){
//game over, do something
}
}
function EntFrame(e: Event) : void
{
//this will snap the peice to the correct spot when the mouse is touching the correct spot
if(currentDraggingItem){
if (this[currentDraggingItem.name.replace("P","T")].hitTestPoint(mouseX,mouseY))
{
currentDraggingItem.x = this[currentDraggingItem.name.replace("P","T")].x;
currentDraggingItem.y = this[currentDraggingItem.name.replace("P","T")].y;
}
}
}
function checkComplete():Boolean {
//use a loop to go through all your pieces and check if they are in the right spot. Again, you could have them in an array, or do it the lazy way
for (var i:int = 1; i <= 19; i++) {
if (!this["T"+i].hitTestObject(this["P"+i]))
{
return false;
}
}
return true;
}

Well, in general you can shuffle with the following code:
var shuffledVector:Vector.<someClass> = new Vector.<someClass>;
while (originalVector.length > 0) {
shuffledVector.push(originalVector.splice(Math.random() * originalVector.length, 1)[0]);
}
Longer, explained version:
var shuffledVector:Vector.<someClass> = new Vector.<someClass>; //We will store our shuffled vector in here
var randomIndex:int; //Random index from the originalVector
var resultVector:Vector.<someClass>; //result from the originalVector.splice(...) function
var randomElement:someClass; //Random element from the originalVector
while (originalVector.length > 0) { //We will reduce the size of the originalVector until the originalVector is empty.
randomIndex = Math.random() * originalVector.length; //Calculate a random index within the range of the originalVector from 0 to originalVector.lenght-1 (note that the range decreases by one on every loop)
randomVector = originalVector.splice(randomIndex, 1); //Use splice to remove one element at the randomly choosen index, we will receive a vector with the removed element...
randomElement = randomVector[0]; //...so we need to access the element
shuffledVector.push(randomElement); //Add the randomly choosen element to our shuffled vector
}
I've written the code for a vector as i suggest to use a vector instead of an array, but the principle behind it is the same for an array.
In your case the originalVector is a vector filled with your P1-P19 Movieclips and someClass would be MovieClip. The originalVector is empty at the end and could be replaced with the shuffled one and of course it would make a lot more sense if you put the code in a seperate function like this:
function Shuffle(originalVector:Vector.<someClass>) : void {
var shuffledVector:Vector.<someClass> = new Vector.<someClass>;
while (originalVector.length > 0) {
shuffledVector.push(originalVector.splice(Math.random() * originalVector.length, 1)[0]);
}
originalVector = shuffledVector;
}
Offtopic, but important for further coding: Someone else already mentioned, that it is not good to add EventListeners on every frame, because it is absolutely unnecessary. You only need to add the Listeners once. Your code is very repetitive, you should use a function which accepts a MovieClip, x and y then call that function 19 times.
e.g.:
function setUpMovieClip(MC:MovieClip, x:int, y:int) : {
MC.addEventListener(MouseEvent.MOUSE_DOWN, clickToDrag);
//more code...
}
within the clickToDrag function you can access the MovieClip which was clicked via the event.target property:
function clickToDrag(e:MouseEvent) : {
e.target.startDrag();
//more code...
}
I hope you get the idea.

Related

AS3: Adding function to population loop

Basically I have 2 movieclip objects with some code, currently just to trace them. The blue circles when clicked will say 'Blue' and the red ones when clicked will say 'Red'. This works fine in theory until I add a population loop, which adds more of them. Then only 1 of each colour correctly works, the rest are just 'mock' circles. I wish for each circle to tell me their colour.
This is my code for the .fla:
import flash.events.MouseEvent;
BlueBall.addEventListener(MouseEvent.CLICK, fun1)
function fun1(e:MouseEvent){
trace("Blue!");
}
RedBall.addEventListener(MouseEvent.CLICK, fun2)
function fun2(e:MouseEvent){
trace("Red!");
}
and this is the population loop in an .as file:
private function PopulateCircles():void
{
for (var i:int=0; i < 10; i++)
{
var blueCircle:BlueCircle = new BlueCircle();
this.addChild(blueCircle);
var redCircle:RedCircle = new RedCircle();
this.addChild(redCircle);
}
}
tldr; how do I get the on-click events to occur on every newly populated circle?
Pretty easy, actually. Just as you subscribe method to listen the predesigned instances' events, you can subscribe via temporary variable references. As long, as the variable holds the reference (or a pointer in C++ terms), you can address the instance and do anything you could do to a predesigned MovieClip:
private function PopulateCircles():void
{
var aRed:RedCircle;
var aBlu:BlueCircle;
for (var i:int = 0; i < 10; i++)
{
// If there are no mandatory constructor arguments,
// you can omit the () brackets.
aRed = new RedCircle;
aBlu = new BlueCircle;
// Disperse clips to random places.
aBlu.x = 500 * Math.random();
aBlu.y = 500 * Math.random();
aRed.x = 500 * Math.random();
aRed.y = 500 * Math.random();
// Subscribe methods to newly created instances.
aRed.addEventListener(MouseEvent.CLICK, fun2);
aBlu.addEventListener(MouseEvent.CLICK, fun1);
// You're operating inside 'this' object,
// no need to explicitly point it out.
addChild(aRed);
addChild(aBlu);
}
}

How to apply drag and drop function to all frames in movie clip? Actionscript3

Each frame contain 1 text field. I apply the code on timeline.
But it only gets applied to the last object, which means that I can only drag and drop the last object. Why?
How can I improve this so that I can drag and drop all objects?
for(var j:uint=0; j<3; j++)
{
var q:Ans = new Ans();
q.stop();
q.x = j * 300+50;// set position
q.y = 500;
var r:uint = Math.floor(Math.random() * q_list.length);
q.qface = q_list[r];// assign face to card
q_list.splice(r,1);// remove face from list;
q.gotoAndStop(q.qface+1);
q.addEventListener(MouseEvent.MOUSE_DOWN, startAnsDrag);
q.addEventListener(MouseEvent.MOUSE_UP, stopAnsDrag);
q.addEventListener(Event.ENTER_FRAME, dragAns);
addChild(q);// show the card
}
//----------------------------drag
// offset between sprite location and click
var clickOffset:Point = null;
// user clicked
function startAnsDrag(event:MouseEvent) :void
{
clickOffset = new Point(event.localX, event.localY);
}
// user released
function stopAnsDrag(event:MouseEvent) :void
{
clickOffset = null;
}
// run every frame
function dragAns(event:Event) :void
{
if (clickOffset != null)
{ // must be dragging
q.x = clickOffset.x+mouseX+135;
q.y = clickOffset.y+mouseY;
}
}
Make a new layer in the timeline just for your drag-and-drop code, which you can remove from your other actionscript. Put the code on the first frame in that layer. Now click on and select the last frame on that layer in which you want the code to be effective (probably the last frame of the MovieClip). Press F5 to draw-out the range of frames which will be affected by the code. Voila!

Creating a vector array of movie clips AS3

I have several movie clips on the stage of my main .fla named btn1-btn7 which will act as buttons. I have a class file named Functions.as where an event listener is created when a button is clicked. onButtonClicked is just going to a frame on the timeline.
obj.addEventListener(MouseEvent.CLICK, onButtonClicked);
I would like the ability to set the buttonMode, visibility, etc. of all of the buttons simultaneously. I have been looking into this for a few hours and am not able to find any solutions. I am now looking into adding them to a vector (which is a new concept for me), but I am not sure how to go about executing this properly. This is what I have so far.
public var buttons:Vector.<MovieClip > = new Vector.<MovieClip > ();
function addButtons()
{
buttons.push(btn1,btn2,btn3,btn4,btn5,btn6,btn7);
for (var i:int; i<buttons.length; i++)
{
trace(buttons[i].name);
}
}
How would I go about, for example, adding the event listener to all of the objects? I will also be setting the buttonMode to true, and making them all invisible simultaneously. I don't even know if it's possible to accomplish this. Thank you in advance for any suggestions.
I'm going to asume that you use timeline code, and have instances of the buttons already placed on the stage. So, first, create the vector:
var _btns:Vector.<MovieClip> = new Vector.<MovieClip>;
_btns.push(btn1,btn2,btn43....) //add all the buttons
Than, you can init the properties of all the buttons:
var _mc:MovieClip;//helper var
for(var i:int=0,i<_btns.length;i++)
{
_mc = _btns[i];
_mc.visible = false;
_mc.buttonMode = true;
_mc.addEventListener(MouseEvent.CLICK, onClick);
}
Then, the event handler:
function onClick(e:MouseEvent):void
{
for(var i:int=0,i<_btns.length;i++)//reset all the buttons
{
_btns[i].visible = false;
}
_mc = MovieClip(e.eventTarget);
_mc.visible = true; //make visible the clicked one
}
You just need to do what you are doing with the .name property in your example code. You need to loop thru every single button in your array (or vector, if you prefer). Here is an example how to set the property of buttonMode:
function setButtonMode(b:Boolean):void {
for(var i:int=0; i<buttons.length; i++) {
var btn:MovieClip = buttons[i]; //store the current reference in a var for faster access
btn.buttonMode = b;
btn.mouseChildren = !b;
}
}

How can I work this out?

I am making a game where insects from above the screen move downwards to the bottom. The object is for the player to kill these insects with his/her mouse. When killed the insect should show a kill frame, where I have put in. The kill frame will stay there for 3 seconds and the object will be removed. This will also increase the player's score.
This code is written inside the insect:
function kill(event:MouseEvent):void
{
this.dead = true;
}
This code is written inside the background movieclip frame.
function moveEnemies():void
{
var tempEnemy:MovieClip;
for (var i:int =enemies.length-1; i>=0; i--)
{
tempEnemy = enemies[i];
if (tempEnemy.dead)
{
tempEnemy.gotoAndStop(21);
var myTimer:Timer = new Timer(3000);
myTimer.addEventListener(TimerEvent.TIMER, timerListener);
myTimer.start();
}
}
}
function timerListener (e:TimerEvent):void
{
for (var i:int =enemies.length-1; i>=0; i--)
{
if (tempEnemy.dead)
{
score++;
roachLevel.score_txt.text = String(score);
removeEnemy(i);
}
}
}
function removeEnemy(id:int)
{
removeChild(enemies[id]);
enemies.splice(id,1);
}
The problem that I am experiencing with this, is that whenever I click on the insects they stay there. I click on another one it stays there. Then another, then after a while they disappear and the score increases. Sometimes the death frame doesn't appear and they die as soon as I touch them. Could you please tell me how I can solve this?
You have to relocate score adding to enemy class, and assign a timeout to the particular squished enemy instead of the base class. The best place to do it is in kill() function.
function kill(e:MouseEvent):void {
this.dead=true;
gotoAndStop(21);
flash.utils.setTimeout(removeSelf,3000);
}
function removeSelf():void {
this.parent.removeChild(this);
}
Now, adjust the mechanics for scoring and tracking objects. First, if an object becomes dead, you immediately remove it from the array of enemies (it will handle its removal on its own) and give score for it. And second, all the other functional should be preserved.
for (var i:int=enemies.length-1;i>=0;i--) {
var tempEnemy=enemies[i];
if (tempEnemy.dead) {
score++;
enemies.splice(i,1);
} else {
tempEnemy.y++; // or other move function
}
}
This loop should be in an enterframe listener, so it'll be called once per frame. Also combining splicing and moving like here eliminates the need of placing movement code elsewhere, as you iterate through the array anyway.

Randomize Saved movieclips stored on children

I'm having a hard time with some code in actionscript 3.0. I don'y know how to randomize the movieclips stored on child and pick only 8 movieclips wherein there are 10 movieclips stored. I hope you'll be able to help me with this problem. thanks
Here is the code:
//start stage function
this.mainmc.addEventListener (Event.ENTER_FRAME, setupStage1);
this.waitingCounter=0;
//set up current stage
function setupStage1 (e:Event) {
//wait for timeline
if (this.waitingCounter<2) {
this.waitingCounter++;
//not ready yet, do nothing
return;
}
//Start the timer
timer.start();
//hide hint
this.mainmc.hintmc.visible=false;
//hide star animation
this.mainmc.starAnimation.visible=false;
//listener for hint button
this.mainmc.hintbut.addEventListener (MouseEvent.CLICK, showHint1);
//create objects array
this.obArr=[];
//count the objects on stage
for (var n=0; n<this.mainmc.numChildren; n++) {
//get the children
var ob=this.mainmc.getChildAt(n);
//only take movie clips
if (ob is MovieClip) {
//only count the movie clips that have name declared
if (ob.myname!=null) {
//push to array
this.obArr.push (MovieClip(ob));
}
}
}
on the code above, the code will store all the movieclips that are present in the stage. it stores them in a child. each 10 movieclips has a variable name "myname".
If you simply want to randomly sort items within an array, use the array.sort method and within your sort function, simply create a random number between 1 and 2. If it's 1, return true, if it's 2, return false. Here is an actionscript 2 snippet along with a link to a couple of tutorials:
var a:Array = new Array(“a”, “b”, “c”, “d”, “e”);
function shuffle(a,b):Number {
var num : Number = Math.round(Math.random()*2)-1;
return num;
}
var b:Array = a.sort(shuffle);
trace(b);
http://mrsteel.wordpress.com/2007/05/26/random-array-in-as2-as3-example-using-sort/
http://sierakowski.eu/list-of-tips/75-random-sort-function-comes-handy-when-building-applications-with-playlists.html
This is a much longer and more in-depth tutorial:
http://active.tutsplus.com/tutorials/actionscript/quick-tip-how-to-randomly-shuffle-an-array-in-as3/