I am new to actions script and I want to stall the code itself for just a bit, but all the tutorials and libraries I have looked at seems to focus more on stopping the Movie clip. Also seems that you have to call a function with a timer, but I just purely want it to wait a few seconds.
What I want to achieve:
Want a function to finish before continuing to the next function.
The Code:
(Credit for code goes to Garry Law. Using his code to provide better clarity)
function PlayGame() {
PauseThenDisplayText(1,"Get Ready");
PauseThenDisplayText(1,"Get Ready");
}
function PauseThenDisplayText(sec,txt) {
trace('timer starts');
var timer1:Timer = new Timer (sec*1000,1);
timer1.start();
timer1.addEventListener(TimerEvent.TIMER,onTimer);
function onTimer(evt:TimerEvent):void{
trace('timer event triggered');
timer1.removeEventListener(TimerEvent.TIMER,onTimer);
Display_Screen.text = txt;
}
}
the reason your code won't work is because the code is all being read before your timer ends. You'll need to add listeners to the Timer event. For example:
function PlayGame() {
PauseThenDisplayText1(1);
}
function PauseThenDisplayText1(sec) {
trace('timer starts 1');
var timer1:Timer = new Timer (sec*1000,1);
timer1.start();
timer1.addEventListener(TimerEvent.TIMER,onTimer);
function onTimer(evt:TimerEvent):void{
trace('timer event triggered 1');
timer1.removeEventListener(TimerEvent.TIMER,onTimer);
Display_Screen.text = "Get Ready";
//now that first timer finishes, start next one
PauseThenDisplayText2(1);
}
}
function PauseThenDisplayText2(sec) {
trace('timer starts 2');
var timer2:Timer = new Timer (sec*1000,1);
timer2.start();
timer2.addEventListener(TimerEvent.TIMER,onTimer);
function onTimer(evt:TimerEvent):void{
trace('timer event triggered 2');
timer2.removeEventListener(TimerEvent.TIMER,onTimer);
Display_Screen.text = "Start Game";
}
}
Related
I have a big issue with my code
I have a function called "delayCallFuntions":
function delayCallFuntions(delay: int, func: Function) {
var timer: Timer = new Timer(delay, 1);
timer.addEventListener(TimerEvent.TIMER, func);
timer.start();
}
And I used this function like below to make connection between 2 point in my screen:
delayCallFuntions(1, function (e: Event) {timer011(wireColor);});
And function "timer011" is making the connections:
function timer011(firstColor: int): void {
wireColor = firstColor;
//GRID is a class
//Path A to B
var PathAB: Grid;
PathAB = new Grid(4, 5, 20, 17, canvas, wireColor);
this.addChild(PathAB);
}
My problem is:
I have several of these functions like "timer012", "timer013", ... that they need to execute one after another.
When I go out of this scene and come back again, these is still some of these functions are working while I need them to start from the beginning and go one by one.
for example: when i come back, "timer011" is starting while "timer016" is also completing at the same time.
hope someone can help me as this problem made me frustrated.
Currently you are creating a whole new timer everytime you add a function. That timer will stay in memory because of the event listener, and since it's encapsulated in the function, you have no easy way to reference it again to stop them.
What would be a better approach, is to create just one timer globally referenced so you can stop it if needed.
Here is a way you could accomplish this:
//create an array that will hold all the functions you are planning on calling
var delayedFuncs:Array = [];
//this var will be used to store the current function that will be called next
var currentFuncObj:Object = null; //set it to null so it clears the value when you return to this frame
//create a single, global timer reference for everything
//don't initialize it here though
//if you revisit this frame, you don't want to create a whole new timer, but keep using the previous one
var funcTimer:Timer;
//if the timer has already been created (you've been to this frame before), stop it
if (funcTimer) {
funcTimer.stop();
}else {
//if you haven't been to this frame before, create the timer and add the listener
funcTimer = new Timer(1,1);
funcTimer.addEventListener(TimerEvent.TIMER, nextFunc, false, 0, true);
}
//this function adds items to your queue. I've added the ability to also include parameters
function delayCallFunctions(delay:int, func:Function, ... funcParams):void {
//add an object to the array that stores the function, delay, and any parameters to pass to that function
delayedFuncs.push({delay: delay, func: func, params: funcParams});
//if the timer hasn't started running yet, start it since we've added something
if(!funcTimer.running) nextFunc();
}
//this function runs when the timer completes
function nextFunc(e:Event = null):void {
//if we have an existing function to call, call it
if (currentFuncObj){
//invoke the function with the parameters
currentFuncObj.func.apply(null, currentFuncObj.params);
}
//if there are still items in the array, grab the next one
if(delayedFuncs.length > 0){
//array.shift grabs the first element in the array and removes it from the array
currentFuncObj = delayedFuncs.shift();
//reset the timer
funcTimer.reset();
//set the appropriate delay
funcTimer.delay = currentFuncObj.delay;
//start the timer again
funcTimer.start();
}
}
So now, you'd use by doing:
delayCallFunctions(3000, trace, "hello", "world", "I'll be traced 3 seconds from now");
delayCallFunctions(2000, trace, "I'll be called 2 seconds after the last one");
Or, with your specific code:
delayCallFuntions(1000, timer011, wireColor);
Now at any time (say you hit a button to go to change scenes), you can just stop the global timer.
funcTimer.stop();
Alright, so I am fairly new to AS3 and I have a level in my game where you have to stay alive for 45 seconds. If I use a code like (Or if there is a better code, I'll use that one)
var myTimer:Timer = new Timer(1000, 1); // 1 second
myTimer.addEventListener(TimerEvent.TIMER, runOnce);
myTimer.start();
function runOnce(event:TimerEvent):void {
trace("runOnce() called # " + getTimer() + " ms");
}
How can I use this to make my game move to scene 6 if they stay alive for 45 seconds? I also want to display text on the animation that keeps track of how long they've been alive so they know how long they have left. How could I accomplish this?
private var startTime:int;
function startGame() {
// this is called when your game starts
startTime=getTimer();
... // rest of init code
}
function onEnterFrame(e:Event):void {
// main loop, whatever you need to do in here
currentTime=getTimer()-startTime; // here we receive the elapsed time
// pause handling is excluded from this example!!11
if (weAreDead()) {
survivalTime= currentTime;// here
...
} else if (currentTime>45000) {
//advance to scene 6 here
}
}
Set the listener for Event.ENTER_FRAME to onEnterFrame, start the game with setting the stored time, and pwn.
The simplest solution is to go ahead and use the timer, but set the value to 45000 and make sure to keep a reference of the timer or it will be garbage collected. Also, create a separate function which allows you to kill the timer from anywhere if this particular thing ever needs to just "go away" without completing.
public static const DELAY:int = 45;
private var _timer:Timer;
public function setTimer():void
{
_timer = new Timer( DELAY * 1000, 1 );
_timer.addEventListener( TimerEvent.TIMER_COMPLETE, timerCompleteHandler );
_timer.start();
}
private function timerCompleteHandler( event:TimerEvent ):void
{
disposeTimer();
goDoTheThingThatYouNeededToDo();
}
public function disposeTimer():void
{
_timer.stop();
_timer.removeEventListener( TimerEvent.TIMER_COMPLETE, timerCompleteHandler );
_timer = null;
}
I'm calling a timerEvent function from enterFrame, it's running the timerEvent function on everyFrame. Is there a way to control it?
I've got my bullets firing function with timerEvent of 500, so it shoots a bullet every half a second on 24fps. It's working fine for now. Now I want to change bullet speed and skin with respect to weapon.
////////////////////////////////
//this function is called first time within an EnterFrame function
///////////////////////////////
function weaponCheck():void
{
switch (weaponState)
{
case STATE_GUN :
gun();
break;
case STATE_DOUBLE_GUN :
doubleGun();
break;
}
}
function gun():void
{
trace("single gun");
laserTimer = new Timer(600);
laserTimer.addEventListener(TimerEvent.TIMER, timerListener);
laserTimer.start();
function timerListener(e:TimerEvent):void
{
var tempLaser:MovieClip = new Laser();
var tempGunBlast:MovieClip = new Gun_blast_01();
tempLaser.x = player.x +((player.width/2)+12);
tempLaser.y = player.y;
tempGunBlast.x = stage.mouseX + 104;
tempGunBlast.y = tempLaser.y;
Lasers.push(tempLaser);
addChildAt(tempLaser,1);
addChildAt(tempGunBlast, 3);
if (tempGunBlast.currentFrame >= tempGunBlast.totalFrames)
{
removeChild(tempGunBlast);
}
}
}
function doubleGun():void
{
trace("Double gun");
doubleGunTimer = new Timer(400);
doubleGunTimer.addEventListener(TimerEvent.TIMER, timerListener2);
doubleGunTimer.start();
function timerListener2(e:TimerEvent):void
{
var tempLaser:MovieClip = new doubleG();
var tempGunBlast:MovieClip = new Gun_blast_01();
tempLaser.x = player.x +((player.width/2)+12);
tempLaser.y = player.y;
tempGunBlast.x = stage.mouseX + 104;
tempGunBlast.y = tempLaser.y;
Lasers.push(tempLaser);
addChildAt(tempLaser,1);
addChildAt(tempGunBlast, 3);
if (tempGunBlast.currentFrame >= tempGunBlast.totalFrames)
{
removeChild(tempGunBlast);
}
}
}
///////////////////////////////////////////////////
//Following function is called within an enterFrame event function
/////////////////////////////////////////////////
function playGame():void
{
weaponCheck();
blah1();
blah2();
blah3();
testForEnd();
}
function testForEnd():void
{
if (level == 3)
{
laserTimer.stop();
weaponState = STATE_DOUBLE_GUN;
weaponCheck();
}
}
So when the game runs for first time, it works fine and uses the timer event of 600 to hit the bullets, but when level == 3 and weaponState changes, the 2nd firing function doubleGun(); is called but it starts to fire the bullets on a per frame count, not on a controlled timerEvent. Please Help. Thanks.
Why don't you drop timers and use enter frame listener as a manner to count time? You are already calling weaponCheck() from an enterframe listener. Make it so that the actual gun() and doublegun() calls will only generate animation, such as firin' mah lazers and blasts, and the main function will just count time.
function weaponCheck():void
{
this.reloading+=this.weaponFiringSpeed; // you alter this to make your weapon fire slower or faster
if (this.reloading<FULLY_RELOADED) return; // we are not yet ready to fire
this.reloading=0;
switch (weaponState) // and here we fire with the gun state
{
case STATE_GUN :
gun();
break;
case STATE_DOUBLE_GUN :
doubleGun();
break;
}
}
function gun():void
{
trace("single gun");
var tempLaser:MovieClip = new Laser();
var tempGunBlast:MovieClip = new Gun_blast_01();
tempLaser.x = player.x +((player.width/2)+12);
tempLaser.y = player.y;
tempGunBlast.x = stage.mouseX + 104;
tempGunBlast.y = tempLaser.y;
Lasers.push(tempLaser);
addChildAt(tempLaser,1);
addChildAt(tempGunBlast, 3);
}
And similarly double gun. FULLY_RELOADED is a constant, reloading is a variable used to track time, it should be a property of the one who's firing.
Note, this approach requires you to manage your "tempGunBlast"s elsewhere, perhaps in the very weaponCheck function, if so, modify it as follows:
function weaponCheck():void
{
if (tempGunBlast) if (tempGunBlast.currentFrame >= tempGunBlast.totalFrames)
removeChild(tempGunBlast);
this.reloading+=this.weaponFiringSpeed; // you alter this to make your weapon fire slower or faster
if (this.reloading<FULLY_RELOADED) return; // we are not yet ready to fire
... // rest of code unchanged
You will most likely not be able to copypastely implement this, but please try.
I'm trying to do a simple url requestion on enter frame, but it seems to be executing multiple times?
I tried removing the event handler on handleLoadSuccessful, that seems to stop it firing infinitely, but its still executing about 10 times before it stops.
How do I make it only fire once?
addEventListener(Event.ENTER_FRAME, onLoadHandler);
function onLoadHandler(event:Event) {
var scriptRequest:URLRequest = new URLRequest("http://ad.doubleclick.net/clk;254580535;19110122;t?http://www.gamespot.com.au/Ads/gswide/hp/1x1.gif");
var scriptLoader:URLLoader = new URLLoader();
var scriptVars:URLVariables = new URLVariables();
scriptLoader.addEventListener(Event.COMPLETE, handleLoadSuccessful);
scriptLoader.addEventListener(IOErrorEvent.IO_ERROR, handleLoadError);
scriptRequest.method = URLRequestMethod.POST;
scriptRequest.data = scriptVars;
scriptLoader.load(scriptRequest);
function handleLoadSuccessful($evt:Event):void {
trace("Message2 sent.");
removeEventListener(Event.ENTER_FRAME, onLoadHandler);
}
function handleLoadError($evt:IOErrorEvent):void {
trace("Message1 failed.");
}
}
ENTER_FRAME is fired x times per second, where x is the document frame rate.
The code within your listening function leads me to thinking that you don't actually need the event listener or the listening function at all - if you just want what's within that function called a single time, move everything within it outside on its own.
You could also move it all into an init() function and then call that once.
Also, your functions handleLoadSuccessful() and handleLoadError() should not be defined within another function (in your case onLoadHandler()).
I'm having trouble making something happen over and over without a for loop. Take a look at this:
package {
import flash.display.Sprite;
import flash.events.Event;
public class Main extends Sprite {
public function Main() {
addEventListener("done", caller);
caller();
}
public function caller(e:Event = null):void {
trace("hello!");
dispatchEvent(new Event("done"));
}
}
}
sing this will get you an "Error #2094: Event dispatch recursion overflow." really fast. It will show that the event dispatcher and caller() are getting called inside of them selves, nesting until the error happens.
What I want to do is this:
"When caller() is done, call it again"
not:
"call caller() before it finishes"
Now, before people start suggesting using a timer to guess how long it will take or use ENTER_FRAME, This caller() will not have any graphic data and won't be connected to a Sprite and the time it takes to finish may vary greatly from call to call. I'm really looking for a way to run it only after it has completely finished.
Thanks for your help.
Thank you for your responses. I used Timer and still could overflow with too many calls and too short a timer interval. So I simplified and tried to just make an Event based for loop class (A class that operates like a for loop, but with events to avoid gobbling up all the resources) The solution was to call the function, on it's completion call the timer; on the timer's completion call the function again and bounce them off of each other. Basically:
call function
wait
call function
wait etc.
Even if the timer is set to 0 and it freezes the swf until the all the functions are called, the function will complete before running again.
try it out:
package {
import flash.display.Sprite;
public class Efl extends Sprite { // the main class
public function Efl() {
// make four functions...
function init (o:Object):void { // akin to the first part of the for loop
o.value = 0;
}
function condition(o:Object):Boolean { // like the condition portion of the for loop
if (o.value <= 100) {
return (true);
} else {
return (false);
}
}
function next(o:Object):void { // the increment part of a for loop
o.value++;
}
function statements(o:Object):void { // the body of the for loop
trace(o.value);
}
// put the four functions in one new EventForLoop
var test1:EventForLoop = new EventForLoop(init, condition, next, statements, 1); // delay is 1 ms
test1.start(); // set it into motion
// do it again all in one line - not pretty but it works
var test2:EventForLoop = new EventForLoop(
function (o:Object):void { o.value = 0; },
function (o:Object):Boolean { if (o.value <= 50) return (true); else return (false); },
function (o:Object):void { o.value++ },
function (o:Object):void { trace("> " + o.value) },
20); // delay in 100ms
test2.start(); // start it up
// if you try this out, the two will run intertwined since the delays are different.
}
}
}
Here is the Class that runs the loop:
package {
import flash.events.EventDispatcher;
import flash.events.Event;
import flash.events.TimerEvent;
import flash.utils.Timer;
public class EventForLoop extends EventDispatcher {
// functions to call when simulating the for loop
private var initializer:Function; // is run once at the start of the loop
private var condition:Function; // returns boolean to tell the loop to continue or not
private var step:Function; // the function that runs after the loop is complete
private var statements:Function; // the actual body of the loop
private var timeout:Timer; // the timer to avaoid overflows
private var operator:Object = new Object(); // this is an object to hold and pass values across all the sub loop functions. it is the parameter passed to all four functions
// some event constants
static const NEXT:String = new String("EFLNext");
static const DONE:String = new String("EFLDone");
// constructor just loads vars and sets up timer
public function EventForLoop (init:Function, cond:Function, stepper:Function, stat:Function, delay:Number = 0) {
initializer = init;
condition = cond;
step = stepper;
statements = stat;
timeout = new Timer(delay, 1);
}
// the mail loop function...
private function next(e:Event = null):void {
// Try this and the lone afte the loop:
// trace ("start statements");
if (condition.call(null, operator)) { // if the condition is still true...
statements.call(null, operator); // do the body statements of the loop
step.call(null, operator); // increment
dispatchEvent(new Event(EventForLoop.NEXT)); // dispatch the event so that thw wait can start
} else { // condition returns false??
dispatchEvent(new Event(EventForLoop.DONE)); // tell the event dispatcher the loop is done
removeEventListener(EventForLoop.NEXT, wait); // stop event listeners
timeout.removeEventListener(TimerEvent.TIMER_COMPLETE, next);
}
// trace ("finish statements\n");
// this line and the one before the if() will show that the functcion ends before starting again, even if the Timer wait 0ms
}
// very simple function that waits and ten triggers the loop again
private function wait(e:Event):void {
timeout.reset();
timeout.start();
}
// metod used to set the loop running
public function start():void {
initializer.call(null, operator); // use the initioalizer to set the operator Object
addEventListener(EventForLoop.NEXT, wait); // when the loops done, wait
timeout.addEventListener(TimerEvent.TIMER_COMPLETE, next); // when done waiting, loop again
next(); //do the first loop
}
}
}
You might want to experiment with flash.utils.setTimeout(). Put it at the bottom of caller() and have it set a timeout for itself. If you give it a very small timeout interval, it will asynchronously recurse the next time Flash gets the chance.
Alternatively, an ENTER_FRAME event will do more or less the same thing (except at extremely high framerates). Flash will delay the rendering of the next frame until all the processing logic on one frame has finished. Furthermore, Flash is single-threaded, so you can be guaranteed that two copies of your function will never run simultaneously.
I've got questions similar to some of the other responders. How often do you want the call to happen? If what you want is for the call to immediately repeat as soon as it finishes, no other part of your program will ever get a chance to execute.
Is this for an assignment?
If you don't want for loops, how about a while loop?
Trying to use timers could work but it gets messy. If you absolutely must user a Timer then have some boolean flag set to true/false if your function is still running. The timer event would see if your function is finished, if so then call it again.
I would use enterFrame... Flash is frame based... when your process is finished, you check if you still have time for another call to the function, if not, just wait for the next frame to come...
addEventListener("enterFrame", loop);
function loop(e) {
var maxtime=1000/stage.frameRate;
var t1=getTimer();
while(getTimer()-t1 < maxtime) {
myProcess();
}
}
OK, I know you said
his caller() will not have any graphic data and won't be connected to a Sprite
And
I'm really looking for a way to run it only after it has completely finished.
So I'll address those and then tell you an enterframe is the best solution :)
You don't need a graphical representation, or access to the stage to use a enter frame event listener. You can simply do the following:
var s:Shape = new Shape();
s.addEventListener(Event.ENTER_FRAME, caller)
private function caller():void
{
//do stuff
}
Above we simple create a shape to listen for the enter frame events, and thats all we use it for.
As for the second part, when code is being interpreted at runtime and it comes to a function, caller in this case, it won't execute another function, or line of code outside that function, until it has finished it. So you know that it will never execute again until it has finished the previous call.
So an enterframe (or a timer) are your best / only solutions.
What you want to do is dispatch a new event when the Caller() finishes that then calls caller again.
But your need to have a max loop counter otherwise you will just get a stack overflow error.
Don't forget to use weak reference to your event listeners, as it will alway unused object to get garbage collected and help your app run smoother + faster.
package {
import flash.display.Sprite;
import flash.events.Event;
public class Main extends Sprite {
public function Main() {
addEventListener("Call_ME_AGAIN", callCaller, false, 0, true );
caller();
}
private var _counter:int = 0;
private const LOOP_TIMES:int = 100;
public function caller(e:Event = null):void {
trace("hello!");
if (counter != LOOP_TIMES)
{
dispatchEvent(new Event("Call_ME_AGAIN"));
counter++;
}
else if (counter == LOOP_TIMES)
{ //reset the counter so it can happen again when you want
counter = 0;
}
}
public function callCaller(e:Event = null):void {
e.stopImmediatePropagation();
caller(null);
}
}
}