as3 Quadtree being slow - actionscript-3

This is my quadtree class, but i haven't added the collision detection yet, in all the examples online they can get 500 + at 60 fps with collision detection but my one only running at 20 fps without collision detection.
I'm following this tutorial http://gamedevelopment.tutsplus.com/tutorials/quick-tip-use-quadtrees-to-detect-likely-collisions-in-2d-space--gamedev-374 which is java but im using as3
public class Quadtree extends Entity
{
private var Max_objects:int = 1;
private var Max_levels:int = 5;
private var level:int;
private var objects:Vector.<Rectangle>;
public var rectangle:Rectangle;
public var Quadtree_list:Vector.<Quadtree>;
public function Quadtree(tmp_level:int , tmp_rec:Rectangle)
{
level = tmp_level;
objects = new Vector.<Rectangle>();
rectangle = tmp_rec;
Quadtree_list = new Vector.<Quadtree>();
Quadtree_list.length = 3;
}
public function clear():void
{
objects.length = 0;
for (var i:Number = 0; i < Quadtree_list.length ; i++)
{
if (Quadtree_list[i] != null)
{
Quadtree_list[i].clear();
world.remove(Quadtree_list[i]);
Quadtree_list[i] = null;
}
}
}
public function split():void
{
var subWidth:int = rectangle.width / 2;
var subHeight:int = rectangle.height / 2;
var xx:int = rectangle.x;
var yy:int = rectangle.y;
Base._world.add(Quadtree_list[0] = new Quadtree(level + 1, new Rectangle(xx + subWidth, yy, subWidth, subHeight)));
Base._world.add(Quadtree_list[1] = new Quadtree(level+1,new Rectangle(xx ,yy,subWidth,subHeight)));
Base._world.add(Quadtree_list[2] = new Quadtree(level+1,new Rectangle(xx,yy + subHeight,subWidth,subHeight)));
Base._world.add(Quadtree_list[3] = new Quadtree(level+1,new Rectangle(xx + subWidth,yy + subHeight,subWidth,subHeight)));
}
/*
* Determine which node the object belongs to. -1 means
* object cannot completely fit within a child node and is part
* of the parent node
*/
public function get_index(tmp_rect:Rectangle):Number
{
var index:int = -1;
var verticalMidpoint:Number = rectangle.x + (rectangle.width / 2);
var horizontalMidpoint:Number = rectangle.y + (rectangle.height / 2);
// Object can completely fit within the top quadrants
var topQuadrant:Boolean = (tmp_rect.y < horizontalMidpoint && tmp_rect.y + tmp_rect.height < horizontalMidpoint);
// Object can completely fit within the bottom quadrants
var bottomQuadrant:Boolean = (tmp_rect.y > horizontalMidpoint);
// Object can completely fit within the left quadrants
if (tmp_rect.x < verticalMidpoint && tmp_rect.x + tmp_rect.width < verticalMidpoint)
{
if (topQuadrant)
{
index = 1;
}
else if (bottomQuadrant)
{
index = 2;
}
}
else
// Object can completely fit within the right quadrants
if (tmp_rect.x > verticalMidpoint)
{
if (topQuadrant)
{
index = 0;
}
else if (bottomQuadrant)
{
index = 3;
}
}
return index;
}
/*
* Insert the object into the quadtree. If the node
* exceeds the capacity, it will split and add all
* objects to their corresponding nodes.
*/
public function insert(tmp_rect:Rectangle):void
{
if (Quadtree_list[0] != null)
{
var index:int = get_index(tmp_rect);
if (index != -1)
{
Quadtree_list[index].insert(tmp_rect)
return;
}
}
objects.push(tmp_rect);
if (objects.length > Max_objects && level < Max_levels)
{
if (Quadtree_list[0] == null)
{
split();
}
var i:int = 0;
while (i < objects.length)
{
var indexx:int = get_index(objects[i]);
if (indexx != -1)
{
Quadtree_list[indexx].insert(objects[i]);
objects.splice(i, 1);
}
else
{
i++;
}
}
}
}
Can you see why it's not performing very well?

Hard to say for certain without seeing exactly how you're using it. Plus it extends Entity which could be doing ...anything :)
I'm not an expert on Quadtrees either, but if you're calling split() a lot, it looks like it could end up being taxing - lots of instantiation calls to new Quadtree and new Rectangle. If this is indeed a bottleneck, you could look into instantiating one rectangle instance that you just pass around. Same with Quadtree. Or use object pooling so you're at least recycling instead of creating new things like crazy.
I hope that helps :)

