Help turning multiple functions into a a few functions.
var myNumberOne = 10;
var myNumberTwo = 100;
function one would create a timer or interval that fire 2 times every second, Traceing the word "food", It would when finished, "myNumberOne += 10" making myNumberOne = 20;
function two would create a timer or interval that fire 5 times every half-second, Traceing the word "ben", It would when finished, "myNumberTwo += 50" making myNumberTwo = 250;
For two functions this is fine, but if I have 100s of possible combinations, I cannot think on this should be done, without intervals , timers, functions etc... interfering with each-other, and passing arguments through time.
Thanks for any help.
for clarification: I waant to call a function like this
setTimeFunction("myTimeOne", myNumberOne, 2,1000,10, "ben");
setTimeFunction("myTimeTwo", myNumberTwo, 5,500,50,"food");
Well, first, you need to compose a generic method that would perform a number of similar actions. Tracing is easy, but you cannot pass a variable to change directly because you'll pass a value, not a reference to variable. In order to do as you want you need to pass it as a pair "container object" and "variable name" to use the square bracket notation.
function myownDothings(target:Object, varname:String, adiff:int, totrace:String):void
{
// Use square bracket notation to change the targeted variable.
target[varname] += adiff;
// Trace the given argument.
trace(totrace);
}
Ok, now the simple complicated part. There's a setTimeout(...) function that calls the given method many times with a given timeout, but it's official documentation officially advises the use of Timer class.
I hope you know how to work with classes, because the thing you want calls for OOP and fitting it into the frame scripts will result in something ugly. So, you need to compose a class that remembers function to call, timeout settings and a bunch of arguments as well.
package
{
import flash.utils.Timer;
import flash.events.TimerEvent;
public class Ticker
{
// You need to keep the references to the things you use,
// or else Garbage Collector might think you don't need it.
static private var list:Array = new Array;
// Instead of static method you can use the "constructor" way,
// but I find it more stylish and it's one more thing for
// you to google and learn of, which I totally approve.
// The ... construction allows to pass a random number
// of arguments (after fixed arguments) as an Array.
static public function create(handler:Function, timeout:int, ...args:Array):void
{
var aTicker:Ticker;
// Brackets () are not mandatory with the "new" operator
// if there are no mandatory constructor arguments.
aTicker = new Ticker;
// Store all the necessary data in the new instance. That's the
// point of OOP scripting here: you want to make 100 different
// tickers and you need each of them to keep some custom data.
aTicker.timeout = timeout;
aTicker.handler = handler;
aTicker.args = args;
// Finally, run the ticker.
aTicker.start();
// Store the created instance into the keeper list
// to prevent Garbage Collector from destroying it.
list.push(aTicker);
}
// Again, fear the Garbage Collector.
private var clock:Timer;
// Keep in mind that timeout is not exactly accurate
// as it aligns to the SWF's frame rate. Setting it up to call
// more times a second than FPS will pose to be a meaningless act.
private var timeout:int;
// The reference to the method to call.
private var handler:Function;
// The list of arguments to pass to the method above.
private var args:Array;
// This method is called from the "create" method
// to finalize things and start ticking.
private function start():void
{
// Create a Timer instance with a given timeout.
clock = new Timer(timeout);
// Subscribe the listener to the Timer.
clock.addEventListener(TimerEvent.TIMER, onTick);
// Start the Timer.
clock.start();
}
// The Timer instance will trigger this method
// (approximately) every given timeout of milliseconds.
private function onTick(e:TimerEvent):void
{
// Now the idea is to call the given method
// passing the list of given arguments to it.
// Normally you don't need to pass the "this" object
// to a method unless you use unnamed unbound closures.
// (which I personally consider a heresy and don't recommend to use)
// So you just pass "null" as the first argument and everything is fine.
handler.apply(null, args);
}
}
}
Now, the usage. It's where all the horrors above finally shine.
import Ticker;
var myNumberOne = 10;
var myNumberTwo = 100;
// Fire 2 times every second, increase "myNumberOne" by 10, trace the word "ben".
// So, 2 times a second it will call: myownDothings(this, "myNumberOne", 10, "ben");
Ticker.create(myownDothings, 1000 / 2, this, "myNumberOne", 10, "ben");
// Fire 5 times every half a second, increase "myNumberTwo" by 50, trace the word "food".
// So, 10 times a second it will call: myownDothings(this, "myNumberTwo", 50, "food");
Ticker.create(myownDothings, 500 / 5, this, "myNumberTwo", 50, "food");
Related
I have a function in Adobe Flex 4 (ActionScript 3) that accepts an object and returns an ArrayCollection...
If a certain global variable is set to true, I want the function to delay itself for 3 seconds before running. Otherwise I want the function to run as normal.
The problem is, if I use a Timer, that timer calls a separate function, and that function cannot return anything to my calling function, nor can the function it calls accept any parameters, so it's not like I can call my own function recursively after the TimerComplete event fires... And a recursive call wouldn't work anyway, because it would return the ArrayCollection to the timer-result function, not to the original calling function...
I need a delay within the function, not a delay that causes me to go outside that function. But I cannot figure out how to do it.
Something like this is what I need to do:
private function createArrayCollection(myObject:Object):ArrayCollection {
var myArrayCollection:ArrayCollection = new ArrayCollection();
if (globalWaitBoolean) {
//delay here for 3 seconds, somehow
}
//Here I do the stuff that uses the info in myObject to figure out what to
//put into the ArrayCollection I want to return
return (myArrayCollection);
}
So... Any ideas on how to accomplish this without calling an external Timer function that cannot return an object back to my original function?
Thanks,
The way you want it you will have your whole application to lag for 3 seconds, unresponsive to any user input and external events. But it is possible, sure:
import flash.utils.getTimer;
private function createArrayCollection(myObject:Object):ArrayCollection
{
var myArrayCollection:ArrayCollection = new ArrayCollection;
if (globalWaitBoolean)
{
var waitUntil:int = getTimer() + 3000;
// Method getTimer() returns time in ms passed since app start.
// So you just have to wait until it is greater than appointed time.
while (getTimer() < waitUntil)
{
// Do nothing.
}
}
return (myArrayCollection);
}
Still, if you want to do it in a correct way of doing it:
import flash.utils.setTimeout;
private function callerMethod():void
{
// Blah blah blah.
// ...
// Finally.
createArrayCollection(sourceData, asyncResult);
}
private function createArrayCollection(myObject:Object, handler:Function):void
{
var result:ArrayCollection = new ArrayCollection;
if (globalWaitBoolean) setTimeout(handler, 3000, result);
else handler(result);
}
private function asyncResult(source:ArrayCollection):void
{
// The rest of your processing code.
}
Normal (synchronous) code flow would not return until the value is prepared, so should you desire to actually wait for 3 seconds while not allowing your app to do anything, use getTimer() approach from #Organis's answer. If you'll go for an asynchronus result, you'll need to face and overcome some more problems.
First, when do you expect your returned ArrayCollection to actually arrive. Speaking of code design, asynchronous code requires a whole lot of assumptions, thread safety etc etc, and even while AS3/Flash does not have true multithreading unless you count Workers, the code flow with events is not as obvious. So, whoever called your createArrayCollection() MUST NOT expect value returned from it right away. So, speaking about your direct question, NO, you can't avoid timers of some sort if you desire a responsive application. But you can use them with an approach that would involve an indirectly returned result.
Second, whether there might be concurring requests for more array collections from objects if your app would require these - you have to prepare for any kind of interference that might be caused by this. Say your function is triggered by a button click - what if that button would get clicked more than once in 3 seconds?
Third, actual route to processing code is not direct with asynchronous return. You need either a callback, an event handler (which is essentially a semi-native callback), a code that periodically checks for value presence (enter frame handler, etc) or a similar trick to gather the value that's returned asynchronously, and then transfer it to any relevant code that would process it further. Therefore, you would need to design an interface capable of receiving complex data (source object forward, array collection backward) and then carefully test it against all the possible cases and flaws.
An example of implementing all that is very long, I'll try to outline it somehow. Ler's assume you have a sort of "server" class that accepts requests for data and processes it synchronously (no wait) or asynchronously (wait). It accepts a source object of type "T" and provides a newly created object of type ArrayCollection, supplied as a parameter to whatever callback function sent to it. Also it accepts a delay (a simple way to show sync/async return would be a boolean, but why not getting an int?) as a parameter, and guarantees (to the extent of event model limitations) that after this delay the callback will be called ASAP. The architecture will then look like this:
class Processor {
Dictionary requests; // here all the requests that are delayed will be stored
public function dpr(source:T,callback:Function,delay:int=0):void{...}
// creates requests and stores them
private function syncProcess(source:T):ArrayCollection {...}
// whatever routine you want to get variably delayed
private function processTimeout(e:Event=null):void {...}
// processes events from "setTimeout()" and calls callbacks
}
Note that asynchronous approach forced to create three more entities than a synchronous one. First is the request holding structure (the dictionary here), second is timeout event handler, third is whatever callback you'll desire to get called when the data is ready. The code flow would go like this:
Synchronous call would result in the callback directly called from within the class: request->processTimeout->syncProcess()->callback. Asynchronous call will have the callback called from within Timer::timerComplete event handler via setTimeout called within request, with data that originally came from request stored in requests.
You could use an embedded/inline function:
private function createArrayCollection(myObject:Object):ArrayCollection {
var myArrayCollection:ArrayCollection = new ArrayCollection();
if (globalWaitBoolean) {
var milliseconds:int = 3000;
//delay here for 3 seconds
setTimeout(function()
{
//Here I do the stuff that uses the info in myObject to figure out what to
//put into the ArrayCollection I want to return
return (myArrayCollection);
},
milliseconds);
}
else
{
//Here I do the stuff that uses the info in myObject to figure out what to
//put into the ArrayCollection I want to return
return (myArrayCollection);
}
}
The inner function will have access to all local vars of the outer function.
I've created a series of classes that can be used to generate and render images. I want to store a copy of the last frame displayed so I can mix it with the current frame to create a video sustain effect. A brief overview of the classes involved in this example:
MasterContainer: a subclass of Sprite used as the main display object. Generative classes are placed in the MasterContainer, and redrawn when the container is told to render
CustomWave: a subclass of Shape used to contain, draw, and manipulate a GraphicsPath object. One of the aforementioned 'generative classes'
My current attempt involves the use of two MasterContainer objects - one for the current frame, and one for the last frame. If I'm not mistaken, the current appearance of one MasterContainer (and its children) can be copied to the other with a command like lastMaster.graphics.copyFrom(master.graphics);. Consider the following code:
var time:Number;
var master:MasterContainer = new MasterContainer(); //current frame
var lastMaster:MasterContainer = new MasterContainer(); // last frame
var wave:CustomWave = new CustomWave(new <Number>[0,0,0,0],0xffffff,5); //generator for current frame
master.RegisterComponent(wave); //adds CustomWave and registers with the rendering loop
addChild(lastMaster); //add last frame to stage
addChild(master); //add current frame to stage
addEventListener(Event.ENTER_FRAME, perFrame);
function perFrame(event:Event):void{
time = 0.001 * getTimer();
lastMaster.graphics.copyFrom(master.graphics); //copy previous frame's graphics
UpdatePoints(); //update the path of the CustomWave
UpdateColor(); //update the color of the CustomWave
master.fireRenderCannon(); //redraw objects registered to master
}
This seems to work in theory, but as far as I can tell lastMaster ends up with no visible graphics content even though master renders as expected. I've tried several times to test whether this is the case, and am pretty convinced that that it is, but am newish to AS3 and am concerned I am overlooking something - the code looks like it should work. Does anyone have suggestions on how to test this properly? Are there obvious defects within this code that would cause lastMaster to be visually blank? Is there an better way of accomplishing my goal?
I think I'm in over my head on this... I would love any input. Thanks!
After you copied graphics, what do you try to do with it?
Method copyFrom works as clocks, without any problems. Isn't here logic bug in your code?
function perFrame(event:Event):void{
time = 0.001 * getTimer();
lastMaster.graphics.copyFrom(master.graphics); //Here
//master.graphics.copyFrom(lastMaster.graphics);
UpdatePoints();
UpdateColor();
master.fireRenderCannon();
}
Example of copyFrom, it works fine with any complexity of graphics:
var complex: Shape = new Shape();
adobeExample(complex.graphics);
var test2: Shape = new Shape();
test2.graphics.copyFrom(complex.graphics);
addChild(test2);
private function adobeExample(graphics: Graphics):void{
// define the line style
graphics.lineStyle(2,0x000000);
// define the fill
graphics.beginFill(0x666699);//set the color
// establish a new Vector object for the commands parameter
var star_commands:Vector.<int> = new Vector.<int>();
// use the Vector array push() method to add moveTo() and lineTo() values
// 1 moveTo command followed by 3 lineTo commands
star_commands.push(1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2);
// establish a new Vector object for the data parameter
var star_coord:Vector.<Number> = new Vector.<Number>();
// use the Vector array push() method to add a set of coordinate pairs
star_coord.push(0,0, 75,50, 100,0, 125,50, 200,0, 150,75, 200,100, 150,125, 200,200, 125,150, 100,200, 75,150, 0,200, 50,125, 0,100, 50,75, 0,0);
graphics.drawPath(star_commands, star_coord);
}
After the comments made by Bennet and Nicolas, it became obvious that my requirements were (nearly) impossible without a fair amount of redesign. The changes made are as follows:
Generators are no longer DisplayObjects. They are only used to calculate vectors containing the IGraphicsData objects necessary to draw the generated graphic with the drawGraphicsData method.
MasterContainer is now a shape subclass that retrieves the Vector.<IGraphicsData> from each registered generator in order to draw the output.
A bitmap subclass is used to render the contents of the MasterContainer, combining it with a color-dampened version of the previous frame.
An abridged version of the bitmap subclass:
private var constantSustain:Number;
private var linearSustain:Number;
private var sustain:ColorTransform;
private var lastFrame:BitmapData;
public function BitmapManipulator(constantSustain:Number = 0.998, linearSustain:Number = 0.98) {
this.constantSustain = Math.min(Math.max(constantSustain, 0), 1);
this.linearSustain = Math.min(Math.max(linearSustain, 0), 1);
this.UpdateSustain();
this.addEventListener(Event.ADDED_TO_STAGE, OnAddedToStage)
}
private function UpdateSustain():void {
var constantRelease:Number = 255 * (this.constantSustain - 1);
this.sustain = new ColorTransform(this.linearSustain, this.linearSustain, this.linearSustain, 1,
constantRelease, constantRelease, constantRelease, 0);
}
private function OnAddedToStage(event:Event) {
this.lastFrame = new BitmapData(stage.stageWidth, stage.stageHeight, true, 0);
}
public function DrawFrame(container:MasterContainer):void {
this.lastFrame.draw(container);
this.bitmapData = lastFrame;
this.lastFrame = this.bitmapData
this.lastFrame.colorTransform(getBounds(this), this.sustain);
}
...and finally the results #60fps when using an indigo sine wave of shifting phase as the input for the CustomWave:
I'm making a game where the reaction time to killing an enemy is shown and used in later calculations. The number needs to be precise and in seconds, with three significant figures. So i need the current value of a timer to be converted to a numerical value.
I have it so the enemy spawns, a timer (reactionTimer) event begins and when it's killed a variable (reactionTime) will be set to the reactionTimer.
a new one then spawns and it resets the timer and variable.
I've had a few tries at it but it always ends up showing the timer value as either "Object Timer" or 0.
I've also tried using currentCount but this doesn't give me decimal numbers and just works oddly.
New ActionScript developers often mix up the purpose of the Timer class and TimerEvent. It's really for triggering actions at a specific time, not for measuring time.
For measuring time, you use the getTimer function. Whenever you call it, it returns the number of milliseconds that have elapsed since Flash player started. To measure an elapsed time you just call getTimer() twice and subtract the first measurement from the second.
There are a lot of ways you could organize your particular situation. One would be to store the spawn timestamp of the enemy right on the enemy class, capturing it the constructor:
public class Enemy extends MovieClip {
public function get spawnTime():int { return _spawnTime; }
private var _spawnTime:int;
// Constructor
public function Enemy() {
super();
_spawnTime = getTimer();
}
}
Then when enemy is killed you call getTimer() again to get the reaction time:
// Somewhere, where you are processing the enemy killed event
var killTime:int = getTimer();
var reactionTime:int = killTime - killedEnemy.spawnTime; // in whole ms
var reactionSeconds:Number = reactionTime / 1000; // in seconds
Im've created a timer that starts every 1 second.
This is the code what's happening every 1 second.
var Random_Value_X:Number = Math.ceil(Math.random() * 1500);
var Random_Value_Y:Number = Math.ceil(Math.random() * 2000);
var enemy:MovieClip = new AI(stage);
addChild(hero);
enemy.x = Random_Value_X;
enemy.y = Random_Value_Y;
Ok. Then I got the class called AI where I've made it so the AI follows my player. The thing is, I need to make a hitTest that testes if an AI hitting another AI? Is there a way I can give every new AI a ID? Like the first gets called "AI1" and second AI2" and then I can make a code that says like If(AT1.hitTestObject(AT2 || AT3))
Hope you understand what I trying to explain! :)
You should just put them all in an array. Then you can loop through the array and do the hit testing for each one. Depending on how many you have, you might need to split them up into groups so you don't have to do so many checks each frame.
I'm pretty sure you can't just use logical or in the hitTestObject method like that.
Considering that you are on root and keyword "this" referring root. If you make instance of class "enemy" then all objects of it will have type "enemy".
import flash.events.Event;
// for every enemy you create, addlistener to it
// it will force to check itself with others
enemy.addEventListener(Event.ENTER_FRAME,checkHit);
// this function will be available to all enemies
// will inform itself that it is hiting enemy instance
function checkHit(e:Event){
// for e.g. object is moving in x direction
// to keep it simple so you can run it in new file
// with two object one is called enemy and other enemy1
// in your case its changing position
e.target.x += 1;
// loop with all children, break when hit someone
for(var i:uint=0;i<this.numChildren;i++){
// in current situation e.target is also a child of root
// therefore avoid checking it
if(e.target==this.getChildAt(i)) continue;//trace("Its me");
// if hit
// currently testing hit with all objects on stage
// you can change it to check specific type
if(e.target.hitTestObject(this.getChildAt(i))){
trace("I got hit by: "+this.getChildAt(i).toString());
break;
}
}
}
when launching the following code in ADL, why does the square continue to rotate?
var square:Sprite = new Sprite();
square.graphics.beginFill(0xFF0000);
square.graphics.drawRect(-25, -25, 50, 50);
square.x = square.y = 100;
addChild(square);
addEventListener(Event.ENTER_FRAME, rotateSquare, false, 0, true);
function rotateSquare(evt:Event):void
{
square.rotation += 2;
}
System.gc();
Update
the following display object has a weak referenced ENTER_FRAME event listener. however, calling:
removeChild(testInstance);
testInstance = null;
doesn't stop the ENTER_FRAME event:
package
{
import flash.display.Sprite;
import flash.events.Event;
public class Test extends Sprite
{
private var square:Sprite;
public function Test()
{
addEventListener(Event.ADDED_TO_STAGE, init);
}
private function init(evt:Event):void
{
removeEventListener(Event.ADDED_TO_STAGE, init);
square = new Sprite();
square.graphics.beginFill(0xFF0000);
square.graphics.drawRect(-25, -25, 50, 50);
square.x = square.y = 100;
addChild(square);
addEventListener(Event.ENTER_FRAME, rotateSquare, false, 0, true);
// //Current Solution - only works on display objects
// addEventListener(Event.REMOVED_FROM_STAGE, removeHandler);
}
private function rotateSquare(evt:Event):void
{
trace("square is rotating");
square.rotation += 2;
}
// private function removeHandler(evt:Event):void
// {
// removeEventListener(Event.REMOVED_FROM_STAGE, removeHandler);
// removeEventListener(Event.ENTER_FRAME, rotateSquare);
// }
}
}
i have added a REMOVED_FROM_STAGE event listener, but this will only work on display objects.
is this problem specific to ENTER_FRAME event?
Regarding your update, I think you are misunderstanding how GC works. The basic idea is rather simple.
When you create an object, flash allocates some memory in a storage called the heap. A reference to this object is returned. This reference is what you store in a variable. What is a reference? A means to access this object. Think of it as link to the object.
var foo:Bar = new Bar();
Now, in some languages, at some point you have to release the memory allocated for this object when you're done with it, or you have a memory leak.
In a GC environment, this is done automatically. Of course, you need some rules. This rules vary depending on the concrete GC, but in general terms, you could say the GC determines that an object is collectable if it's no longer reachable. This makes sense, because if you can't reach an object, you can't use it. You've lost your link to it. So, it's considered garbage and will be eventually collected.
The specifics on how reachability is determined vary, but in flash it's a mix of reference counting and a mark and sweep algorithm.
(The following is just a high level overview, the details might not be exact)
One method is reference counting: it's easy and fast but it doesn't work in all situations. Basically, each object has a reference count. Each time you assign this object to a variable (i.e. you store a reference to the object), the reference count is incremented. Each time you lost this reference (for instance, you null out your var), this count is decremented. If the count reaches 0, it means the object is unreachable and so it's collectable.
This works fine in some cases, but no others. Specially when there are crossed references.
var foo1:Bar = new Bar(); // let's call this object Bar_1
var foo2:Bar = new Bar(); // let's call this one Bar_2
// at this point, Bar_1 has reference count of 1 (foo1) and Bar_2 has a reference of 1 (foo2)
foo1.theOtherFoo = foo2;
// now Bar_2 has a RC of 2: foo2 and foo1.theOtherFoo
foo2.theOtherFoo = foo1;
// now Bar_1 has a RC of 2: foo1 and foo2.theOtherFoo
foo1 = null;
// foo1 no longer references Bar_1, so its RC is decremented.
foo2 = null;
// foo2 no longer references Bar_2, so its RC is decremented.
// but still both Bar_1 and Bar_2 have a RC of 1.
As you can see, both Bar_1 and Bar_2 have a RC of 1, but are unreachable. This is one of the cases where reference counting doesn't work. Because for all intents and purposes, both objects are unreachable and yet won't be collected.
That's why there's a mark/sweep algorithm. From a high level point of view, what it does is traversing your objects graph, starting from some root objects and analize its relationships to determine whether an object is reachable or not. This algorithm will determine that even though Bar_1 and Bar_2 have a RC of 1, they're not reachable and thus should be considered garbage and be collected at some point.
Events, listeners and dispatchers work the same way. They're not a special case. When you do:
function test():void {
foo1.addEventListener("someEvent",someHandler);
}
function someHandler(e:Event):void {
}
It's the same as doing:
function test():void {
foo1.someProperty = this;
}
The effect is that foo1 now has a reference to this. You'd normally call removeEventListener when you're done for 2 reasons:
1) You no longer want foo1 to have a reference to this.
2) You no longer want to listener for "someEvent" events.
Lots of people insist on using weak references, because they think that then you can pretend you don't have to call removeEventListener (which is apparently too hard...). This is wrong. removeEventListener does two things and both are important. If you want to stop receiving notifications for some event, you have to tell the dispatcher. It's really that simple. In my opinion, weak references are innecesary in most cases. Some advocate to use them by default; but in my experience, in practice this is a bad service to them, as it confuses people further, encourages them to write sloppy code and gives them the impression that you can ignore how this very basic feature of the language (which is not that hard to graps) works.
Now, after this rather long (but hopefuly constructive) rant, let's look at your code:
Your sprite is not going to be collected, because it has 2 references:
1) the square variable
2) the stage.
The first follows the rules outline above. The second too, but it might not be so obvious at first sight. But it makes sense if you think about it for a second. When you did this:
addChild(square);
square got added to the Test instance, which is in turn added to the stage. The stage is always alive, so if something can be reached from the stage, it's reachable. As long as square remains added to the stage, you can be sure it won't be collected.
So, if you at some point do what Sean Thayne suggested:
removeChild(square)
square = null;
your Sprite will be collectable. That doesn't affect the fact that you told your Test object that you wanted to be called whenever a frame is entered. And that's exactly what's happening. Until you don't tell it you don't want to receive this event anymore (calling removeEventListener), it will call you back.
Flash's garbage collection only clears out elements/objects/variables that have either a zero reference count or have only weak references.
This means you would need to do this. For it to truly be gc'd.
removeChild(square)
square = null;
System.gc()