Математика Номера Флэш-Памяти При Спаривании Displayobject

Однажды меня попросили разобраться с багом: при изменении FrameRate в произвольном количестве вложенных .

swf-файлов самописный «твинер» — класс, интерполирующий определенное значение за заданное время, — начал вести себя странно.

Вместо своей обычной деятельности твинер мог пропускать значения, мог застревать на одном, а иногда просто в произвольный момент времени устанавливать переменной конечное значение и сообщать о завершении своей работы.

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

В процессе решения проблемы я узнал пару замечательных флеш-трюков, которыми спешу поделиться.

Казалось бы, какие могут быть трудности? Конструктору собственного твинера мы передаем ссылку на объект, параметр, который нужно изменить, конечное значение и время/кадры, в течение которых этот параметр должен плавно принять окончательный вид. Для простоты рассмотрим случай покадрового изменения значения:

  
  
  
  
  
   

public class SomeTweener { private var _obj:Object; private var _paramName:String; private var _endValue:Number; private var _frames:Number; public function SomeTweener(obj:Object, paramName:String, endValue:Number, frames:Number) { _obj = obj; _paramName = paramName; _endValue = endValue; _frames = frames; } }

Да, самый простой способ определить, когда пришло время обновить значение, — это подписаться на событие.

Событие.

ENTER_FRAME передано конструктору ДисплейОбъект .

Однако мы не ищем легких путей и делаем твинер универсальным.

То есть тот, который меняет параметры не только ДисплейОбъект 'А.

Поэтому вам придется использовать еще одну недокументированную малоизвестную возможность as3:

Любой DisplayObject, даже тот, который не добавлен в DisplayList, регулярно получает событие входа в кадр.

Изменение конструктора:

import flash.display.Shape; import flash.events.Event; import flash.events.IEventDispatcher; public class SomeTweener { private var _obj:Object; private var _paramName:String; private var _endValue:Number; private var _frames:Number; private var eventDispatcher:IEventDispatcher; public function SomeTweener(obj:Object, paramName:String, endValue:Number, frames:Number) { _obj = obj; _paramName = paramName; _endValue = endValue; _frames = frames; eventDispatcher = new Shape(); eventDispatcher.addEventListener(Event.ENTER_FRAME, tween); } private function tween(e:Event):void { } }

Вы можете проверить это сами.

Метод подросток будет вызываться правильно при fps. Вперед, продолжать.

Опять по самому простому пути - каждое испытание подросток меняем переданное значение на такое же значение (тип анимации easyNone — то есть униформа).

Для этого лучше в конструкторе посчитать покадровое приращение, исходя из разницы конечного и начального значений и длительности двойника, и записать приращение в поле класса.

В самом методе подросток проверим, сколько кадров уже длилось двойникование и по достижении указанного значения прервем двойникование:

