FLEXYGEN

Adobe Flex and AIR development

Shapes and Sprites

In recent weeks I’ve been diving into the world of Flex custom components, and the drawing APIs. Many years ago I worked on the Transform Panel in InDesign, but it’s been a while since I did any graphics-intensive programming. It’s great fun building slick user experiences with the standard Flex components, but I’d also love to work on a juicy RIA like Buzzword or SproutBuilder.

One of my personal projects is a board game. I have the board (a subclass of UIComponent) rendering a collection of tiles, which are subclasses of Sprite. It was necessary (I think) to use Sprite, because each tile has a child text label and the underlying Graphics object doesn’t have any sort of drawText() method. The next step was to look at handling selection and dragging of tiles.

Chet Haase recently joined the Flex SDK team and posted a drawing sample called TopDrawer on his blog. This was most helpful! TopDrawer has an ArtCanvas class (also a UIComponent), and the drawn shapes (rectangles, lines etc.) are represented by subclasses of Shape added as children of the canvas.

I cheerfully copied a couple of lines from the ArtCanvas event handler for MouseDown events:

            	var selectPoint:Point = localToGlobal(new Point(event.localX, event.localY));
                for each (var shape:ArtShape in shapes)
                {
                    if (shape.hitTestPoint(selectPoint.x, selectPoint.y, true))
                    {
                        selectShape(shape);
                        break;
                    }
                }

In my game, the hitTestPoint() call did not succeed when I clicked on a tile (a Sprite) but did, if I changed it to a Shape. The simple fix was to call event.target.localToGlobal(). So one difference between Sprites and Shapes is that a child Sprite may be the “target” property of a MouseEvent, but a Shape won’t. The code worked in TopDrawer because the the shapes are Shapes, and the localX and localY properties were already in the coordinate system of the ArtCanvas.

Before I figured this out, I tried to create an AS3-only test case with a Shape child and Sprite child. The mouse event handler wasn’t even reached. I learned (via O’Reiily’s AS3 Cookbook) that the main application class does not receive mouse events, though it does receive enterFrame, for example. So here’s a one-file non-object-oriented test that does work as expected. You can pop this in an AS3-only FlexBuilder project and run it in the debugger. I hope this is helpful to someone.


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

    // Testing Hit detection

    public class TestHitDetection extends Sprite
    {
        private var _rootSprite:Sprite;
        private var _circleSprite:Sprite;
        private var _circleShape:Shape; 

        public function TestHitDetection()
        {
            super();

            _rootSprite = new Sprite(); // This is necessary because the main application class does not receive mouse events
            _rootSprite.x = 50;
            _rootSprite.y = 50;

            _rootSprite.graphics.beginFill(0xDDDDDD);
            _rootSprite.graphics.drawRect(0,0,500,500);
            _rootSprite.graphics.endFill();

            _circleSprite = new Sprite();
            _circleSprite.graphics.beginFill(0x00FF00);
            _circleSprite.graphics.drawCircle(60,60,50);
            _circleSprite.graphics.endFill();
            _circleSprite.x = 10;
            _circleSprite.y = 10;

            _rootSprite.addChild(_circleSprite);

            _circleShape = new Shape();
            _circleShape.graphics.beginFill(0xFF0000);
            _circleShape.graphics.drawCircle(0,0,75);
            _circleShape.graphics.endFill();
            _circleShape.x = 200;
            _circleShape.y = 200;

            _rootSprite.addChild(_circleShape);

            this.addChild(_rootSprite);

            _rootSprite.addEventListener(MouseEvent.MOUSE_DOWN, handleMouseDown);
        }

        private function handleMouseDown(event:MouseEvent):void{
            trace("mousedown");
            var selectPoint:Point = event.target.localToGlobal(new Point(event.localX, event.localY));
            if (_circleSprite.hitTestPoint(selectPoint.x, selectPoint.y, true)) {
                trace("hit sprite");
            }
            if (_circleShape.hitTestPoint(selectPoint.x, selectPoint.y, true)) {
                trace("hit shape");
            }
        }
    }
}

March 27, 2008 Posted by richard | Uncategorized | | No Comments Yet