从内存中删除后重新实例化时对象的阶段引用丢失显示列表

时间:2015-03-23 01:04:10

标签: actionscript-3

我正在构建游戏。当您死亡或赢得关卡时,系统会提示您继续或返回主菜单。如果您选择进入主菜单,则可以开始新游戏。当你开始一个新游戏时,游戏就是再次创建对象&它的孩子们已经失去了对舞台的参考。我不确定为什么会这样,我花了一个多星期试图找出原因。这里有一些来自游戏的代码(以及代码的描述),希望能够提供足够的洞察力来解决问题的原因:

如果"新游戏"在startMenu上单击按钮,将调度NavigationEvent.START事件。当级别完成时,WeeBeeGame将调度LevelEvent.COMPLETE事件。

public class DocumentClass extends MovieClip {

    public var startMenu:StartMenuGenerator = new StartMenuGenerator();
    public var weeBeeGame:WeeBeeGame;
    public var youWonBox:YouWonBox = new YouWonBox();

    public function DocumentClass() {
        // constructor code
        addChild(startMenu);
        startMenu.addEventListener(NavigationEvent.START, startGameHandler);
    }

    public function startGameHandler(e:NavigationEvent) : void {
        this.removeChild(startMenuBG);
        removeChild(startMenu);

        weeBeeGame = new WeeBeeGame();
        this.addChild(weeBeeGame);

        weeBeeGame.addEventListener(LevelEvent.COMPLETE, levelCompleteHandler);
    }

    public function levelCompleteHandler(e:LevelEvent) : void {

        youWonBox.x = this.stage.stageWidth/2;
        youWonBox.y = this.stage.stageHeight/2;
        addChild(youWonBox);

        youWonBox.addEventListener(MouseEvent.CLICK, mouseClickHandler);
    }

    private function mouseClickHandler(e:MouseEvent) : void {
        if(e.target.name === "mainmenubtn"){
            mainmenuHandler();  
        }
    }

    private function continueHandler() : void {
        youWonBox.removeEventListener(MouseEvent.CLICK, mouseClickHandler);
    }

    private function mainmenuHandler() : void {
        youWonBox.removeEventListener(MouseEvent.CLICK, mouseClickHandler);
        WeeBeeGame.collisionDOC.removeChildren();
        removeChild(weeBeeGame);
        weeBeeGame = null;

        this.addChild(startMenuBG);
        addChild(startMenu);
        removeChild(youWonBox);
    }
}

调度LevelEvent.COMPLETE事件的代码未显示,但在级别完成时调度。 collisionDOC需要是静态的,因为它在很多其他类中都需要它并保存第三方像素级碰撞检测所需的显示对象。

public class WeeBeeGame extends MovieClip {

    public var bee: Bee;
    public var beeHurt:BeeHurt;

    public var spawningDaemon:SpawningDaemon;

    public static var collisionDOC:DisplayObjectContainer;

    public function WeeBeeGame() {
        this.addEventListener(Event.ADDED_TO_STAGE, addedToStageHandler, false, 0, true);
    }

    private function addedToStageHandler(e:Event) : void {
        this.removeEventListener(Event.ADDED_TO_STAGE, addedToStageHandler);

        collisionDOC = new MovieClip();
        addChild(collisionDOC);

        bee = new Bee();
        collisionDOC.addChild(bee);

        beeHurt = new BeeHurt(bee.x, bee.y);
        addChild(beeHurt);
        beeHurt.visible = false;

        spawningDaemon = new SpawningDaemon(currentLevel);

        this.addEventListener(LevelEvent.COMPLETE, levelCompleteHandler, false, 0, true);
    }

    private function levelCompleteHandler(e:LevelEvent) : void {
        removeEventListener(Event.ENTER_FRAME, enterFrameHandler);
    }
}

抛出1009错误的第一行(无法访问空对象引用的属性或方法)是包含stage.mouseX的行,因为阶段引用为null。

public class Bee extends MovieClip {

    public function Bee() {
        this.addEventListener(Event.ADDED_TO_STAGE, addedToStageHandler);
    }

    private function addedToStageHandler(e:Event) : void {
        this.removeEventListener(Event.ADDED_TO_STAGE, addedToStageHandler);
        this.x = this.stage.stageWidth / 2;
        this.y = this.stage.stageHeight - this.stage.stageHeight / 9;
        this.addEventListener(Event.ENTER_FRAME, enterFrameHandler, false, 0, true);
    }

    private function enterFrameHandler(e:Event) : void {
        if(stage == null){
            trace('stage is null' + stage.mouseX);
        }
    }
}

首次打开swf并创建一个新的WeeBeeGame时,它的子节点会引用该舞台。但是当WeeBeeGame和它的孩子从显示列表和内存中删除时,他们就会失去参考,即使他们重新实例化,他们的引用仍然会丢失。我该如何解决?我很困惑。谢谢大家!!

1 个答案:

答案 0 :(得分:3)

即使从舞台中删除了显示对象,

ENTER_FRAME处理程序仍继续执行。因此,当您removeChild(weeBeeGame)时,这些ENTER_FRAME处理程序仍在尝试访问每个帧stage.mouseX。您需要停止ENTER_FRAME处理程序。

最简单的解决方法可能是添加Event.REMOVED_FROM_STAGE处理程序以删除Event.ENTER_FRAME处理程序。

更好的修复IMO不是从游戏对象中添加任何ENTER_FRAME处理程序,而是公开update()之类的公共函数,它从ENTER_FRAME中的WeeBeeGame处理程序调用游戏运行时{1}}然后,要完全停止游戏,只需删除单个ENTER_FRAME处理程序即可停止所有更新。这是一种常见的做法。