import flash.display.Shape; import flash.events.Event; import flash.events.IEventDispatcher; public class SomeTweener { private var _obj:Object; private var _paramName:String; private var _endValue:Number; private var _frames:Number; private var eventDispatcher:IEventDispatcher; private var increment:Number; public function SomeTweener(obj:Object, paramName:String, endValue:Number, frames:Number) { _obj = obj; _paramName = paramName; _endValue = endValue; _frames = frames; increment = (endValue - Number(obj[paramName])/frames; eventDispatcher = new Shape(); eventDispatcher.addEventListener(Event.ENTER_FRAME, tween); } private function tween(e:Event):void { if (_frames == 0) { e.currentTarget.removeEventListener(e.type, tween); return; } obj[paramName] += increment; _frames--; } }

Нет ничего плохого в использовании рамки в качестве счетчика оставшихся «тиков» двойников: мы договорились не использовать секунды в качестве времени двойников.

Более того, кроме параметра конструктора рамки нигде не используется и никто (даже мы) не обвинит нас в изменении переданной по значению переменной в своих целях.

Казалось бы, все готово.

И это даже будет работать, и в большинстве случаев тоже будет работать корректно.

Но есть один случай, когда это ни при каких обстоятельствах не даст нормального результата.

Позвольте мне привести вам небольшой пример:

import flash.display.Sprite; import flash.events.Event; public class SomeClass extends Sprite { private var sprite:Sprite; public function SomeClass() { sprite = new Sprite(); sprite.x = 1; sprite.addEventListener(Event.ENTER_FRAME, traceSome); var tween:SomeTweener = new SomeTweener(sprite, 'x', -1, 40); } private function traceSome(e:Event):void { trace(sprite.x); } }

Что нам даст трассировка в результате вызова этого кода? Логически это столбец из сорока чисел, уменьшающихся с шагом 0,05. На практике любой ДисплейОбъект есть как минимум еще одна недокументированная особенность: ее координаты (и, возможно, некоторые другие свойства) всегда кратны 0,05. Попытка присвоить им некратное значение завершится неудачей: при следующем рендеринге оно будет округлено до ближайшего числа, кратного нулю.

В данном конкретном примере этот эффект нам грозить не должен (фактически он проявляется во всей красе), но, например, увеличив значение кадров, во время которых должно проявляться двойникование, до 80, мы получим приращение, равное до 0,025, и кривая застынет на нуле, никогда не достигая -1. Есть еще одна особенность.

Время выполнения FlashPlayer тип Число представляет собой 64-битное число с плавающей запятой.

Из-за этого совпадения происходят довольно часто.

Самый простой способ объяснить это на примере:

trace(String(-.

35 - .

05)) // 0.39(9)97

Вот как ведет себя flash со всеми значениями типа Число (который также включает в себя поля координат ДисплейОбъект ), с этим ничего не поделаешь.

Вполне естественно, что пример метода трассировки нашего класса SomeClass потерпит неудачу даже при дублировании на 40 кадрах.

Практика показала, что спрайт.x не сможет двигаться точно от значения -0,35, если каждый кадр округлять до него.

Цикл такой:

  1. Возьмите значение поля объекта (-0,35)
  2. Увеличиваем его на величину приращения (-0,35 + (-0,05) = -0,39(9)97)
  3. Записываем его в поле объекта (-0.39(9)97)
  4. (Скрытый обязательный) Значение поля координат экземпляра DisplayObject округлено (-0,35).

  5. Входим в следующий кадр и берем значение поля объекта (-0,35)
  6. ПЕРЕЙТИ К 2
От скрытого четвертого пункта избавиться невозможно.

Однако ошибка, вызванная симбиозом математики Число и ограничения на значения координат ДисплейОбъект мы обойдемся ровно тремя дополнительными строками.

Для этого нужно изменить первую и пятую точки цикла и вместо перманентно «разрушающегося» хранилища ценностей создать свой собственный:

import flash.display.Shape; import flash.events.Event; import flash.events.IEventDispatcher; public class SomeTweener { private var _obj:Object; private var _paramName:String; private var _endValue:Number; private var _frames:Number; private var eventDispatcher:IEventDispatcher; private var increment:Number; private var currentValue:Number; public function SomeTweener(obj:Object, paramName:String, endValue:Number, frames:Number) { _obj = obj; _paramName = paramName; _endValue = endValue; _frames = frames; currentValue = Number(obj[paramName]); increment = (endValue - Number(obj[paramName])/frames; eventDispatcher = new Shape(); eventDispatcher.addEventListener(Event.ENTER_FRAME, tween); } private function tween(e:Event):void { if (_frames == 0) { e.currentTarget.removeEventListener(e.type, tween); return; } currentValue += increment; obj[paramName] = currentValue; _frames--; } }

Казалось бы, причем здесь MVC? :-) P.S.: Да, существующий код твинера можно и нужно улучшать.

Добавьте отложенный запуск, отправку сообщений.

Вы можете добавить замедление и другие двойники с несколькими полями.

Но к данной задаче это не имеет отношения, а потому оставим это в качестве домашнего задания.

Теги: #as3 #as3 #tricks #tween #Adobe Flash

Вместе с данным постом часто просматривают: