I have a large number of objects (472) that require a timer used to check how long it takes between state changes of a variable. The code below is what I have so far but that many timers running definitely impacts the performance of the application, is there a better optimised way of measuring this?
import flash.utils.Timer;
var active_:Boolean;
var matched:Boolean;
var vacated:Boolean;
var circ:Shape=new Shape();
this.addChild(circ);
circ.x = 0;
circ.y = 0;
var circRad:Number = 5;
var mat= new Matrix();
var busyColors = [0xFFFF00,0xFFCC00];
var idleColors = [0xCCCCCC,0x000000];
var matchedColors = [0x0099FF,0x0066FF];
var vacatedColors = [0xFF0000,0x990000];
var busyAlphas = [1,1];
var idleAlphas = [0.5,0.5];
var ratios = [0,255];
var prev:int = 0;
var time:Timer = new Timer(1000,0);
//time.start();
mat.createGradientBox(2*circRad,2*circRad,0,-circRad,-circRad);
circ.graphics.lineStyle();
if (active_ == false)
{
if (prev != 0)
{
setAverage(prev,false);
prev = 0;
}
circ.graphics.clear();
circ.graphics.beginGradientFill(GradientType.RADIAL,idleColors,idleAlphas,ratios,mat);
circ.graphics.drawCircle(0,0,circRad);
circ.graphics.endFill();
}
else if (active_ == true && matched == true)
{
if (prev != 1)
{
setAverage(prev,true);
prev = 1;
}
circ.graphics.clear();
circ.graphics.beginGradientFill(GradientType.RADIAL,matchedColors,busyAlphas,ratios,mat);
circ.graphics.drawCircle(0,0,circRad);
circ.graphics.endFill();
}
else if (active_ == true && vacated == false && matched == false)
{
if (prev != 2)
{
setAverage(prev,true);
prev = 2;
}
circ.graphics.clear();
circ.graphics.beginGradientFill(GradientType.RADIAL,busyColors,busyAlphas,ratios,mat);
circ.graphics.drawCircle(0,0,circRad);
circ.graphics.endFill();
}
else if (active_ == true && vacated == true)
{
if (prev != 3)
{
setAverage(prev,true);
prev = 3;
}
circ.graphics.clear();
circ.graphics.beginGradientFill(GradientType.RADIAL,vacatedColors,busyAlphas,ratios,mat);
circ.graphics.drawCircle(0,0,circRad);
circ.graphics.endFill();
}
function setAverage(i:int, a:Boolean)
{
time.stop();
switch (i)
{
case 0 :
break;
case 1 :
MovieClip(root).avgMat.push(uint(time.currentCount));
break;
case 2 :
MovieClip(root).avgBusy.push(uint(time.currentCount));
break;
case 3 :
MovieClip(root).avgVac.push(uint(time.currentCount));
break;
}
if(a == true){
//time.reset();
//time.start();
}
}
Commented out the timer starts because of the issue.
You can make public function like "tick()" at yours objects and call it in loop from 1 general timer outside.
Consider doing the following:
in the top, import getTimer
import flash.utils.Timer;
Create a variable containing the starting time (instead of doing timer.start) do this:
var timerStart : int = getTimer();
and instead of doing this: time.currentCount
Simply do this:
(getTimer() - timerStart) / 1000
Good luck.
you could change all the timers with addEventListener of type EVENT FRAME
var counter:Number = 0;
addEventListener(Event.ENTER_FRAME, counting);
function counting (e:Event):void
{
counter++
}
So counter will increment on every frame. Now you have to find fps. Now multiply fps by time that you want timer to run after. So it's should look like this:
var counter:Number = 0;
addEventListener(Event.ENTER_FRAME, counting);
function counting (e:Event):void
{
counter++
if(counter == 60) //60 fps means this line will run after 1 sec.
{
//do something
}
If(counter == 120) //60 fps; 2 sec
{
//do something
}
}
This method is much more accurate and the system will not be so overloaded. This can especcially help you for setting up the stage using addChild, when you need a delay after the animation is started.
You can make more of this 'timers', but that depends on the demands of the game, but i'm sure that it will work faster than hundreths of timers.
Related
I created some MovieClips and put all of them on the stage. I'm trying to move a random MovieClip, using it only once, but at the same time I need to know which one it is because it will conflict with other tween effects. Is there an easy way to do that?
Here is what I've tried:
var biryukseklev1, ikiyukseklev1, ucyukseklev1, dortyukseklev1, besyukseklev1:int;
var assignavalue1, assignavalue2:int;
stage.addEventListener(Event.ENTER_FRAME,survivordondurlev6);
function survivordondurlev6(e:Event) {
if (biryerlessurlev1 == 0) {
biryukseklev1 = 764;//36
} else if (biryerlessurlev1 == 1) {
biryukseklev1 = 680;//120
} else if (biryerlessurlev1 == 2) {
biryukseklev1 = 596;//204
} else if (biryerlessurlev1 == 3) {
biryukseklev1 = 512;//288
} else if (biryerlessurlev1 == 4) {
biryukseklev1 = 428;//372
}
if (assignavalue1 == 0 && rabbitstatus.text.length < 2) { //Make sure to not used before
var my:Tween = new Tween(rabbit, "y", Back.easeInOut, rabbit.y, rabbit.y -biryukseklev1, 3, true);
rabbitstatus.text = "okfull";
} else if((assignavalue1 == 1 && birdstatus.text.length < 2){ //Make sure to not used before
var mys:Tween = new Tween(bird, "y", Back.easeInOut, bird.y, bird.y -biryukseklev1, 3, true);
birdstatus.text = "okfull";
}
}
If you want to keep track of which clips you've animated, then you'll have to have some way of iterating through them. The easiest list would be an Array, though how you mark them as "animated" is up to you. If you never plan on animating them again, then simply removing the ones that you've animated, and leaving the rest for later will work.
Below is example code, which you should be able to run in a new scene. You should be able to apply it to your needs with some small edits.
import fl.transitions.Tween;
import fl.transitions.easing.*;
// We'll keep track of which blocks still need to be animated by including them in this list
// Once we animate a block, we remove it from the list.
var pool:Array = [];
// Populate the stage with blocks.
for (var i:int = 0; i < 30; i++) {
var block:Sprite = createBlock();
addChild(block);
block.y = 50;
if (i > 0) { block.x = block.width * i; }
pool.push(block);
}
// Create an interactive button.
var txt:TextField = new TextField();
txt.text = "Do something";
addChild(txt);
txt.addEventListener("click", animateNext);
function createBlock():Sprite {
// Creates a single, randomly colored block.
var block:Sprite = new Sprite();
block.graphics.beginFill(random(0x000000, 0xFFFFFF));
block.graphics.drawRect(0,0,16,16);
block.graphics.endFill();
return block;
}
function random(low:Number, high:Number):Number {
// Returns a random number between the low and high numbers.
return Math.floor(Math.random() * (1+high-low)) + low;
}
function animateNext(e:Event):void {
// Do this 5 times.
for (var i:int = 0; i < 5; i++) {
// As long as we have blocks in the pool...
if (pool.length > 0) {
// Pick a random block from the pool
var index:int = random(0, pool.length-1);
// Animate it
new Tween(pool[index], "y", Back.easeInOut, pool[index].y, pool[index].y + 50, 3, true);
// Remove it from the pool so it isn't picked again
pool.splice(index, 1);
} else {
// Otherwise, break the loop.
break;
}
}
}
Using Away3D...I have this array of cubes I generated. This array of cubes are in a "Sector" that contains 50x50x50 cubes. The only thing the sector contains is the cubes coordinates and color. They are stored inside the Cube class. I only want to render the ones that touch air (currently "air" the "color" 0xFFFFFF)
I've tried this...an interesting number of ways...
My current method (slowest) was to make vector3D points for each object, and then use indexOf on a set of Vector3Ds containing the points of all the cubes in my "Sector".
public function renderSector(sector:Sector):void
{
trace("init sector render..");
allCubes = sector.cubes;
//Render only things touching air.
for each (var cube:Cube in sector.cubes)
{
//If the cube is not an air block
if (cube.color != 0xFFFFFF)
{
var topEstimate:Vector3D = new Vector3D(cube.x + 1, cube.y, cube.z);
var bottomEstimate:Vector3D = new Vector3D(cube.x, cube.y +1, cube.z);
var leftEstimate:Vector3D = new Vector3D(cube.x + 1, cube.y, cube.z +1);
var rightEstimate:Vector3D = new Vector3D(cube.x - 1, cube.y, cube.z);
var frontEstimate:Vector3D = new Vector3D(cube.x, cube.y -1, cube.z);
var backEstimate:Vector3D = new Vector3D(cube.x, cube.y, cube.z - 1);
//If the cube is next to an air block
if (checkForAir(topEstimate) || checkForAir(bottomEstimate) || checkForAir(leftEstimate) || checkForAir(rightEstimate) || checkForAir(frontEstimate) || checkForAir(backEstimate))
{
var meshCube:Mesh = new Mesh(new CubeGeometry(10, 10, 10), new ColorMaterial(cube.color));
meshCube.x = (sector.x * 125000) + (10 * cube.x);
meshCube.y = (sector.y * 125000) + (10 * cube.y);
meshCube.z = (sector.z * 125000) + (10 * cube.z);
trace("This cube touches air..rendering");
viewport.scene.addChild(meshCube);
}
}
}
}
private function checkForAir(point:Vector3D):Boolean
{
var returnValue:Boolean = new Boolean(false);
var index:int = allCubes.indexOf(point);
if (index > -1)
{
if (allCubes[index].color == 0xFFFFFF)
{
returnValue = true;
}
}
return returnValue;
}
Nothing happens. I get no cubes (letting it run for about 2 minutes) that have an air block next to them using a 3DVevtor. So, I try iterating through all my cubes again while fetching a list of cubes that are "next" to my current cube. I do this by comparing each cube to each other vs a stored 3DVector in my Sector class.
public function renderSector(sector:Sector):void
{
//Render only things touching air.
for each (var cube:Cube in sector.cubes)
{
//If the cube is next to an air block and is not an air block, render it.
if (cube.color != 0xFFFFFF)
{
var touchesAir:Boolean = new Boolean(false);
//Search touching cubes
var touchingCubes:Vector.<Cube> = new Vector.<Cube>();
for each (var possibleCube:Cube in sector.cubes)
{
if ((possibleCube.x == cube.x + 1 && possibleCube.y == cube.y && possibleCube.z == cube.z) ||
(possibleCube.y == cube.y + 1 && possibleCube.x == cube.x && possibleCube.z == cube.z) ||
(possibleCube.z == cube.z + 1 && possibleCube.x == cube.x && possibleCube.y == cube.y) ||
(possibleCube.x == cube.x - 1 && possibleCube.y == cube.y && possibleCube.z == cube.z) ||
(possibleCube.y == cube.y - 1 && possibleCube.x == cube.x && possibleCube.z == cube.z) ||
(possibleCube.z == cube.z - 1 && possibleCube.x == cube.x && possibleCube.y == cube.y))
{
touchingCubes.push(possibleCube);
}
}
for each (var touchingCube:Cube in touchingCubes)
{
if (touchingCube.color == 0xFFFFFF)
{
touchesAir = true;
}
}
if (touchesAir)
{
var meshCube:Mesh = new Mesh(new CubeGeometry(10, 10, 10), new ColorMaterial(cube.color));
meshCube.x = (sector.x * 125000) + (10 * cube.x);
meshCube.y = (sector.y * 125000) + (10 * cube.y);
meshCube.z = (sector.z * 125000) + (10 * cube.z);
trace("This cube touches air..rendering");
viewport.scene.addChild(meshCube);
}
}
}
It works..but it takes about 15 seconds for it to find one....The current spec of the Sector is a plane of 50x25x50 grass colored blocks. So this would take a while..
My first method (and oh man was this about an hour+ of brainstorming back) was to fetch the positions of each cube that [i]would[/i] be next to my main cube by basing it on the render order in my world generator function. [Seen below]
public static function generateSector(type:String, position:Vector3D):Sector
{
var returnSector:Sector;
var grassArray:Vector.<uint> = new Vector.<uint>();
grassArray.push(new uint(0x56b000));
grassArray.push(new uint(0x63c900));
grassArray.push(new uint(0x6fe300));
grassArray.push(new uint(0x7cfc00));
//Current types...grass field
switch(type)
{
case "grass":
var cubeArray:Vector.<Cube> = new Vector.<Cube>();
for (var x:int = 0; x < 50; x++) //Moving right
{
for (var z:int = 0; z < 50; z++) //Headed out.
{
for (var y:int = 0; y < 50; y++) //From bottom up.
{
if (y < 25)
{
var color:uint = grassArray[Math.floor(Math.random() * 4)];
}
else
{
var color:uint = 0xFFFFFF;
}
cubeArray.push(new Cube(x,y,z,color));
}
}
}
returnSector = new Sector(position.x, position.y, position.z, cubeArray);
break;
}
return returnSector;
}
Y building first (bottom to top)
then X
then Z
So, simple right? Based on the order of the cubes, I should be able to just pull, for example, the cube on top of my current cube by adding 1 to the index of my current cube, right? (Getting the other cubes respectively based on their order of course and catching errors for any cubes that would be outside of my 50x50x50 grid)
public function renderSector(sector:Sector):void
{
//Render only things touching air.
var counter:int = 0;
for each (var cube:Cube in sector.cubes)
{
//If the cube is next to an air block and is not an air block, render it.
if (cube.color != 0xFFFFFF)
{
var touchesAir:Boolean = new Boolean(false);
try
{
var topCube:Cube = sector.cubes[counter + 1];
if (topCube.color == 0xFFFFFF)
{
touchesAir == true;
}
}
catch(rangeError:RangeError)
{
}
//-------
try
{
var bottomCube:Cube = sector.cubes[counter - 1];
if (bottomCube.color == 0xFFFFFF)
{
touchesAir = true;
}
}
catch (rangeError:RangeError)
{
}
//-------
try
{
var leftCube:Cube = sector.cubes[counter - (50 * 50)];
if (leftCube.color == 0xFFFFFF)
{
touchesAir = true;
}
}
catch (rangeError:RangeError)
{
}
//-------
try
{
var rightCube:Cube = sector.cubes[(50 * 50) + counter];
if (rightCube.color == 0xFFFFFF)
{
touchesAir = true;
}
}
catch (rangeError:RangeError)
{
}
//-------
try
{
var frontCube:Cube = sector.cubes[counter - 50];
if (frontCube.color == 0xFFFFFF)
{
touchesAir = true;
}
}
catch (rangeError:RangeError)
{
}
//-------
try
{
var backCube:Cube = sector.cubes[counter + 50];
if (backCube.color == 0xFFFFFF)
{
touchesAir = true;
}
}
catch (rangeError:RangeError)
{
}
if (touchesAir)
{
var meshCube:Mesh = new Mesh(new CubeGeometry(10, 10, 10), new ColorMaterial(cube.color));
meshCube.x = (sector.x * 125000) + (10 * cube.x);
meshCube.y = (sector.y * 125000) + (10 * cube.y);
meshCube.z = (sector.z * 125000) + (10 * cube.z);
trace("This cube touches air..rendering");
viewport.scene.addChild(meshCube);
}
}
}
}
This one renders in about 4 seconds! Though, no cubes actually appear on screen...and the trace statement never fires. I have had no luck finding out why.
TL;DR Let's say you have a grid of cubes. How do you only render the ones that are out in the open?
Or (great alternative) only render mesh's that you can "see". (I need the meshs not merged because I have to have listeners on them to remove them or add new meshes when clicked next to or ontop of them)
You know those moments when you forget to sleep and then you forget to add counters to your for each loops since you're referencing a counter inside of it?
counter = sector.cubes.indexOf(cube);
Need to make sure that the counter (which I should rename indexItem) matched the index of the current cube that I was running through inside my conditional that checks to see if the cube is "air" or not.
Of course, now I'm getting a resource limit error but this can be fixed by reducing the sector size and combining a few meshes.
[Fault] exception, information=Error: Error #3691: Resource limit for this resource type exceeded.
Please do forgive me if this question is very stupid, but I couldn't figure out what to do, which is why I ask it.
Here, I declared a small white square as a movieclip symbol(Dot) and I wish to generate it after a specific gap on the entire screen.
So, when I execute this (test it) code on Flash CS6, it hangs. After that I will be forced to end the program without doing anything further.
import flash.ui.*;
stop();
Mouse.hide();
var ctX:int = 0,ctY:int = 0,done:Boolean = false;
var base:Object = MovieClip(root);
this.addEventListener(Event.ENTER_FRAME, eFrame);
function eFrame(event:Event):void
{
while (done == false)
{
var dots:Dot = new Dot ;
dots.x += (50 * ctX);
dots.y += (50 * ctY);
ctX++;
if (ctX == 11)
{
ctX = 0;
ctY++;
}
else if (ctX == 11 && ctY == 10)
{
done = true;
break;
}
stage.addChild(dots);
}
}
Thank you in advance.
I have attached a screenshot of the situation.
The loop will never finish because the condition for done=true is ctX==11, but ctX==11 causes ctX=0 in the first condition:
if (ctX == 11) // when ctX is 11
{
ctX = 0; // ctX is reset to 0
ctY++;
}
else if (ctX == 11 && ctY == 10) // so you will never have ctX=11 here
{
done = true;
break; // (Tip: you don't need `done` since `break` exits the loop)
}
You could fix this by swapping the conditions, but I think this use of a while loop is unnecessarily complex and fragile. Why not just use two for loops:
for (var ctX:int = 0; ctX < 11; ctX++) {
for (var ctY:int = 0; ctY < 11; ctY++) {
var dots:Dot = new Dot();
dots.x = (50 * ctX);
dots.y = (50 * ctY);
stage.addChild(dots);
}
}
This is much clearer and less fragile because the loops are fixed length.
You could even do it with one for loop and a little math, but you lose some clarity:
for (var i:int = 0; i < 11 * 11; i++) {
var dots:Dot = new Dot();
dots.x = (50 * (i % 11));
dots.y = (50 * int(i / 11));
stage.addChild(dots);
}
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);
}
}
}
}
Had a look for this but nothing seemed clear at the moment I have a script which will only play if the frame is the currentFrameLabel or rewind.
However in order for it not to go one frame too far I need to be able to stop it on the frame before the change not on the change.
Or am I just going about this the wrong way?
For example:
Frame 10 Label: Up
Frame 12-36 Label: Idle Loop
Frame 37 Label: Hand Up
I need it to only play from frames 12 to 36 but at the moment it plays from frames 12-37.
var reverse:Boolean = false;
var robotlabel:String = 'Up/Down';
what.addEventListener(MouseEvent.MOUSE_OVER, botAction);
what.addEventListener(MouseEvent.MOUSE_OUT, botAction2);
function botAction(evt:MouseEvent):void{
reverse = false;
robotlabel = 'Hand up/Down';
robot.gotoAndPlay('Hand up/Down');
robot.addEventListener(Event.ENTER_FRAME,run);
}
function botAction2(evt:MouseEvent):void{
reverse = true;
robot.prevFrame();
}
function run(e:Event):void{
trace("label:" + robotlabel);
trace("current" + robot.currentFrameLabel);
if(robot.currentFrameLabel != robotlabel && robot.currentFrameLabel != null){
trace("stoooooppppp");
robot.stop();
}
if(reverse == true && currentFrameLabel==robotlabel){
robot.prevFrame();
trace("reversing!");
}else if(reverse == false && (currentFrameLabel==robotlabel || robot.currentFrameLabel == null)){
robot.nextFrame();
}else{
trace("destroy");
reverse = false;
robot.stop();
robot.removeEventListener(Event.ENTER_FRAME,run);
}
}
There is not a "nextFrameLabel" property as such in as3, however you can get an array of all the frame labels and numbers in your target movieclip using the currentLabel property of the MovieClip and work it out from there, since you know the currentFrame at all times.
Quick example from the docs:
import flash.display.FrameLabel;
var labels:Array = mc1.currentLabels;
for (var i:uint = 0; i < labels.length; i++)
{
var label:FrameLabel = labels[i];
trace("frame " + label.frame + ": " + label.name);
}
In case this is useful to anybody else I solved it by looping through each frame and then storing the corresponding label to the frame so it can later be checked against.
for (i = 1; i < robot.totalFrames; i++)
{
for (var n:uint = 0; n < this.robolabels.length; n++)
{
if(this.robolabels[n].frame == i){
newLabel = this.robolabels[n].name
}
}
this.roboframes[i] = newLabel;
}