Actionscript 3 AS3-MouseEvent.CLICK触发在添加侦听器之前发生的鼠标单击

Actionscript 3 AS3-MouseEvent.CLICK触发在添加侦听器之前发生的鼠标单击,actionscript-3,Actionscript 3,我是AS3新手,制作了一个简单的“小行星”游戏,屏幕上有一个游戏和一个让用户再次玩的重置按钮。当用户单击重置按钮时,屏幕上的游戏和重置按钮将从舞台上移除,游戏本身将与eventListeners一起添加到舞台上。其中一个是MouseEvent。单击添加到stage的listener,它调用fireball函数。此函数绘制一个项目符号并将其添加到后台(代码的其他部分随后使项目符号在屏幕上移动) 我遇到的问题是,当用户单击重置按钮时,gameover屏幕被正确移除,游戏本身(玩家、小行星、event

我是AS3新手,制作了一个简单的
“小行星”
游戏,屏幕上有一个游戏和一个让用户再次玩的
重置按钮。当用户单击重置按钮时,屏幕上的游戏和重置按钮将从舞台上移除,游戏本身将与eventListeners一起添加到舞台上。其中一个是
MouseEvent。单击添加到
stage
listener,它调用
fireball
函数。此函数绘制一个项目符号并将其添加到后台(代码的其他部分随后使项目符号在屏幕上移动)

我遇到的问题是,当用户单击重置按钮时,gameover屏幕被正确移除,游戏本身(玩家、小行星、eventListeners)被正确添加到舞台上,但同时,即使用户在单击
重置按钮后未单击,
项目符号
也会触发

我的
gameOver()
函数如下:

stage.removeChild() all objects
stage.removeEventListener() all listeners
null out all objects

draw and add to the stage the game over text and resetButton
addEventListener(MouseEvent.CLICK, onReset) to the resetButton
stage.removeChild() the gameover text and the resetButton
call gameStart();
initialize variables
draw and add player and asteroids on the screen
add eventListeners including MouseEvent.Click, fireBullet
然后,
onReset()
函数如下所示:

stage.removeChild() all objects
stage.removeEventListener() all listeners
null out all objects

draw and add to the stage the game over text and resetButton
addEventListener(MouseEvent.CLICK, onReset) to the resetButton
stage.removeChild() the gameover text and the resetButton
call gameStart();
initialize variables
draw and add player and asteroids on the screen
add eventListeners including MouseEvent.Click, fireBullet
gameStart()
函数如下所示:

stage.removeChild() all objects
stage.removeEventListener() all listeners
null out all objects

draw and add to the stage the game over text and resetButton
addEventListener(MouseEvent.CLICK, onReset) to the resetButton
stage.removeChild() the gameover text and the resetButton
call gameStart();
initialize variables
draw and add player and asteroids on the screen
add eventListeners including MouseEvent.Click, fireBullet
我在每个函数中添加了跟踪,以查看发生了什么,以下是流程:

added fireBullet listener //this is gameStart() function being called from Main() and adding everything to the stage the first time
fired bullet //shooting at the asteroids
fired bullet
fired bullet
fireBullet listener should have been removed //this is gameOver() being called that removes everything from the stage and adds the resetButton
clicked on reset
added fireBullet listener //gameStart() being called again from onReset() function
fired bullet //I did not click a second time after clicking on reset
我在某个地方读到,无论是否有侦听器正在侦听事件,事件都会一直被调度,因此我怀疑我的MouseEvent.CLICK侦听器是从单击重置按钮时开始拾取鼠标按钮单击的,即使该侦听器随后被添加到后台

我没有足够的AS3或编程经验来判断这是否真的是这样,我可以做些什么来确保
MouseEvent.CLICK
listener不会对添加到后台之前发生的任何单击做出响应,因此对此提供的任何帮助都将不胜感激

====

编辑 我假设我有一个逻辑问题,或者对AS3和flash一无所知,所以我只使用上面的伪代码。下面是完整的.as文件的链接,包括生成的.swf 下面是完整的相关功能

私有函数startGame():void
这是从Main调用的

{
        //initialize variables
        bulletArray = [];
        cleanupBullets = [];
        bulletSpeed = 10;
        score = 0;

        asteroid1Speed = 0;
        asteroid2Speed = 0;
        asteroid3Speed = 0;
        asteroid4Speed = 0;

        //draw player and asteroids
        ship = drawPlayer();
        asteroid1 = drawAsteroid();
        asteroid2 = drawAsteroid();
        asteroid3 = drawAsteroid();
        asteroid4 = drawAsteroid();

        //embarrasing and inefficient code to get random number between -5 and 5 without a 0
        asteroid1Speed = Math.ceil(Math.random() * 10 -5);
        if (asteroid1Speed == 0)
            asteroid1Speed = returnNonZero(asteroid1Speed);

        asteroid2Speed = Math.ceil(Math.random() * 10 -5);
        if (asteroid2Speed == 0)
            asteroid2Speed = returnNonZero(asteroid2Speed);

        asteroid3Speed = Math.ceil(Math.random() * 10 -5);
        if (asteroid3Speed == 0)
            asteroid3Speed = returnNonZero(asteroid3Speed);

        asteroid4Speed = Math.ceil(Math.random() * 10 -5);
        if (asteroid4Speed == 0)
            asteroid4Speed = returnNonZero(asteroid4Speed);

        //trace(asteroid1Speed, asteroid2Speed, asteroid3Speed, asteroid4Speed);

        //add asteroids to stage
        stage.addChild(asteroid1);
        stage.addChild(asteroid2);
        stage.addChild(asteroid3);
        stage.addChild(asteroid4);

        //position player and add to stage
        ship.x = 40;
        ship.y = 40;

        stage.addChild(ship);

        //add event listeners
        stage.addEventListener(Event.ENTER_FRAME, onFrame);
        stage.addEventListener(MouseEvent.CLICK, fireBullet);
        trace("added fireBullet listener");
}
private function gameOver():void
这是从一个我没有包含的
onFrame
(调用每帧)函数调用的(它太大而且不完全相关)。当所有小行星都被移除时,它被称为

{
        //remove any remaining bullets off the screen
        for each (var item:Sprite in bulletArray)
        {
            stage.removeChild(item);
        }
        //null out objects and remove listeners
        bulletArray = null;
        stage.removeEventListener(Event.ENTER_FRAME, onFrame);
        stage.removeEventListener(MouseEvent.CLICK, fireBullet);
        //check if the listener has actually been removed
        if (!(stage.hasEventListener(MouseEvent.CLICK))) {
            trace("fireBullet listener should have been removed");
        }

        stage.removeChild(ship);
        ship = null

        //graphic for resetButton
        resetButton = new Sprite();
        resetButton.graphics.beginFill(0xFFFFFF);
        resetButton.graphics.drawRect(0, 0, 100, 50);
        resetButton.graphics.endFill();

        //position for resetButton
        resetButton.x = 250;
        resetButton.y = 300;

        //text for resetButton
        resetTextField = new TextField();
        var resetTextFormat:TextFormat = new TextFormat();
        resetTextFormat.size = 30;
        resetTextFormat.color = 0x000000;
        resetTextField.defaultTextFormat = resetTextFormat;
        resetTextField.selectable = false;
        resetTextField.text = "RESET";
        resetButton.addChild(resetTextField);

        //add resetButton and listener
        stage.addChild(resetButton);
        resetButton.addEventListener(MouseEvent.CLICK, onReset);

        //gameover text
        gameOverTxtField = new TextField();
        gameOverFormat = new TextFormat();
        gameOverFormat.size = 50;
        gameOverFormat.color = 0xFFFFFF; 
        gameOverFormat.align = "center";
        gameOverTxtField.defaultTextFormat = gameOverFormat;
        gameOverTxtField.selectable = false;
        gameOverTxtField.text = "GAME OVER";
        gameOverTxtField.width = 660;
        gameOverTxtField.height = 200;
        gameOverTxtField.x = -10;
        gameOverTxtField.y = 20;

        stage.addChild(gameOverTxtField);
}
onReset私有函数(e:MouseEvent):void

{
        trace("clicked on reset");
        //remove gameover objects and null them
        resetButton.removeEventListener(MouseEvent.CLICK, onReset);
        stage.removeChild(gameOverTxtField);
        stage.removeChild(resetButton);
        resetButton = null;
        gameOverTxtField = null;

        //restart the game
        startGame();
}

在我看来,游戏的上一次迭代似乎没有删除鼠标。单击事件侦听器。即使游戏被移除,MOUSE.CLICK事件仍将继续触发您添加到其中的任何处理程序,例如;addEventListener(鼠标单击,)。当游戏被移除时,你还需要移除VentListener(MOUSE.CLICK,)。

发生的事情是
MouseEvent。CLICK
是一个。在Flash中,事件有三个阶段:“捕获阶段”、“目标阶段”和“冒泡阶段”。你可以在这本书里读到

重置按钮的单击事件处理程序发生在“目标”阶段。如果在重置按钮单击处理程序中跟踪事件的阶段,它将显示
event.phase
为2。根据,1=“捕获阶段”,2=“目标阶段”,3=“气泡阶段”

重置按钮单击处理程序完成其工作后,事件会在显示列表中出现气泡。由于
阶段
位于显示列表的顶部,因此单击事件“冒泡”到该阶段。到那时,您已经再次开始游戏,并添加了舞台的点击事件处理程序。因此阶段的点击处理程序也会被触发

您可以通过跟踪
bulletFired()
方法中
event.phase
的值来确认这一点:

private function fireBullet(e:MouseEvent):void 
{ 
    // most of this time it will trace out 2 for the phase
    // except when you click on an asteroid when firing or
    // click the reset button
    trace("fired bullet, event phase: " + e.eventPhase);
    bullet = drawBullet(); 
    bullet.y = ship.y; 
    bullet.x = ship.x + (ship.width / 2); 
    bulletArray.push(bullet); 
    stage.addChild(bullet); 
} 
private function onReset(e:MouseEvent):void 
{
    // prevent this event from bubbling
    e.stopPropagation();
    trace("clicked on reset"); 
    //remove gameover objects and null them 
    resetButton.removeEventListener(MouseEvent.CLICK, onReset); 
    stage.removeChild(gameOverTxtField); 
    stage.removeChild(resetButton); 
    resetButton = null; 
    gameOverTxtField = null; 
    //restart the game 
    startGame(); 
} 
要解决此问题,可以在
onReset()方法中停止事件冒泡:

private function fireBullet(e:MouseEvent):void 
{ 
    // most of this time it will trace out 2 for the phase
    // except when you click on an asteroid when firing or
    // click the reset button
    trace("fired bullet, event phase: " + e.eventPhase);
    bullet = drawBullet(); 
    bullet.y = ship.y; 
    bullet.x = ship.x + (ship.width / 2); 
    bulletArray.push(bullet); 
    stage.addChild(bullet); 
} 
private function onReset(e:MouseEvent):void 
{
    // prevent this event from bubbling
    e.stopPropagation();
    trace("clicked on reset"); 
    //remove gameover objects and null them 
    resetButton.removeEventListener(MouseEvent.CLICK, onReset); 
    stage.removeChild(gameOverTxtField); 
    stage.removeChild(resetButton); 
    resetButton = null; 
    gameOverTxtField = null; 
    //restart the game 
    startGame(); 
} 

请显示真实代码,而不是伪代码。在调用gameStart()之前,跟踪(stage.hasEventListener(MouseEvent.CLICK))是否为真?我使用该方法与if一起跟踪“fireBullet listener应该已删除”。我将在一分钟后发布实际的源代码。+1对于编辑和显示代码,它确实起到了作用:)这是在将resetButton添加到舞台之前在gameOver函数中完成的。@GarryWong这就是为什么我建议显示真实代码的原因,当你不这样做时,你会让我们假设一些事情。我们陷入了“哦,这可能是他似乎没有做的明显的事情……”的困境。如果代码太多,你需要做一些调试/调查(就像你用跟踪语句做的那样)我尽可能多地想清楚到底发生了什么,并将问题缩小到这三个函数和eventlisteners之间的流程,所以这是我最初包含的唯一内容。我现在用实际的代码编辑了这个问题。奇怪的是,在我试图回答这个问题(错误的,下面)之后,我在一个我正在工作的项目中发生了这样的事情。不确定我是否可以在这里问,但是,@Sunil D,你知道冒泡的时间吗。你说“到那时”新游戏已经被实例化了,这就是为什么同样的点击会触发子弹。在继续游戏之前,我认为它会解决(但显然不是)。“但是我们怎么知道冒泡需要多长时间?”Moosefacher问得好。事件冒泡将全部发生在同一帧内。查看上面的
onReset()
单击处理程序。当我单击reset按钮时,Flash渲染的当前帧被我们的事件处理程序中断(
onReset()
onReset()
还调用
startGame()
,因此执行其代码,将单击侦听器添加到
阶段。当
startGame()
完成时,Flash Player将“恢复”进程