Related

Flex tree expand slowly/lazy loading when there are more than 30000 nodes

I want to open all nodes of Flex mx Tree in such a way that UI is responsive at all times. Normally when there are limited no of nodes then this code works just fine.
public function expandAllNodes():void {
for (var i:int = 0; i < this.dataProvider.length; i++) {
expandChildrenOf(this.dataProvider.getItemAt(i), true);
}
}
maybe i need to callLater instead of directly calling the function
public function expandAllNodes():void {
for (var i:int = 0; i < this.dataProvider.length; i++) {
calllater(expandChildrenOf,[this.dataProvider.getItemAt(i), true]);
}
}
but this is not working either.
Using callLater won't help in this case. From the documentation,
The callLater method queues an operation to be performed for the next screen refresh, rather than in the current update.
Using callLater in your loop will simply push all the expandChildrenOf() calls to the next screen refresh -- which will still put way too much work into a single update cycle.
Instead, you could spread your loop over several frames and limit work done per frame:
private var processingLimit:int = 10; //experiment to get a reasonable value for this
private var totalNodes:int = 0;
private var nodesProcessed:int = 0;
public function expandAllNodes():void {
this.totalNodes = this.dataProvider.length;
this.addEventListener(Event.ENTER_FRAME, expandNextNode);
}
private function expandNextNode(e:Event):void {
var numToProcess = Math.min(processingLimit + nodesProcessed, totalNodes);
for (var i:int = nodesProcessed; i < numToProcess; i++) {
nodesProcessed++;
expandChildrenOf(this.dataProvider.getItemAt(i), true);
}
if (numToProcess == totalNodes) {
this.removeEventListener(Event.ENTER_FRAME, expandNextNode);
}
}
n.b. Make sure that expandChildrenOf doesn't recursively open children of children -- if it does, then you could still end up expanding the entire tree based on a single call to expandChildrenOf(1);!
This is how i solved the issue
private const PROCESSING_LIMIT:int = 25;
public var _expandNodesStack:Vector.<Object>;
private function expandNextNode(e:Event):void {
for (var i:int = 0; i < PROCESSING_LIMIT; i++) {
var item:* = _expandNodesStack.pop();
if (item)
expandItem(item, true);
else
break;
}
if (_expandNodesStack.length == 0) {
this.removeEventListener(Event.ENTER_FRAME, expandNextNode);
}
}
public function expandAllNodes():void {
if(this.dataProvider == null || this.dataProvider.length <= 0) return;
if (_expandNodesStack != null && _expandNodesStack.length > 0) {
//already expanding so abort this operation
return;
}
if (_expandNodesStack == null) _expandNodesStack = new <Object>[];
iterateAndPushToStack(this.dataProvider.getItemAt(0));
_expandNodesStack.reverse();
this.addEventListener(Event.ENTER_FRAME, expandNextNode);
}
private function iterateAndPushToStack(item:Object):void {
if (iterator == null)
return;
// if it is not a branch item there's nothing to do
if (item != null && item.children is ArrayCollection && item.children.length > 0) {
_expandNodesStack.push(item);
var childItems:ArrayCollection;
childItems = item.children;
if (childItems) {
for each (var object:Object in childItems) {
iterateAndPushToStack(object);
}
}
}
}

Trouble with addChild/removeChild and display list order in ActionScript3

