获得每个mousePressed事件的准确时间

时间:2010-08-06 17:13:12

标签: flash actionscript-3

当有人点击我的Flash活动时,有时会触发大量计算。如果用户再次单击(例如双击),则在繁重计算完成后,此事件将排队并调度。如果我多次点击,问题就会复杂化 - 如果点击速度足够快,排队繁重的计算会在十秒后完成,每次clickEvent都会慢慢地运出下一个任务。

我有两个问题。

首先:如何获得点击发生的准确时间?在下面的示例中,我发现快速点击事件很快就会发生点击(sp?)。

第二:收集每一次点击的好设计模式是什么?我想我应该

  • 推迟任何计算直到下一个EnterFrame事件,但如果有人在EnterFrame事件的计算过程中点击......那么,我遇到了同样的问题!

  • 我认为将重度计算分解为伪线程是另一种解决方案,但是根据处理器的速度,很难找到粒度。

  • 在第一次点击后添加标记以忽略下次点击...但此解决方案不允许我跟踪用户在被锁定时尝试执行的操作。第一个问题的解决方案是我在这里需要的。

感谢您的任何建议。以下是一些示例代码来演示此问题:

package
{
    import flash.display.Sprite;
    import flash.display.StageAlign;
    import flash.display.StageScaleMode;
    import flash.events.MouseEvent;
    import flash.geom.Rectangle;

    public class clicky extends Sprite
    {
        private static var _lastTraceTime:Number = new Date().getTime();

        private var _sp:Sprite;
        private var _state1:Boolean;

        public function clicky( ):void
        {   super( );

            stage.align = StageAlign.TOP_LEFT;
            stage.scaleMode = StageScaleMode.NO_SCALE;

            _state1 = true;

            _sp = new Sprite( );
            addChild( _sp );
            _sp.graphics.beginFill( 0xFF00AA, 1 );
            _sp.graphics.drawRect( 10, 10, 100, 100 );
            _sp.graphics.endFill( );
            _sp.addEventListener(MouseEvent.MOUSE_DOWN, mDnCb, false, 0, true);
        }

        private function mDnCb( evt:MouseEvent ):void
        {   traceTime( "click" );
            _state1 = !_state1;
            var c:uint = 0xFF0000;
            if (_state1)
            {   c = 0x00FFAA;
            }
            paintThatRect( c );

            killTime( );
        }

        private function paintThatRect( c:uint ):void
        {
            _sp.graphics.beginFill( c, 1 );
            _sp.graphics.drawRect( 10, 10, 100, 100 );
            _sp.graphics.endFill( );
        }

        private function killTime( ):void
        {   var r:Rectangle = new Rectangle( 0, 0, 100, 100 );
            for (var i:uint = 0; i < 500000; i++)
            {
                var t:Rectangle = new Rectangle( i, i, i, i );
                if (t.intersects(r) || r.containsRect(t) || t.containsRect(r))
                {   r = t.union(r);
                }
            }
        }

        public static function traceTime( note:String ):Number
        {   var nowTime:Number = new Date().getTime();
            var diff:Number = (nowTime-_lastTraceTime);
            trace( "[t" + diff + "] " + note );
            _lastTraceTime = nowTime;
            return diff;
        }
    }
}

2 个答案:

答案 0 :(得分:1)

我过去的一个项目涉及快速按下按钮,所以我做了一项调查,看看我通常期望玩家有多快。我能找到的最快的点击器每秒不能超过10次。

这样做的结果是您可以假设帧速率总是快于点击率。在几乎所有情况下,帧速率为10fps或更低是不可接受的。设置程序,以便将检测到的任何火灾事件添加到队列中。每个帧只处理队列中的一个火灾事件。

  

有时会触发很多计算。

不要那样做。

如果一个过程需要超过十分之一秒才能完成,那么每秒执行它的次数绝对不可能超过10次。 (至少在AS3中)你所做的任何和所有处理都必须设计成不会严重延迟下一帧。它不仅看起来很不稳定,而且你会开始出现饥饿问题。

  

如何获得点击发生的准确时间?

保持良好的帧速率。

答案 1 :(得分:0)

如果播放器忙于运行killTime方法,您将无法获得单击事件的“准确”时间戳。当您的方法阻止动作脚本代码执行时,这些事件将不会及时处理(或者更确切地说,将调用您的处理程序)。

做你想做的事情(或者我认为你想要做的事情)的唯一方法是将重型加工部件分解成更小的部分,有点像绿线,如你所说。如果您使用google actionscript +绿色线程,有很多关于如何实现此功能的示例。有些人为问题添加了更多的结构,有些则更加简单,但他们都归结为相同的基本想法。处理块,检查您是否超过某个阈值;当你/如果你这样做,从你的功能返回并等待再次被召唤从你离开的地方接你。您可以使用计时器或订阅EnterFrame。

