Prevent Movie Clip Overlapsing - actionscript-3

I'm creating a screen saver which requires a movie clip to be loaded to the stage at a random position based on the size of the screen, then fade out (which I have all of the animation within a movie clip as tweens)
I've hit a road block and can't figure out how to prevent the movie clips from overlapsing on top of each other. If anything, I'd like for them to appear at another random spot that does not cause the overlapse.
here is all of the code for my project:
stop();
import flash.display.MovieClip;
import flash.utils.Timer;
import flash.events.TimerEvent;
import flash.events.Event;
import flash.display.Stage;
import flash.display.StageAlign;
import flash.display.StageScaleMode;
import flash.events.Event;
var greystoneLogos:Array = new Array ;
var countTimeArray:Array = new Array ;
var previousLogos:Array = new Array ;
var xpoint:int;
var ypoint:int;
function getNewSymbols()
{
previousLogos = new Array ;
greystoneLogos = new Array ;
var i:int;
for (i=0; i < 3; i++)
{
greystoneLogos[i] = new GreystoneLogo1();
greystoneLogos[i].width = 100;
greystoneLogos[i].height = 60;
addSymbolToStage(greystoneLogos[i],i*2000);
}
}
getNewSymbols();
function addSymbolToStage(currentLogo:MovieClip,waitTime:int)
{
var i3:int;
var i4:int;
var logoBoundaries:Array = new Array()
var XandY:Array = new Array()
for (i3=0; i3 < greystoneLogos.length; i3++)
{
if (greystoneLogos[i3] !== currentLogo)
{
xpoint = randomRange(this.stage.stageWidth - (currentLogo.width * 4.8));
ypoint = randomRange(this.stage.stageHeight - (currentLogo.height * 6.9));
logoBoundaries = getOffDimensions(currentLogo)
for (i4=0; i4 < logoBoundaries.length; i4++)
{
XandY = logoBoundaries[i4].split(":")
while ((xpoint <= (Number(XandY[0]) + Number(currentLogo.width * 4.8)) && xpoint >= (Number(XandY[0]) - Number(currentLogo.width * 4.8))) && (ypoint <= (Number(XandY[1]) + Number(currentLogo.height * 6.9)) && ypoint >= (Number(XandY[1]) - Number(currentLogo.height * 6.9)))){
xpoint = randomRange(this.stage.stageWidth - (currentLogo.width * 4.8));
trace(XandY[0] + " And " + (Number(currentLogo.width * 4.8)))
trace(xpoint + " And " + (Number(XandY[0]) + Number(currentLogo.width * 4.8)))
ypoint = randomRange(this.stage.stageHeight - (currentLogo.height * 6.9));
}
}
}
else
{
continue;
}
}
previousLogos.push(currentLogo);
currentLogo.x = xpoint;
currentLogo.y = ypoint;
stage.addChild(currentLogo);
currentLogo.gotoAndStop(1);
var countTime:Timer = new Timer(waitTime,1);
countTime.addEventListener(TimerEvent.TIMER, function(){
currentLogo.gotoAndPlay(1);
currentLogo.addFrameScript ( currentLogo.totalFrames - 1 , function(){
currentLogo.stop()
stage.removeChild(currentLogo)
if(stage.numChildren <= 1){
getNewSymbols();
}
}) ;
});
countTime.start();
}
function getOffDimensions(currentLogo:MovieClip){
var i3:int;
var tempArr:Array = new Array()
for (i3=0; i3 < greystoneLogos.length; i3++)
{
if (greystoneLogos[i3] !== currentLogo){
tempArr[i3]=greystoneLogos[i3].x +":"+ greystoneLogos[i3].y
}
}
return tempArr
}
function randomRange(max:Number, min:Number = 0):Number
{
return Math.random() * (max - min) + min;
}
There are also may be a handful of unused variables from multiple things I've been trying out.
The code that I posted, will make the movie clip appear at a random spot based on the last movie clip that came up. So let's say we have 3 movie clips (the user will be able to change how many of the clips get displayed) 1 appears at 0,0 the other at 400,400 and the last one appears at 10,10 because I have no way of saving the previous values to compare in the while loop.
I hope this clarifies it a tad more
EDIT:
Based on a function shown below, I've added this:
for (i3=0; i3 < greystoneLogos.length; i3++)
{
if (greystoneLogos[i3] !== currentLogo && greystoneLogos[i3] != null)
{
while(currentLogo.hitTestObject(greystoneLogos[i3]) == true){
xpoint = randomRange(this.stage.stageWidth - (currentLogo.width));
ypoint = randomRange(this.stage.stageHeight - (currentLogo.height));
i3 = 0
}
}else{
continue;
}
}
Which results in a rather bad loop as well as the logo's still overlap above each other

