получение точного времени для каждого события mousePressed - PullRequest
2 голосов
/ 06 августа 2010

Когда кто-то нажимает на мою флэш-активность, иногда это вызывает много вычислений.Если пользователь нажимает снова (например, двойной щелчок), это событие ставится в очередь и отправляется после завершения сложных вычислений.Если я нажимал много раз, проблема только усугублялась - если щелкнуть достаточно быстро, тяжелые вычисления в очереди заканчиваются спустя десять секунд, и каждое нажатие кнопки «событие» медленно завершает следующую задачу.

У меня два вопроса.

Первое: как я могу получить точное время, когда произошел щелчок?В приведенном ниже примере я получаю рассылку событий быстрого щелчка спустя много времени после щелчка (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 ]

1 голос
/ 07 августа 2010

Один из моих прошлых проектов включал быстрое нажатие кнопок, поэтому я провел опрос, чтобы выяснить, насколько быстро я могу ожидать, что игроки будут нажимать.Самый быстрый кликер, который я мог найти, не мог пройти 10 раз в секунду.

Следствием этого является то, что вы можете предположить, что частота кадров всегда будет выше, чем частота кликов.Частота кадров 10 кадров в секунду или менее недопустима почти во всех случаях.Настройте свою программу так, чтобы любые обнаруженные пожарные события были добавлены в очередь.Каждый кадр обрабатывает только одно событие пожара из очереди.

иногда это вызывает много вычислений.

Тогда не делайте этого.

Если процесс занимает больше десятой доли секунды, просто невозможно выполнить его более 10 раз в секунду.(по крайней мере в AS3) Любая и вся обработка, которую вы выполняете, должна быть спроектирована так, чтобы она не критически задерживала следующий кадр.Мало того, что это будет выглядеть действительно нестабильно, но у вас начнутся проблемы с голодом.

как я могу получить точное время, когда произошел щелчок?

Поддерживая приличную частоту кадров.

0 голосов
/ 09 августа 2010

Вы не получите «точные» временные метки для событий щелчка, если игрок занят выполнением вашего метода killTime.Эти события не будут обработаны своевременно (или, скорее, ваши обработчики будут вызваны), пока ваш метод блокирует выполнение кода Actionscript.

Единственный способ сделать то, что вы хотите (или то, что, я думаю, вы пытаетесь сделать)в любом случае) разбивает тяжелую обрабатывающую часть на более мелкие кусочки, вроде зеленых нитей, как вы и предлагали.Существует множество примеров того, как реализовать это, если вы используете Google Actionscript + зеленые темы.Некоторые добавляют больше структуры к проблеме, другие более просты, но все они сводятся к одной и той же основной идее.Проводите обработку кусками, проверяя, не превышаете ли вы пороговое значение;когда / если вы это сделаете, вернитесь из своей функции и подождите, пока вам не позвонят, чтобы продолжить с того места, где вы ушли.Вы можете использовать таймер или подписаться на EnterFrame для этого.

В зависимости от вашей игры это может решить вашу проблему или просто переместить ее в другое место.Если это замечание, сделанное Gunslinger47, применимо к вашей игре, этот подход на самом деле не сработает:

Если процесс занимает больше десятой доли секунды, просто невозможно выполнить его больше, чем10 раз в секунду

Однако, вот эскиз возможной реализации, если предположить, что это не так.Я удалил некоторые вещи из вашего примера кода и добавил другие.Я думаю, что за кодом легко следовать, но в любом случае я просто объясню немного.

Я использую внутренний класс Context для отслеживания прогресса и также использую его для сохранения метки времени клика, вызвавшего запуск процесса.Вы можете использовать его для хранения других данных, связанных с каждым процессом.Каждый раз, когда пользователь нажимает кнопку, один из этих объектов контекста создается и помещается в очередь.Если в очереди только один элемент (который мы только что добавили), мы сразу запускаем процесс и устанавливаем таймер / интервал.Если в очереди больше материала, значит, процесс уже запущен, поэтому мы пока ничего не делаем.

Каждый раз, когда запускается механизм таймера, он начинает с того места, где он ушел в предыдущей итерации.Предыдущее состояние сохраняется в первом элементе очереди.Я храню счетчик, но вам может понадобиться сохранить другие данные там.В этой функции я проверяю, превышен ли порог времени.Это включает вызов getTimer (), который является более легким, чем использование объекта Date, но вы, вероятно, не захотите вызывать его для каждой итерации.Вместо этого вы можете проверить время только каждые N петель, но это на ваше усмотрение.Кроме того, это максимальное время несколько произвольно.Вы должны немного его настроить, хотя 20 мс кажется разумным для 20 FPS swf (предполагая теоретические 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;
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...