根据您的游戏情况,这可能会解决您的问题或将其移至其他位置。如果Gunslinger47的这一点适用于你的游戏,这种方法将无法真正起作用:

  

如果一个过程需要十分之一以上   一秒钟完成,简单来说   不可能执行超过10   每秒次数

然而,这是一个可能的实现草图,假设情况并非如此。我从示例代码中删除了一些内容并添加了其他内容。我认为代码很容易理解,但无论如何我只会解释一下。

我正在使用内部类Context来跟踪进度,我还使用它来保存导致进程运行的点击的时间戳。您可以使用它来存储与每个进程相关的其他数据。每次用户单击该按钮时,都会创建其中一个上下文对象并将其推送到队列中。如果队列中只有一个项目(我们刚添加的项目),我们立即启动该过程并设置定时器/间隔。如果队列中有更多的东西,这意味着已经有一个进程在运行,所以我们暂时不做任何事情。

每次执行计时器机制时,它都会从上一次迭代中的位置开始拾取。先前的状态存储在队列的第一项中。我正在存储一个计数器,但你可能需要在那里保存其他数据。在这个函数中,我正在检查是否超过了时间阈值。这涉及调用getTimer(),它比使用Date对象更轻量级,但您可能不希望每次迭代都调用它。你可以只检查每N次循环的时间,但这取决于你。此外,这个最大时间有点任意。你应该稍微调整一下,虽然对于20 FPS swf来说20 ms似乎是合理的(假设代码和渲染每帧有50毫秒的理论值)。

当进程完成时,队列被移位。然后我们检查是否还有待处理的项目。如果有,我们只是让事情再次运行。否则,我们停止删除EnterFrame。

请注意使用这种“绿色线程”方法会有一些开销,因为只需一次运行相同的代码就会明显加快。所以它并不完美,但它通常是在处理过程中保持应用可用的唯一方法。

package {


    import flash.display.Sprite;
    import flash.display.StageAlign;
    import flash.display.StageScaleMode;
    import flash.events.MouseEvent;
    import flash.geom.Rectangle;
    import flash.events.Event;
    import flash.utils.getTimer;

    public class clicky extends Sprite {

        private static var _lastTraceTime:Number = new Date().getTime();

        private var _sp:Sprite;

        private var _queue:Array;
        private const MAX_TIME:int = 20;

        public function clicky( ):void {
            super( );

            stage.align=StageAlign.TOP_LEFT;
            stage.scaleMode=StageScaleMode.NO_SCALE;

            _queue = [];

            _sp = new Sprite( );
            addChild( _sp );
            _sp.graphics.beginFill( 0xFF00AA, 1 );
            _sp.graphics.drawRect( 10, 10, 100, 100 );
            _sp.graphics.endFill( );
            _sp.addEventListener( MouseEvent.MOUSE_DOWN, mDnCb, false, 0, true );
        }

        private function mDnCb( evt:MouseEvent ):void {
            _queue.push(new Context(new Date()));
            if(_queue.length == 1) {
                initProcess();
            }
        }

        private function initProcess():void {
            trace("initProcess");
            killTime();
            addEventListener(Event.ENTER_FRAME,run);
        }

        private function processDone():void {
            trace("processDone, " + _queue[0].clickTime);
            _queue.shift();
            if(_queue.length == 0) {
                removeEventListener(Event.ENTER_FRAME,run);         
            }
        }

        private function run(e:Event):void {
            killTime();
        }

        private function paintThatRect( c:uint ):void {
            _sp.graphics.beginFill( c, 1 );
            _sp.graphics.drawRect( 10, 10, 100, 100 );
            _sp.graphics.endFill( );
        }

        private function killTime():void {
            var r:Rectangle=new Rectangle(0,0,100,100);
            var initTime:int = getTimer();
            var runningTime:int = 0;
            var loops:int = 500000;
            var ctx:Context = _queue[0];
            for(var i:int = ctx.i; i < loops; i++) {
                var t:Rectangle=new Rectangle(i,i,i,i);
                if (t.intersects(r)||r.containsRect(t)||t.containsRect(r)) {
                    r=t.union(r);
                }
                runningTime = getTimer() - initTime;
                if(runningTime >= MAX_TIME) {
                    break;
                }
            }
            ctx.i = i;
            if(i == loops) {
                trace(i);
                processDone();
            }

        }

    }
}

class Context {
    public var i:int = 0;
    public var clickTime:Date;

    public function Context(clickTime:Date) {
        this.clickTime = clickTime;
    }

    public function reset():void {
        i = 0;
    }
}
相关问题