The quickest way I can think to solve this is to do a hit test (http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/flash/display/DisplayObject.html#hitTestObject()) on the new MovieClip and if it returns true, run the placement code again

Ok, here's the implementation of this quickest way.
You can track your present displayObject by the .numChildren property. You don't even need an array of your logos or things. Let's say you have a function that adds new movieClip to your screen. It knows how many clips you can have (MovieClipDummy is just my testing class - it draws a circle with a specified radius. It should be replaced by your objects).
private function addAnotherMovieClip(): void {
if (_currentDummmiesCount < _dummmiesCount) {
var newDummy: MovieClipDummy = new MovieClipDummy(90);
addChild(newDummy);
//If we get a stackoverflow error (see below)
//we just remove our object from the screen as it will most likely just won't fit
if (checkForEmptySpace(newDummy) != "") {
removeChild(newDummy);
return;
}
} else {
//Do nothing or do something
}
}
It calls another function, checkForEmptySpace, which tries to place your movieclip so it won't overlap with other objects. Here it is:
private function checkForEmptySpace(newClip: Sprite): String {
//you should store your screen width somewhere.
//you can also call stage.stageWidth instead, but first make sure that you
//always have a link to the stage or it will throw an error
newClip.x = Math.random() * _screenWidth;
newClip.y = Math.random() * _screenHeight;
//===========================
//Important part - we try to check all the present children of our container
//numChildren is a property of the DisplayObjectContainer
for (var i: int; i < numChildren; i++) {
//We need a try here because we will get a StackOverflow error if there's no empty space left
try {
//We need to check if our current display object, received with getChildAt()
//is not the same as the one we've just added to the screen
//And if our new object intersects with ANY other object on the stage - we
//call THIS function once again.
//We do recursion because we can easily catch an error and remove this object from
//the screen
if (newClip != getChildAt(i) && newClip.hitTestObject(getChildAt(i))) {
//If our recursive function returns an error - we should pass it further
if (checkForEmptySpace(newClip) != "") {
return "error";
}
}
//The only error that can go here is stackoverflow error. So when we get one
//we return this error string
} catch (error: Error) {
trace(error);
return "error";
}
}
//We only return this empty string if we don't have stackoverflow error
//so there's possibly no space left for another movieclip
return "";
}
You can do it without recursion, but you will have to check for empty space with another logic.
And, you can do it more "professionally" by using Minkowski addition. You should consider storing an array of "boundary" points of your movieclips (let's say every movieclip is a rectangle) and when you add a new object to the screen, you calculate this Minkowsky addition. It will have some "free" spots on your screen which represent any possible coordinates of your new movieclip. It's pretty interesting to implement something like that because the accuracy will be phenomenal. But if you don't have time - just use that recursive placement function

Related

How to get symbols to revert back to starting position in AS3 flash

Hi im trying to create a very simple drag and drop game in flash and I the scene to just loop round itself so say, if ( user does this) then go back to the beginning of the scene. but when I try this the symbols that the user has moved stay to where they have moved them too rather than returning to the position they were in at the start of the scene.
Can anyone explain a way to get the symbols to return to where they were at the begining of the scene once the if statement has completed?
The following example will traverse the display list, storing symbols along with their initial positions in a Dictionary. When it's time to reset, just call the resetToOriginalPositions method, passing it the Dictionary that was created.
import flash.utils.Dictionary;
import flash.display.DisplayObject;
import flash.geom.Point;
import flash.events.MouseEvent;
import flash.events.Event;
import flash.display.MovieClip;
var originalPositions:Dictionary;
if(!originalPositions)
{
originalPositions = collectPositions(this);
}
function collectPositions(container:DisplayObjectContainer):Dictionary
{
var positions:Dictionary = new Dictionary(true);
for(var i:int = 0; i < container.numChildren; i++)
{
var currentSymbol:DisplayObject = container.getChildAt(i);
positions[currentSymbol] = new Point(currentSymbol.x, currentSymbol.y);
// this will allow the symbols to be dragged/dropped
currentSymbol.addEventListener(MouseEvent.MOUSE_DOWN, clickHandler);
currentSymbol.addEventListener(MouseEvent.MOUSE_UP, clickHandler);
}
return positions;
}
function clickHandler(e:MouseEvent):void
{
var target:MovieClip = e.target as MovieClip;
if(e.type == MouseEvent.MOUSE_DOWN)
{
target.startDrag();
}
else
{
target.stopDrag();
}
}
function resetToOriginalPositions(positions:Dictionary):void
{
for(var currentSymbol:Object in positions)
{
var position:Point = positions[currentSymbol];
DisplayObject(currentSymbol).x = position.x;
DisplayObject(currentSymbol).y = position.y;
}
}
// this line should be called in your if statement that you already have set up
resetToOriginalPositions(originalPositions);
Marcela's answer may work for you, but just in case you would like something a little simpler...you can store the original positions on a dynamic property of each object. When the condition you require is met to go back to the original positions you can call a function that resets the current x and y positions to the original ones.
//place in frame 1
dragObject1.originalY = dragObject1.y;
dragObject1.originalX = dragObject1.x;
dragObject2.originalY = dragObject2.y;
dragObject2.originalX = dragObject2.x;
//...rest of object posisitions
function resetObjectPositions()
{
dragObject1.y = dragObject1.originalY;
dragObject1.x = dragObject1.originalX;
dragObject2.y = dragObject2.originalY;
dragObject2.x = dragObject2.originalX;
//...rest of object positions
}
//later in scene
if(condition)
{
resetObjectPositions();//make sure to run this code before going back to frame one in the scene to use the original values;
}

AS3 Object Bin and Reset Button

I am trying to create a game for kids where they can drag letters on to a stage to make words.
I want to add a 'trash can' where users can drag letters they no longer need to dispose of them. I have created the movie clip but am totally unsure how to make it function using AS3.
I would also like to add a reset button so that the stage reverts to it's original state. Again, I have drawn it up and added the little as3 that I am aware of (to make it a button) but if anyone could assist with how to actually make this happen, I would be grateful.
The files are here: SWF | FLA and the code for the game is as follows:
import flash.display.MovieClip;
for (var i=1; i<27; i++)
{
this["object" + i].addEventListener(MouseEvent.MOUSE_DOWN, onStart);
this["object" + i].addEventListener(MouseEvent.MOUSE_UP, onStop);
}
var sx = 0,sy = 0;
function onStart(e)
{
sx = e.currentTarget.x;
sy = e.currentTarget.y;
e.currentTarget.startDrag();
}
function onStop(e)
{
if ( e.target.dropTarget != null &&
e.target.dropTarget.parent == dest &&
e.currentTarget.name != "copy" )
{
var objectClass:Class =
getDefinitionByName(getQualifiedClassName(e.currentTarget)) as Class;
var copy:MovieClip = new objectClass();
copy.name = "copy";
this.addChild(copy);
copy.x = e.currentTarget.x;
copy.y = e.currentTarget.y;
e.currentTarget.x = sx;
e.currentTarget.y = sy;
copy.addEventListener(MouseEvent.MOUSE_DOWN, onStart);
copy.addEventListener(MouseEvent.MOUSE_UP, onStop);
}
e.currentTarget.stopDrag();
}
resetButton.addEventListener(MouseEvent.CLICK, reset);
resetButton.buttonMode = true;
function reset(event:MouseEvent):void
{
//Not sure what AS3 to add here to reset to original state
}
I have already gave you the solution here Flash AS3 Clone, Drag and Drop
Here, I am providing a detail solution on how to drag objects inside a bin and remove them.
For dropping copied objects inside a bin, after dragging is stopped, check collision with bin object. for more info see,
copiedObject.hitTestObject(binObject)
For e.g.
First create trash-can MovieClip on the stage and give it an instance name 'trashCan' and add following lines to your onStop()(below e.currentTarget.stopDrag();)function like so:
UPDATE:
var copiedObjsArr:Array = [];
function onStop(e)
{
if ( e.target.dropTarget != null &&
e.target.dropTarget.parent == dest &&
e.currentTarget.name != "copy" )
{
//Code here remains same
//.......
//Keep collecting copied letters for further access in `reset()` function
copiedObjsArr.push(copy);
}
else if(e.currentTarget.name == "copy") //this is 'else if' (newly added)
{
var tarObject:MovieClip = e.currentTarget;
// These detects collision of dragged object with the trashCan
if(tarObject.hitTestObject(trashCan)) {
//These removes dragged object from the display list (not from the memory)
removeChild(tarObject);
tarObject = null; //to garbage
}
}
e.currentTarget.stopDrag();
}
And your reset() becomes like so:
function reset(event:MouseEvent):void
{
if(copiedObjsArr.length > 0)
{
//Traverse through all copied letters
for(var i:int = 0; i<copiedObjsArr.length; i++)
{
var objToRemove:MovieClip = copiedObjsArr[i];
removeChild(objToRemove);
objToRemove = null;
}
//Finally empty the array
copiedObjsArr = [];
}
}

how to create element only after previous is on stage

How do i add a new element only after the previous one has passed the stage.stageWidth / 2
except by my way (the code below ,where i create a zone that the element will pass only one time)
PS:I dont want to do it like this cause the speed of movement will be different in time (it will slowly go up and down). Like from 3 to 6 by a easing factor of 0.005
so far i have this
import flash.display.MovieClip;
import flash.events.Event;
public class Main extends MovieClip
{
private var myArray:Array = new Array();
public function Main()
{
stage.addEventListener(Event.ENTER_FRAME, everyFrame)
var item:Box = new Box();
item.x = stage.stageWidth - 100
item.y = 40
addChild(item)
myArray.push(item)
}
private function everyFrame(ev:Event):void
{
var myBox:Box
for(var i:int = 0; i< myArray.length; i++)
{
myBox = myArray[i]
myBox.x -=3
if(myBox.x <= stage.stageWidth/2 && myBox.x >= stage.stageWidth/2 - 3)
{
trace("new Box")
var myNewBox:Box = new Box()
myNewBox.x = stage.stageWidth - 100
myNewBox.y = 40
addChild(myNewBox)
myArray.push(myNewBox)
}
if(myBox.x < 0 )
{
removeChild(myBox)
myArray.splice(i, 1)
trace(myArray.length)
}
}
}
}
Your code is already working and do things as you required.
The code looks like document class, but there is one little mistake that prevent it from execution. You forget package{...} wrap.
But you compiler should say you about this, didn't it?
You are right, using range can provide you bunch of problems then objects didn't get in it, or get several times.
To solve this you could not check area but only myBox.x<= stage.stageWidth/2 condition. After object met this condition, just remove element from array you use for checking and add it to array of objects which you check for leaving stage to delete them.
If you don't want make another array, you could add some property to every new Box.
For example - passedCenter and set it to false. Then change if statement for
if(myBox.x <= stage.stageWidth/2 && !myBox.passedCenter){
myBox.passedCenter=true;
//you stuff
}

How to Add objects without them hitting each other

Im doing a test/game and i have bumped in a problem I cant figure out.
I am trying to add Objects (in my case Bricks) to the stage ,but adding in such a way that they never hit one other, and when there is`t any space left on the stage ,to stop adding them and to display lets say "no more space".
The stage is 500x500px and the "block" is 75px to 30px ,but I need this to be able to do the same with other objects with different width and height.
I would be very thankful to an solution for this.:)
The creating of the blocks is done in an AS.
There is a movieClip exported for AS with the name Block
package {
import flash.display.MovieClip;
import flash.events.Event;
public class MainClass extends MovieClip {
private var _blockTime:Number = 0;
private var _blockLimit:Number = 20;
private var Number_:int =0;
private var _blockHolder:MovieClip = new MovieClip();
public function MainClass() {
addEventListener(Event.ENTER_FRAME ,onEveryFrame);
this.addChild(_blockHolder)
}
private function onEveryFrame(ev:Event):void{
makeBlocks();
}
private function makeBlocks():void{
_blockTime++;
if(_blockTime >= _blockLimit){
var _block:Block = new Block();
_block.y = Blocks_YX_Positioning()
_block.x = Blocks_YX_Positioning()
_blockHolder.addChild(_block);
_blockTime = 0;
Number_++
}
}
//code so the block is staing on the stage
private function Blocks_YX_Positioning():int{
var _block_YX:int = Math.random()*500
if (_block_YX < 0 ) {
_block_YX = 50;
}
if (_block_YX > 450 ) {
_block_YX = 450;
}
return _block_YX;
}
}
}
It's as simple as implementing a 2 dimensional Array representing a grid. You don't need any collision detection.
This i how it goes. first you create an Array that represents the grid.
var arrayGrid = [];
// define number of elements in grid
var columns = Math.floor(500 / 70);
var rows = Math.floor(500 / 30);
// create the references: 1,2,3,4 etc.
for( var i = 0; i < columns * rows; i++ ) {
array[i] = i;
}
As you can see, we are putting a number in every element of the array for every element on the imaginary grid. If the grid was 5 x 5, then it would have 25 elements.
Then you make a function like putBrick that puts your bricks in the stage randomly using a number from the array. I'm using 12 as the random number. At the end of the function remove the element of the array we used as a reference. At the start of the function check if the array is empty.
if( arrayGrid.length > 0 ){
var someRandomNumber = 12;
var currentCol = someRandomNumber % columns;
var currentRow = Math.floor(someRandomNumber / columns);
brick.x = currentCol * brick.width;
brick.y = currentRow * brick.height;
array.splice(someRandomNumber,1);
} else {
trace("we are done here!");
}
You will have to tweak a few things, and find the random number, but that's the core logic.

StackOverflow Error AS3

This is the code I am getting a StackOverflow error for. I am not entirely sure what is wrong with it. The code is plug and play, so u cna plug it in and test it your self. Can somebody please Help me with it? I am basically genereating 2 different objects from one array and trying to get rid of the object that gets clicked on and, then I put that object into a different array.
import flash.sampler.NewObjectSample;
import flash.display.Sprite;
import flash.events.MouseEvent;
var eating_breakfast:Sprite;
var walking:Sprite;
var swimming:Sprite;
var art:Sprite;
var choices:Array = new Array ();
//Sprite Creation
eating_breakfast = new Sprite ();
eating_breakfast.graphics.beginFill(0xE39D43);
eating_breakfast.graphics.drawRect(0,0,50,50);
eating_breakfast.graphics.endFill();
eating_breakfast.x = 50;
eating_breakfast.y = 50;
walking = new Sprite ();
walking.graphics.beginFill(0xC3266C);
walking.graphics.drawRect(0,0,50,50);
walking.graphics.endFill();
walking.x = 100;
walking.y = 100;
swimming = new Sprite ();
swimming.graphics.beginFill(0x48AFD1);
swimming.graphics.drawRect(0,0,50,50);
swimming.graphics.endFill();
swimming.x = 150;
swimming.y = 150;
art = new Sprite ();
art.graphics.beginFill(0xafdb44);
art.graphics.drawRect(0,0,50,50);
art.graphics.endFill();
art.x = 200;
art.y = 200;
//adding sprites into array
choices.push( eating_breakfast);
choices.push(walking);
choices.push(swimming);
choices.push(art);
var indexcount = 0;
var randomize:Number;
var storageArray: Array = new Array ();
civilizedorder();
randomizedorder();
this.addEventListener(MouseEvent.CLICK,switchpic);
//pick the target generated object
function switchpic(t:MouseEvent)
{
//for index count
// this works as a target so if your mouse target is the object generated by indexcount this will initiate
if (t.target == choices[indexcount])
{
storageArray.push(choices[indexcount]);
removeChild(choices [indexcount]);
removeChild(choices [randomize]);
choices.splice(indexcount,1);
goNext();
}
// for randomize
if (t.target == choices[randomize])
{
// this works as a target so if your mouse target is the object generated by randomize this will initiate
storageArray.push(choices[randomize]);
removeChild(choices [indexcount]);
removeChild(choices [randomize]);
choices.splice(randomize,1);
indexcount++;
goNext();
}
}
//generates the index count object
function civilizedorder()
{
trace("The Index count is" + indexcount);
addChild(choices [indexcount]);
choices[indexcount].x = 300;
}
trace("The number of choices in the choice array is " + choices.length);
//generates the randomized object
function randomizedorder()
{
randomize = Math.floor(Math.random() * choices.length);
trace("the random number is" + randomize);
if (randomize == indexcount )
{
randomizedorder();
}
else
{
addChild(choices [randomize]);
}
}
function goNext()
{
trace("The storagearray has " + (storageArray.length));
if (choices.length < 0 || choices.length > 0)
{
if (indexcount > choices.length-1)
{
indexcount = choices.length - 1;
}
civilizedorder();
randomizedorder();
}
}
Stack Overflow means you have too much recursion. In this case, that's probably in the randomizedorder function when choices.length is 1 and indexcount is 0 (i.e. the first call of goNext), it makes an infinite loop.
You need to re-think the structure of this program. Avoid recursion wherever possible. Loops are better, but you don't need them either; to fix that one function:
randomize = Math.floor(Math.random() * (choices.length - 1));
if (randomize >= indexcount ) {
randomize ++;
}
You'll still probably get bizarre results since it isn't being called as you expect, but the stack overflow should go away.