В AS3 removeEventListener(Event.ENTER_FRAME) не работает
Я занимался этой проблемой уже несколько дней. Я в конце моего ума! Я не могу найти однозначного ответа ни на одном из форумов, документации и т. Д.
Все выглядит хорошо при первом запуске или при загрузке следующего уровня для пользователя, чтобы играть. Но если пользователь нажимает клавишу ESC для загрузки другого уровня, слушатель ENTER FRAME не удаляется, и он дублирует все триггеры в нем, показывая, что игрок идет очень быстро, и все фанки, потому что он построен поверх ранее экземпляр слушателя ENTER FRAME.
Я не знаю, есть ли у меня проблема с анонимной функцией или с неизвестным экземпляром, на который ссылается моя команда removeEvent... В итоге, я сдаюсь и мне нужна эта рабочая ПОМОЩЬ!!!
Вот код:
function initPlay():void
{
//code here determining what display object to add to the list and assign it to the currentLevel variable (a movieclip)
if(userIsLoadingOtherLevel){
removeEnterFrameListener();
addChild(currentLevel);
}
if(userIsGointToNextLevel)
addChild(currentLevel);
currentLevel.addEventListener(Event.ENTER_FRAME, onEnterFrame);
function onEnterFrame(event:Event):void
{
//collision detection, parallax scrolling, etc, etc is done here.
if(allCoinsCollected)
loadNextLevel();
if(ESCKeyPressed)
ESCKeyPressHandler();
}
function loadNextLevel():void
{
removeChild(currentLevel);
newLevelToLoad++
removeEnterFrameListener();
initPlay();
}
function ESCKeyPressHandler():void
{
removeChild(currentLevel);
initPlay();
}
function removeEnterFrameListener();
{
currentLevel.removeEventListener(Event.ENTER_FRAME,onEnterFrame)
trace("currentLevel.hasEventListener(Event.ENTER_FRAME) = "+currentLevel.hasEventListener(Event.ENTER_FRAME)); //outputs TRUE if called from loadNextLevel but FALSE if called from initPlay() !!!
}
}
Я также попытался добавить и удалить eventListener для stage, MovieClip(Root) или вообще ничего, и результат всегда одинаков.
Я знаю, что могут быть другие способы спроектировать такой процесс, но, пожалуйста, обратите внимание, что в данный момент я не очень гибок в выполнении этого, потому что проект очень длинный (около 4000 строк кода) и удаляет таким образом ENTER ENTER, безумно или не должен все еще работать!!
СПАСИБО заранее всем, кто хочет помочь.
2 ответа
Проблема заключается во вложенных функциях внутри initPlay()
метод.
Каждый раз, когда вы звоните initPlay()
вы определяете новые функции. Некоторые из этих вложенных функций вызывают initPlay()
самих себя.
Функции - это объекты (ссылки на память). Поэтому каждый раз, когда вы звоните initPlay()
вы делаете новые ссылки на новые функции. Поэтому, когда вы пытаетесь удалить прослушиватель событий, вы можете удалить только один из этих обработчиков событий (тот, что находится в текущей области выполнения).
Я не уверен, если я объясню это ясно, возможно, этот пример поможет. Я буду использовать числа для представления ссылок на каждую функцию и простой сценарий, который похож на ваш:
function example():void
{
addEventListener(MouseEvent.CLICK, mouseClickHandler);
function mouseClickHandler(event:Event):void
{
if (someCondition)
{
example();
}
else
{
removeEventListener(MouseEvent.CLICK, mouseClickHandler);
}
}
}
Когда мы запускаем эту функцию в первый раз, в рамках example()
функция. Позволяет использовать номер 1 для представления ссылки на эту вложенную функцию. someCondition
является true
в первый раз, и поэтому example()
функция вызывается снова.
На втором исполнении example()
функция, новая ссылка на обработчик событий мыши создается (#2). Мы также снова добавляем прослушиватель событий. На данный момент в памяти есть две функции обработки событий, и обе будут выполняться при отправке события.
Допустим, что во втором вызове example()
тот someCondition
является false
и теперь мы хотим удалить слушателя. Когда мы звоним:
removeEventListener(MouseEvent.CLICK, mouseClickHandler);
Это относится к обработчику событий #2. Обработчик событий #1 все еще существует, и потому что он скрыт в области первого вызова example()
это не может быть удалено здесь.
Мой простой пример разбивается после этого... но я надеюсь, что он проясняет, почему ваши обработчики событий не должны быть вложены в функцию. По общему признанию, это трудно описать, и тем более в реальном примере, подобном вашему. Но я вполне уверен, что это является источником большинства, если не всех, проблем, которые вы описываете.
Вот как мне удалось обойти это, не изменяя области действия вложенных функций (хотя я согласен, что это было бы предпочтительным решением), создав булеву переменную с именем "loadingNewGame" и изменив ее на true вне onEnterFrame (на самом деле, это назначение было сделано из initPlay(), а затем из onEnterframe я вызвал функцию removeEnterFrameListener().
Вот код на случай, если кто-то заинтересован:
// package, and other code here.
var loadingNewGame:Boolean = new Boolean(false);
function initPlay():void
{
//code here determining what display object to add to the list and assign
//it to the currentLevel variable (a movieclip)
if(userIsLoadingOtherLevel)
{
loadingNewGame = true;
removeEnterFrameListener();
addChild(currentLevel);
}
if(userIsGointToNextLevel)
addChild(currentLevel);
loadingNewGame:Boolean = false;
currentLevel.addEventListener(Event.ENTER_FRAME, onEnterFrame);
function onEnterFrame(event:Event):void
{
if(loadingNewGame)
removeChild(currentLevel);
//collision detection, parallax scrolling, etc, etc is done here.
if(allCoinsCollected)
loadNextLevel();
if(ESCKeyPressed)
ESCKeyPressHandler();
}
function loadNextLevel():void
{
removeChild(currentLevel);
newLevelToLoad++
removeEnterFrameListener();
initPlay();
}
function ESCKeyPressHandler():void
{
initPlay();
}
function removeEnterFrameListener();
{
currentLevel.removeEventListener(Event.ENTER_FRAME,onEnterFrame)
trace("currentLevel.hasEventListener(Event.ENTER_FRAME) = "+currentLevel.hasEventListener(Event.ENTER_FRAME));
//outputs true
}