3

나는이 문제를 이미 며칠 동안 처리해 왔습니다. 나는 재치가있다! 포럼, 문서 등 어디서나 확실한 답변을 찾을 수 없습니다., 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를 제거하기 위해 노력하고 결과는 항상 동일합니다.

그와 같은 프로세스를 설계하는 다른 방법이있을 수 있지만, 프로젝트가 매우 길기 때문에 (약 4000 줄의 코드) 및 ENTER FRAME 이 방법, 미쳤던, 또는, 일하지 않아야한다!!

도움을 주실 분은 미리 감사드립니다.

+0

그것이 작동합니까 ESCKeyPressHandler 대신 loadNextLevel을 사용한다면 – Pan

+0

나는 느낌도 가지고있다. 무슨 일이 일어나고 있는지 ENTER_FRAME 이벤트 리스너가 currentLevel 표시 객체의 모든 자식에 첨부되고 다른 레벨을 추가하기 위해이 객체를 제거하면 currentLevel Children 중 일부 또는 그와 같은 것으로 남아 있습니다. 나는 새로운 레벨을 로딩 한 후에도 여전히 플레이어가 여전히 이전에 존재했던 "보이지 않는"오브젝트와 충돌하기 때문에 이것이 일어날 수 있다고 생각합니다. –

+0

예 팬, 괜찮습니다. ESCKeyPress 핸들러를 사용할 때만 문제가 있습니다. 그것은 단지 그것을 제거하지 않습니다! –

답변

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()의 범위 내에서 정의된다 : I는 각 기능에 대한 참조, 그리고 당신과 비슷한 간단한 시나리오를 나타내는 숫자를 사용합니다 기능. 이 중첩 된 함수에 대한 참조를 나타 내기 위해 숫자 1을 사용할 수 있습니다. someCondition은 처음으로 true이고 따라서 example() 함수가 다시 호출됩니다.

example() 함수의 두 번째 실행에서 마우스 이벤트 처리기에 대한 새 참조가 만들어집니다 (# 2). 또한 이벤트 리스너를 다시 추가합니다. 이 시점에서 메모리에는 두 가지 이벤트 처리 함수가 있으며 두 가지 모두 이벤트가 전달 될 때 실행됩니다.

example()의 두 번째 호출에서 someConditionfalse인데 이제는 청취자를 제거하려고한다고 가정 해 봅시다.전화 할 때 :

removeEventListener(MouseEvent.CLICK, mouseClickHandler); 

이벤트 처리기 # 2를 참조합니다. 이벤트 처리기 # 1은 여전히 ​​존재하며 첫 번째 호출 범위에 숨겨져 있으므로 example()은 여기에서 제거 할 수 없습니다.

내 간단한 예제는 다음과 같이 나뉩니다.하지만 이벤트 처리기가 함수 내부에 중첩되어서는 안된다는 것이 명확 해지기를 바랍니다. 틀림없이, 이것은 당신의 현실적인 예에서 묘사하기가 더 어렵고 더욱 그렇습니다. 그러나 이것이 당신이 묘사하는 이슈의 전부는 아니지만, 대부분의 원인이라고 확신합니다.

+0

내가 이것을 분명하게 설명했는지 확실치 않다. 아마도 그 예가 과잉이었다. 만약 내가 좀 더 명확하게 설명 할 수 있다면 여기에 코멘트 ... –

+0

그건 그렇고, 이벤트 핸들러 (또는 심지어 익명 함수)에 대한 중첩 된 함수를 사용하는 것은 괜찮지 만 로직이 상황과 같이 복잡해지면 그렇지 않습니다. 따라서 이러한 기능을 "외부"범위로 이동하여 추가/제거되는 항목에 혼란을주지 않는 것이 좋습니다. –

+0

실제로 당신은 완벽한 감각을했습니다. 따라서 중첩 된 함수를 중첩 된 상태로 둘 필요가 없으면 중첩 된 함수를 외부로 이동하는 것입니다.또한 개발 중에 다른 작업을 단순화해야합니다. 임시 변수를 추가하는 일시적인 수정은 이벤트 처리기 양식을 "입력 프레임"자체를 제거 할 수있게 해주었기 때문에 해결되었지만 너무 복잡한 것처럼 보입니다 ... 정말 많이 감사합니다 @SunilD !! !! 그 모든 기능을 외부로 옮기는 데는 어느 정도 시간이 걸릴 것이기 때문에 null 참조가 많이 생길 수는 있지만 간단하기 때문에 가치가 있다고 생각합니다. –

0

"loadingNewGame"이라는 부울 변수를 만들고 onEnterFrame 외부에서 true로 변경하면 중첩 함수의 범위를 변경하지 않고도이를 해결할 수있었습니다 (선호하는 솔루션에 동의 함에도 불구하고). .. (이것은 속임수를 썼는지 사실이 할당이 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 
    }