开发者

Problem in timer delay in action script 3

I'm working on a car flash game using action script 3, I want the road lines to go faster if I press "up" on the keyboard,the lines are created by a timer whose delay is modified by the pressed key, problem is, if I let the key pressed the timer of the event still ref开发者_如何学运维reshes and it doesn't show any road lines until I stop pressing it.

Here's the key press code:

if (upPressed)
{
    speed = speed*1.02;
    timerB.delay = timerB.delay/1.007;
    timerA.delay = timerA.delay/1.005;
}

Any tips? Thanks in advance.


The issue may be a result of the Timer class restarting everytime you change the delay property.

From Adobe docs:

If you set the delay interval while the timer is running, the timer will restart at the same repeatCount iteration.

http://livedocs.adobe.com/flash/9.0/ActionScriptLangRefV3/flash/utils/Timer.html#delay

This basically means that the timer restarts at 0, but remembers how many times it has repeated. I realized my answer was getting long so I've put some reference at the bottom.

SOLUTION: Use flash.utils.getTimer() instead of a Timer object.

http://www.adobe.com/livedocs/flash/9.0/ActionScriptLangRefV3/flash/utils/package.html#getTimer()

The getTimer() function returns an integer that represents the number of milliseconds that have passed since the launch of the .swf file. If you check this number each frame, you can see how much time has elapsed since the previous frame. Simply keep adding the number of milliseconds that have passed each frame until that number reaches your threshold, then call the line drawing function and reset your counter.

When checking to see if the key is pressed reduce your new 'myDelay' variable instead of a Timer.delay property.

    var ms:int;
    var timeStamp:int = flash.utils.getTimer();
    var myDelay:Number;    

    onEnterFrame() {

          // Track how much time has passed since the last frame
          ms += flash.utils.getTimer() - timeStamp;

          if (ms > myDelay)
          {
               // DRAW THE ROAD LINES
               drawLines();
               // RESET THE COUNTER 
               ms = 0;
          }

          //other stuff  

          // Record the timestamp of this frame so you can see the difference next frame
          timeStamp = flash.utils.getTimer();                 
    }

EXPLANATION OF TIMER RESET:

NOTE: I'm assuming you are checking for key presses every second. If you are changing the delay property of a Timer every frame (eg. 24 times a second) and your timer happens more infrequently than 24 times a second, it will be reset before it gets the chance to send out an event to call its handler function to create lines.

If you take a look at the example below, starting and stopping the event is the only way to prevent it from being reset, but even then the timer event handler function will only be called when the delay is shorter than the time between changes to the delay. Since the average swf is 24fps, this means that the delay needs to be shorter than 41.6 milliseconds to be called before the next frame changes the value once again.

  package 
{
    import flash.display.Sprite;
    import flash.events.Event;
    import flash.events.KeyboardEvent;
    import flash.events.TimerEvent;
    import flash.utils.Timer;

    /**
     * ...
     * @author 
     */
    public class Main extends Sprite 
    {

        var timer:Timer;
        var keyPressed:Boolean = false;

        public function Main():void 
        {
            if (stage) init();
            else addEventListener(Event.ADDED_TO_STAGE, init);
        }

        private function init(e:Event = null):void 
        {
            removeEventListener(Event.ADDED_TO_STAGE, init);
            timer = new Timer(50);
            timer.addEventListener(TimerEvent.TIMER, function ():void { trace (timer.delay); } );
            timer.start();

            stage.addEventListener(KeyboardEvent.KEY_DOWN, keyDown);
            stage.addEventListener(KeyboardEvent.KEY_UP, keyUp );

            addEventListener(Event.ENTER_FRAME, onEnterFrame);

        }

        function onEnterFrame (e:Event)
        {                   
            if (keyPressed)
            {
                timer.stop();
                timer.delay -= 1;
                timer.start();
            }
        }

         function keyDown (e:KeyboardEvent):void { keyPressed = true; } 


         function keyUp (e:KeyboardEvent):void { keyPressed = false; }
    }

}


While Josh's answer seems to accurately describe the source of your bug, it should be noted that the correct way to solve your problem is to stop using timers entirely, and run all your logic in an EnterFrame handler.

To see why that's true, recall that the flash player runs at a given framerate, and only redraws the screen once per frame. Suppose you run a timer at the same speed as the framerate. This means you'll get one Timer event between each screen redraw. Now suppose you speed up the timer by 10%. All that happens is, the Timer events and frame events get out of sync, so you'll get one Timer per frame for nine frames, and during the tenth frame the Timer will fire twice. Your road line isn't moving 10% faster, it's moving at the same speed for nine frames and double speed for the tenth, which is nothing like what you want.

The solution is, instead of trying to move the road lines more frequently, you want to move them once per frame, but move them a greater or lesser distance. And the right way to do this is in an EnterFrame handler.


EnterFrame is an event that gets fired by the flash runtime every time the view refreshes (roughly). You can add an event listener to your root display object, and have an "update()" method triggered. This would replace what you are currently doing in your timer.

You can read a detailed explanation of why EnterFrame is more useful than Timer in your gaming context here:

http://www.bit-101.com/blog/?p=910

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