0
votes

I'm new to ActionScript 3.0. I tried a tutorial at http://www.senocular.com/flash/tutorials/as3withmxmlc/ . The demo program animates a ball and allows it to be dragged.

There was a problem with the program as written. When you drag the mouse outside the stage and release the mouse button, the ball wouldn't get the MOUSE_UP event. The code, therefore would never call stopDrag(). I searched stackoverflow for suggestions, and one suggestion was to listen to MOUSE_UP with the stage as well as the ball and add some logic for dealing with it.

I added some code to do this. I also refactored the program as written because it was pretty disorganized. Here's what I have now:

package {
    import flash.display.Sprite;
    import flash.events.Event;
    import flash.events.MouseEvent;
    import flash.geom.Point;
    import flash.geom.Rectangle;

    public class BallToss extends Sprite {
        private var ball:TossableBall;
        // mouse position at last call to trackMouseMvt()
        private var lastMousePos:Point = new Point();
        // delta mouse movement from frame L-1 to frame L, where L is last frame
        private var lastDeltaMouse:Point = new Point();

        public function BallToss() {
            var stageBounds:Rectangle = new Rectangle(0, 0, stage.stageWidth, 
                                                      stage.stageHeight);
            ball = new TossableBall(50, stageBounds);
            ball.x = stageBounds.width/2;
            ball.y = stageBounds.height/2;
            addChild(ball);
            ball.addEventListener(MouseEvent.MOUSE_DOWN, grabBall);
            // however I order the next two calls to addEventListener(), it seems 
            // that the ball's MOUSE_UP gets handled before the stage's MOUSE_UP
            stage.addEventListener(MouseEvent.MOUSE_UP, handleStageMouseUp);
            ball.addEventListener(MouseEvent.MOUSE_UP, releaseBall);
            // initialize 'lastMousePos' and set up 'trackMouseMvt' to be called on
            // every frame
            lastMousePos = new Point(mouseX, mouseY);
            ball.addEventListener(Event.ENTER_FRAME, trackMouseMvt);
        }

        private function grabBall(evt:MouseEvent):void {
            trace("in grabBall");
            // set ball 'glideVector' to (0,0) so it will stop moving
            ball.setGlideVector(new Point(0,0));
            ball.startDrag();
        }

        private function releaseBall(evt:MouseEvent):void {
            trace("in releaseBall");
            ball.stopDrag();
            // set up the ball to glide at the rate of 'lastDeltaMouse'
            ball.setGlideVector(lastDeltaMouse);
        }

        private function trackMouseMvt(evt:Event):void {
            var currMouse:Point = new Point(mouseX, mouseY);
            lastDeltaMouse = currMouse.subtract(lastMousePos);
            lastMousePos = currMouse;
        }

        private function handleStageMouseUp(evt:Event):void {
            trace("in handleStageMouseUp"); 
            ball.stopDrag();
            var stageBounds:Rectangle = new Rectangle(0, 0, stage.stageWidth, 
                                                      stage.stageHeight);
            if (ball.x > stageBounds.right - 0.5)
                ball.x = stageBounds.right - 0.5;
            else if (ball.x < 0) 
                ball.x = 0;
            if (ball.y > stageBounds.bottom - 0.5)
                ball.y = stageBounds.bottom - 0.5;
            else if (ball.y < 0)
                ball.y = 0;
        }
    }
}

import flash.display.Sprite;
import flash.events.Event;
import flash.geom.Point;
import flash.geom.Rectangle;

class TossableBall extends Sprite {

    private var stageBounds:Rectangle;
    private var glideVector:Point = new Point();
    private var friction:Number = .95;

    public function TossableBall(size:Number, stageBoundsIn:Rectangle) {
        stageBounds = stageBoundsIn;
        graphics.lineStyle(1);
        graphics.beginFill(0xFF8000);
        graphics.drawCircle(0, 0, size/2);
        addEventListener(Event.ENTER_FRAME, glide);
    }

    public function setGlideVector(glideVectorIn:Point):void {
        glideVector = glideVectorIn;
    }

    private function glide(evt:Event):void {
        x += glideVector.x;
        y += glideVector.y;
        var shapeBounds:Rectangle = getBounds(parent);
        if (shapeBounds.left < stageBounds.left) {
            glideVector.x = Math.abs(glideVector.x);
        } else if (shapeBounds.right > stageBounds.right) {
            glideVector.x = -Math.abs(glideVector.x);
        }
        if (shapeBounds.top < stageBounds.top) {
            glideVector.y = Math.abs(glideVector.y);
        } else if (shapeBounds.bottom > stageBounds.bottom) {
            glideVector.y = -Math.abs(glideVector.y);
        }
        glideVector.x *= friction;
        glideVector.y *= friction;
    }
}           

I don't like this code very much. The problem comes down to not being able to detect all the cases in one place. I would like to write something like this:

if (..ball and stage both got MOUSE_UP..) {
    ..handle it..;
else if (..only stage got MOUSE_UP..) {
    ..handle it..;
}

This logic would let me write more foolproof, simpler case handling and clearer logic. As things stand, there is a lot of complex behavior that emerges from this way of organizing the code.

The event listening model doesn't seem to make this possible. The response to events must happen individually, or must it? Is there a way to detect events that are "in the queue"?

Alternatively, I could avoid using startDrag(), i.e. avoid making the ball Sprite draggable, and have only the stage listen to MOUSE_UP, then handle all the drag logic myself. That would also let me better handle questions like where I want the ball to be positioned when the user drags outside the stage. I wonder if that is better overall.

2
don't post all your code, just the relevant block for the scope of your question.ILikeTacos
@composerMike have you solved your problem?ZuzEL
Yes. I solved it based on information posted elsewhere.composerMike

2 Answers

0
votes

To track object being dragged this works good for me:


ball.addEventListener(MouseEvent.MOUSE_DOWN, onBallMouseDown)


    var _stage:Stage;

    private function onBallMouseDown(e:MouseEvent):void 
    {
        _stage = stage;
        stage.addEventListener(MouseEvent.MOUSE_UP, onStageMouseUp)
        stage.addEventListener(MouseEvent.MOUSE_MOVE, onStageMouseMove)
        ball.startDrag();
    }

    private function onStageMouseMove(e:MouseEvent):void 
    {
        // track ball coordinates
    }

    private function onStageMouseUp(e:MouseEvent):void 
    {
        ball.stopDrag();
        _stage.removeEventListener(MouseEvent.MOUSE_UP, onStageMouseUp)
        _stage.removeEventListener(MouseEvent.MOUSE_MOVE, onStageMouseMove)
    }
0
votes

What about that, after years of Flash programming only now have I discovered the joys of MouseEvent.RELEASE_OUTSIDE. No more ugly hacks needed.