I am working on a project, which includes a Lake symbol that the player can throw stones into, which in turn causes octopi to rise out of the lake in the positions that each stone hits the lake.
There is also a symbol for the splash made by the stone which will appear after the stone hits and before the octopus appears.
It is likely that there will be many octopi on the screen at the same time and they need to be ordered in the display list so that the ones that should appear further back are behind the others.
Each instance of these symbols should only play once and then be removed.
My code for this makes use of the different add/remove child method alongside for loops, conditionals and arrays which I have put together with the help of various tutorials and forums.
The problem I have is that when you click on the lake two or more times in quick succession, the stone and the splash symbols aren't removed properly and often keep looping.
Here is the code I am using. Any ideas?
var stone:Stone;
var stoneSplash:StoneSplash;
var octopus1:Octopus1;
var octopus2:Octopus2;
var whichOctopus:Array = [addOctopus1, addOctopus2];
var octopusScale:Number;
var octopusContainer:MovieClip = new MovieClip;
lake.lakeHitArea.addEventListener(MouseEvent.CLICK, onClickLake);
//Add octopusContainer to the stage's display list just above the Lake
addChildAt(octopusContainer,getChildIndex(lake) + 1);
octopusContainer.x = 0;
octopusContainer.y = 0;
function onClickLake(e:MouseEvent):void
{
trace("CLICK");
throwStone(mouseX, mouseY);
}
function throwStone(stonePositionX:int, stonePositionY:int)
{
stone = new Stone();
stone.x = stonePositionX;
stone.y = stonePositionY;
addChild(stone);
addEventListener(Event.ENTER_FRAME, removeStone);
}
function removeStone(e:Event):void
{
var count:int = numChildren;
var children:Array = [count];
//load all the children of the component into an Array
for (var i:int=0; i<count/* -1*/; i++)
{
children[i] = getChildAt(i/* + 1*/);
}
for (i=0; i<count/* - 1*/; i++)
{
if (children[i] is Stone)
{
if (children[i].currentFrameLabel == "Splash")
{
stoneSplash = new StoneSplash();
octopusContainer.addChild(stoneSplash);
stoneSplash.x = children[i].x;
stoneSplash.y = children[i].y;
}
if (children[i].currentFrameLabel == "end")
{
octopusContainer.removeChild(stoneSplash);
var positionX:int = children[i].x;
var positionY:int = children[i].y;
addOctopus(positionX, positionY);
removeChild(children[i]);
}
}
}
}
function addOctopus(positionX, positionY)
{
var o:int = Math.round(randomNumber(0,1));
whichOctopus[o](positionX, positionY);
reorderDisplayList();
addEventListener(Event.ENTER_FRAME, removeOctopus);
}
function addOctopus1(positionX: int, positionY:int):void
{
// if (whichOctopus1 == true)
// {
// var octopus:* = octopus1_1;
// }
// else
// {
// octopus = octopus1_2;
// }
octopus1 = new Octopus1();
var octopus:DisplayObject = octopus1;
octopusContainer.addChild(octopus);
octopus.x = positionX;
octopus.y = positionY;
octopusScale = randomNumber(0.5,0.85);
octopus.scaleX = octopusScale;
octopus.scaleY = octopusScale;
trace("children = " + octopusContainer.numChildren);
testPosition(octopus);
}
function addOctopus2(positionX: int, positionY:int):void
{
// if (whichOctopus2 == true)
// {
// var octopus:* = octopus2_1;
// }
// else
// {
// octopus = octopus2_2;
// }
octopus2 = new Octopus2();
var octopus:DisplayObject = octopus2;
octopusContainer.addChild(octopus);
octopus.x = positionX;
octopus.y = positionY;
octopusScale = randomNumber(0.25,0.5);
octopus.scaleX = octopusScale;
octopus.scaleY = octopusScale;
trace("children = " + octopusContainer.numChildren);
testPosition(octopus);
}
function testPosition(octopus:Object):void
{
trace(octopus)
for (var i:int = 0; i < 200; i++)
{
if (lake.hitTestPoint(octopus.x + octopus.hitTestBox1.x * octopus.scaleX,octopus.y + octopus.hitTestBox1.y * octopus.scaleY,true))
{
break;
}
else
{
octopus.x++;
}
}
for (i = 0; i < 100; i++)
{
if (lake.hitTestPoint(octopus.x + octopus.hitTestBox2.x * octopus.scaleX,octopus.y + octopus.hitTestBox2.y * octopus.scaleY,true))
{
break;
}
else
{
octopus.y--;
}
}
for (i = 0; i < 200; i++)
{
if (lake.hitTestPoint(octopus.x + octopus.hitTestBox3.x * octopus.scaleX,octopus.y + octopus.hitTestBox3.y * octopus.scaleY,true))
{
break;
}
else
{
trace(i);
octopus.x--;
}
}
for (i = 0; i < 100; i++)
{
if (lake.hitTestPoint(octopus.x + octopus.hitTestBox1.x * octopus.scaleX,octopus.y + octopus.hitTestBox1.y * octopus.scaleY,true))
{
break;
}
else
{
octopus.y--;
trace(i);
}
}
}
function randomNumber(min:Number, max:Number):Number
{
return Math.random() * (max - min) + min;
}
function reorderDisplayList():void
{
//the number of children in our component
var count:int = octopusContainer.numChildren;
var children:Array = [count];
//load all the children of the component into an Array
for (var i:int=0; i<count; i++)
{
children[i] = octopusContainer.getChildAt(i);
}
//sort the Array children based on their 'y' property
children.sortOn("y", Array.NUMERIC);
//re-add the children to the component ;
//in the order of the sorted Array we just created.
//When we add the children using 'addChild' it will
//be added at the top of the component's displaylist
//and will automatically be removed from its original position.
for (i=0; i<count/* - 1*/; i++)
{
if (children[i] is Octopus1 || children[i] is Octopus2)
{
// trace("child = " + children[i] + " at i: " + i);
octopusContainer.removeChild(children[i]);
octopusContainer.addChild(children[i]);
}
}
}
function removeOctopus(e:Event):void
{
var count:int = octopusContainer.numChildren;
var children:Array = [count];
//load all the children of the component into an Array
for (var i:int=0; i<count/* -1*/; i++)
{
children[i] = octopusContainer.getChildAt(i/* + 1*/);
}
for (i=0; i<count/* - 1*/; i++)
{
if (children[i] is Octopus1 || children[i] is Octopus2)
{
trace(i);
trace("Is an octopus");
if (children[i].currentFrame >= 202)
{
octopusContainer.removeChild(children[i]);
}
}
}
}
I would greatly appreciate any advice to help me overcome this hurdle and continue with my project.
Thank you in advance.
Chris Collins.
Your issue (or at least one of them) is that your code will only remove the most recent StoneSplash. So if you click a bunch of times in between the splash and end animation , only the last clicked one will get removed.
This is because you are using a global var (stoneSplash) to reference the splash, and it gets overwritten to the new one. You need to either add a splash reference on the stone itself, or create a dictionary so you know which splash goes with which stone.
Here would be one way:
if (children[i].currentFrameLabel == "Splash")
{
stoneSplash = new StoneSplash();
MovieClop(children[i]).stoneSplash = stoneSplash; //add a reference the splash on the stone itself
Then later, instead of octopusContainer.removeChild(stoneSplash); do:
octopusContainer.removeChild(MovieClop(children[i]).stoneSplash);
This way your removing the correct splash that goes with this stone.
Here would be a much cleaner way to architect this instead of using an enter frame handler:
On your Stone class timeline, put the following code on your Splash and End frames respectively:
Splash frame: this.dispatchEvent(new Event("Splash"));
End frame: this.dispatchEvent(new Event("End"));
Listen for those events when you create a new stone instance:
stone = new Stone();
stone.x = stonePositionX;
stone.y = stonePositionY;
stone.addEventListener("Splash", splashHandler,false,0,true);
stone.addEventListener("End",removeStone,false,0,true);
addChild(stone);
Respond to those events appropriately:
function splashHandler(e:Event):void {
var stone:Stone = e.currentTarget as Stone;
stoneSplash = new StoneSplash();
//you need a reference to the splash from the stone class - it would be best to create a class file and add a public property called splashObj and then just use stone.splashObj = new StoneSplash();
MovieClip(stone).stoneSplash = stoneSplash; //so on the end event we can read this var to remove stoneSplash
octopusContainer.addChild(stoneSplash);
stoneSplash.x = stone.x;
stoneSplash.y = stone.y;
}
function removeStone(e:Event):void {
var stone:Stone = e.currentTarget as Stone;
octopusContainer.removeChild(MovieClip(stone).stoneSplash);
addOctopus(stone.x, stone.y);
removeChild(stone);
}

How can I turn an AS2 snow effect into a AS3 snow effect?

So I manage to find a snow effect that I like and wanted to use it but I realized it was in AS2 and I need it to be in AS3. Since there isn't a small difference between AS2 and AS3 I'm here to find some in these matter.
As you can see in the fla provided I also want to control the wind and speed by buttons.
Here is a link to the AS2 snow effect: http://www.freeactionscript.com/download/realistic-snow-fall-snowflake-effect.zip
This is the code in AS2:
//settings
var speed:Number = 2;
var wind:Number = -2;
var movieWidth:Number = 550;
var movieHeight:Number = 400;
createSnow(_root, 100);
function createSnow(container:MovieClip, numberOfFlakes:Number):Void
{
//run a for loop based on numberOfFlakes
for (var i = 0; i < numberOfFlakes; i++)
{
//set temporary variable and attach snowflake to it from the library
var tempFlake:MovieClip = container.attachMovie("snow_mc", "snow"+container.getNextHighestDepth(), container.getNextHighestDepth());
//variables that will modify the falling snow
tempFlake.r = 1+Math.random()*speed;
tempFlake.k = -Math.PI+Math.random()*Math.PI;
tempFlake.rad = 0;
//giving each snowflake unique characteristics
var randomScale:Number = random(50)+50;
tempFlake._xscale = randomScale;
tempFlake._yscale = randomScale
tempFlake._alpha = random(100)+50;
tempFlake._x = random(movieWidth);
tempFlake._y = random(movieHeight);
//give the flake an onEnterFrame function to constantly update its properties
tempFlake.onEnterFrame = function()
{
//update flake position
this.rad += (this.k / 180) * Math.PI;
this._x -= Math.cos(this.rad)+wind;
this._y += speed;
//if flake out of bounds, move to other side of screen
if (this._y >= movieHeight) {
this._y = -5;
}
if (this._x >= movieWidth)
{
this._x = 1
}
if (this._x <= 0)
{
this._x = movieWidth - 1;
}
}
}
}
//buttons
//wind
left_btn.onRelease = function()
{
wind = 2;
}
none_btn.onRelease = function()
{
wind = 0;
}
right_btn.onRelease = function()
{
wind = -2;
}
//speed
slow_btn.onRelease = function()
{
speed = .5;
}
normal_btn.onRelease = function()
{
speed = 1
}
fast_btn.onRelease = function()
{
speed = 3
}
It's going to be really quite similar.
The first thing would be, instead of:
var tempFlake:MovieClip = container.attachMovie("snow_mc", "snow"+...
you want something like:
var tempFlake = new snow_mc();
container.addChild(tempFlake);
Then convert all the property names such as _x etc to their AS3 equivalents (no underscore, scaleX in place f _xscale etc), Math.random() * 50 in place of random(50).
Replace all onRelease with addEventListener(MouseEvent.CLICK, function() {})
Finally instead of tempFlake.onEnterFrame you'll need one frame loop something like:
function onFrame(event: Event): void {
foreach(var child: MovieClip in container) {
child.rad += ... etc
}
}
addEventListener(Event.ENTER_FRAME, onFrame);
These steps should be sufficient to get it working as AS3. Once it's running, you could go further to make it more AS3 by creating a SnowFlake class that encapsulates all the properties and updates for one snowflake.

Odd numbers of object extraction from an array

I'm trying to get 2 objects at a time form the array for now. but soon I will be using odd number of length and splicing items.
This works out perfectly so far with Even numbers in the Array, but I am not sure how to make it work with odd numbers. The way I think it may work is ask it to check the objects coming up next and if it is less than 2 than change the counters to 1. but I am not even sure how to put that in code specifically. I posted my code so far be
import flash.events.MouseEvent;
import flash.net.Socket;
var socket_Array_current_position = 0;
var socket_counter = 2;
var socket_Array: Array = new Array ();
socket_Array.push(socket_one, socket_two,socket_three, socket_four, socket_five, socket_six);
go_next_left.addEventListener(MouseEvent.CLICK, go_left);
go_next_right.addEventListener(MouseEvent.CLICK, go_right);
function go_left(going_left:MouseEvent)
{
if (socket_Array_current_position > 0)
{
socket_remove();
socket_Array_current_position -= socket_counter;
socket_x_position = 125;
socket_display();
}
}
function go_right(going_right:MouseEvent)
{
if (socket_Array_current_position < socket_Array.length-socket_counter)
{
socket_remove();
socket_Array_current_position += socket_counter;
socket_x_position = 125;
socket_display();
}
}
socket_display();
function socket_display()
{
var s = 0;
for (s; s < socket_counter; s++)
{
addChild(socket_Array[socket_Array_current_position + s]);
socket_Array[socket_Array_current_position + s].x = socket_x_position;
socket_Array[socket_Array_current_position + s].y = socket_y_position;
//socket_Array[s].addEventListener(MouseEvent.CLICK, picked);
socket_x_position = socket_x_position + 275;
}
}
function socket_remove()
{
var s = 0;
for (s; s < socket_counter; s++)
{
removeChild(socket_Array[socket_Array_current_position+s]);
}
}
I suppose that you want display X objects (in this case two) at a time from an array. Whatever length. I'm using Math lib. Consider that I didn't try the code below with sdk or Flash.
const X_START_POS:int = 125;
const COLUMN_WIDTH:int = 275;
const QTY_SCREEN:int = 2;
var socket_Array:Array = new Array();
var socket_Array_pos:int = 0;
var socket_Array_target:int = 0; // target is always right
var socket_Array_on_screen:Array = new Array();
// socket_Array.length must be >= QTY_SCREEN, always.
socket_Array.push(socket_one, socket_two, socket_three, socket_four, socket_five, socket_six);
go_next_left.addEventListener(MouseEvent.CLICK, go_left);
go_next_right.addEventListener(MouseEvent.CLICK, go_right);
socket_display();
function go_left(going_left:MouseEvent) {
socket_Array_target = Math.max(socket_Array_pos - QTY_SCREEN, 0);
socket_display();
}
function go_right(going_right:MouseEvent) {
socket_Array_target = Math.min(socket_Array_pos + QTY_SCREEN, socket_Array.length - QTY_SCREEN);
socket_display();
}
function socket_display() {
socket_remove();
socket_x_position = X_START_POS;
var limit:int = socket_Array_target + QTY_SCREEN;
for (var i = socket_Array_target; i < limit; i++) {
show_socket(socket_Array[i]);
socket_x_position += COLUMN_WIDTH;
}
socket_Array_pos = socket_Array_target;
}
function show_socket(asocket:DisplayObject) {
addChild(asocket);
asocket.x = socket_x_position;
asocket.y = socket_y_position;
socket_Array_on_screen.push(asocket); // remember me
}
function socket_remove() {
var qty:int = socket_Array_on_screen.length;
for (var s = 0; s < qty; s++) {
removeChild(socket_Array_on_screen.pop());
}
}

Filling open spaces in a grid top down

I am writing a match three engine and I succeed in creating the matching with using huge loops to find the matching items. Any ideas on how to fill the empty spaces with the items ( dropping down into the empty spaces ) and creating new items without excessive looping and if statements?
Here is my relavant code so far.
public var rows:uint = 8;
public var cols:uint = 7;
public var cell:Array = new Array();
public var plot:Array = new Array();
public var height:int;
public var width:int;
public var relativePositions:Array = [{name:'top', position:-1}, {name:'bottom', position:1}, {name:'left', position:rows*-1}, {name:'right', position:rows*1}];
public var dictionary:Dictionary = new Dictionary();
public var matches:Array = new Array();
public function createGrid(target:*, displayObject:*, spacer:int) : void {
var iterator:uint = 0;
for(var c:uint = 0;c<cols;c++){
for(var r:uint = 0;r<rows;r++){
cell[iterator] = createGamePiece();
Sprite(cell[iterator]).name = String(iterator);
Sprite(cell[iterator]).addEventListener(MouseEvent.CLICK, _handleGamePiece_CLICK);
Sprite(cell[iterator]).addEventListener(MouseEvent.MOUSE_OVER, _handleGamePiece_MOUSE_OVER);
Sprite(cell[iterator]).addEventListener(MouseEvent.MOUSE_OUT, _handleGamePiece_MOUSE_OUT);
cell[iterator].y = cell[iterator].height * r + (spacer*r);
cell[iterator].x = cell[iterator].width * c + (spacer*c);
GamePiece(cell[iterator]).positionX = cell[iterator].x;
GamePiece(cell[iterator]).positionY = cell[iterator].y;
GamePiece(cell[iterator]).positionRow = r;
GamePiece(cell[iterator]).positionCol = c;
target.addChild(cell[iterator]);
dictionary[String(iterator)] = cell[iterator]
iterator++
}
}
}
public function findRelativeMatches(targetSprite:Sprite) : void {
targetSprite.alpha = .5;
var rootPosition:Number = Number(targetSprite.name);
for ( var i:int = 0; i < relativePositions.length; i ++ ) {
var key:String = String(rootPosition + relativePositions[i].position);
// to do >> Not hardcoded to 'Pig'
if (findSprite(key) != null && GamePiece(targetSprite).color == GamePiece(findSprite(key)).color && GamePiece(findSprite(key)).found == false) {
var sprite:Sprite = findSprite(key);
sprite.alpha = .5;
GamePiece(sprite).found = true;
matches.push(sprite);
findRelativeMatches(sprite);
};
};
targetSprite.addEventListener(MouseEvent.MOUSE_OUT, function() : void {
if ( matches.length != 0 ) {
for ( var j:int = 0 ; j < matches.length ; j++ ) {
Sprite(matches[j]).alpha = 1;
GamePiece(matches[j]).found = false;
}
matches.splice(0);
}
});
}
public function findSprite(key:String) : Sprite {
var sprite:Sprite;
dictionary[key] != undefined ? sprite = dictionary[key] : null;
return sprite;
}
protected function _handleGamePiece_CLICK(event:MouseEvent):void
{
for ( var j:int = 0 ; j < matches.length ; j++ ) {
var sprite:Sprite = matches[j];
view.removeChild(matches[j]);
}
matches.splice(0);
}
public function createGamePiece() : Sprite {
var gamePiece:GamePiece = new GamePiece();
return gamePiece;
}
You want to collapse your grid downwards, right? The common algorithm is going from the bottom of every row upwards, having one index of the first empty space found, and the other for the first occupied space above empty space, then exchange those values once found, iterating to the top. But you don't store the grid in any accessible form! You should create a grid object, say a vector of vectors of sprites, and assign values in it as you move pieces around. Like this:
var GRID:Vector.<Vector.<Sprite>>; // this should be allocated at createGrid
// populate GRID with your sprites once generated:
// put the following into your inner loop in CreateGrid:
GRID[r][c]=cell[iterator];
// and the following into your removal of matches[] sprites:
GRID[GamePiece(sprite).positionRow][GamePiece(sprite).positionCol]=null; // release link from GRID
// now to move grid objects:
function DropAll():void {
var i:int;
var j:int;
for (i=GRID.length-1;i>=0;i--) {
var lastEmpty:int=-1;
for (j=GRID[i].length-1;j>=0;j--) {
if (GRID[i][j]) {
if (lastEmpty>0) {
GRID[i][lastEmpty--]=GRID[i][j];
// relocate your sprite properly here
GRID[i][j]=null;
} // else we're still at full part of grid, continue
} else if (lastEmpty<0) lastEmpty=j;
}
}
}
To properly instantiate GRID you need to allocate vectors of desired length that are filled with "null" values. Also, "GRID" itself is a Vector, and needs to be instantiated too.
GRID=new Vector.<Vector.<Sprite>>();
for (i=0;i<rows;i++) {
var a:Vector.<Sprite>=new Vector.<Sprite>(cols);
GRID.push(a);
}
After you do this, you fill the GRID by directly assigning links in it, like GRID[r][c]=gameObject;
This is actually what I wanted. A way to collapse WITHOUT iterating over the entire board. This way I JUST loop through the items that have been removed.
protected function _handleGamePiece_CLICK(event:MouseEvent):void
{
for ( var j:int = 0 ; j < matches.length ; j++ ) {
var oldSprite:Sprite = matches[j];
moveAllPiecesDown(oldSprite);
view.removeChild(oldSprite);
oldSprite = null;
}
matches.splice(0);
}
private function moveAllPiecesDown(oldSprite:Sprite):void
{
var piecesAbove:int = GamePiece(oldSprite).positionRow;
var index:int = int(oldSprite.name);
for( var i:int = 0; i < piecesAbove; i ++ ) {
var spriteAbove:Sprite = Sprite(view.getChildByName(String(index-(1+i))));
if(spriteAbove) {
spriteAbove.y = spriteAbove.y + spriteAbove.height + 1;
spriteAbove.name = String(Number(spriteAbove.name)+1);
GamePiece(spriteAbove).textField.text = spriteAbove.name;
delete dictionary[spriteAbove.name];
dictionary[spriteAbove.name] = spriteAbove;
}
}
}