How to use a movieclip as a boundary for another dragable object in AS3? - actionscript-3

How to use a movieclip as a boundary for another dragable object?
All I know is, we can use a rectangle for boundary in start drag .
dragable_mc.addEventListener(MouseEvent.MOUSE_DOWN, start_drag);
function start_drag(e:MouseEvent)
{
var rect:Rectangle = new Rectangle(0,0,100,100);
dragable_mc.startDrag(false, rect);
}
What is the way to drag a movieclip in another movieclip in flash by as3?
(like as I shown in pic)

If it is a dynamic shape, you'd have to log the x,y coordinates of the drag-able object every frame upon drag. Then do a bitmap-hitpoint test with the boundary to check if the object is out of bounds. If it is outside, return to the last coordinates that isn't out of bound.
edit:
The two variables you need to rename are dragTarget and bound_mc
dragTarget is your dragable_mc
bound_mc is the name of the movieclip of your boundary.
bound_mc need to be in png format, and the out of bounds area MUST be transparent. example:
import flash.geom.Point;
import flash.geom.Rectangle;
import flash.events.MouseEvent;
import flash.events.Event;
import flash.display.BitmapData;
stop();
var bmd:BitmapData =new BitmapData(600, 400, true, 0x000000);
var rect:Rectangle;
var lastPt:Point = new Point();
function init():void {
rect = new Rectangle(0, 0, stage.stageWidth, stage.stageHeight);
setUpBitmap();
}
function setUpBitmap():void {
bmd.draw(bound_mc);
dragTarget.addEventListener(MouseEvent.MOUSE_DOWN, start_drag);
}
function start_drag(event:MouseEvent):void {
dragTarget.removeEventListener(MouseEvent.MOUSE_DOWN, start_drag);
stage.addEventListener(MouseEvent.MOUSE_UP, stop_drag);
lastPt.x = dragTarget.x;
lastPt.y = dragTarget.y;
dragTarget.startDrag(false, rect);
this.addEventListener(Event.ENTER_FRAME, logPoint);
}
function stop_drag(event:MouseEvent):void {
this.removeEventListener(Event.ENTER_FRAME, logPoint);
stage.removeEventListener(MouseEvent.MOUSE_UP, stop_drag);
dragTarget.addEventListener(MouseEvent.MOUSE_DOWN, start_drag);
dragTarget.stopDrag();
}
function logPoint(event:Event):void {
var curPoint:Point = new Point(stage.mouseX, stage.mouseY);
if ( bmd.hitTest(new Point( bound_mc.x, bound_mc.y ), 0, curPoint) ) {
lastPt = curPoint;
} else {
dragTarget.x = lastPt.x;
dragTarget.y = lastPt.y;
stage.dispatchEvent(new MouseEvent(MouseEvent.MOUSE_UP));
}
}
init();

Related

ActionScript 3, handling MOUSE_UP outside stage

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.
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)
}
What about that, after years of Flash programming only now have I discovered the joys of MouseEvent.RELEASE_OUTSIDE. No more ugly hacks needed.

Dragging a movieclip as3

I have a Movieclip which is a child of another movieclip. I use startDrag() and stopDrag() with first (parent) Movieclip but the nested one doesnt move. Why?
relevant code on stage:
var main:rt = new rt(); // rt being a class in my library, which extends MovieClip object.
addChild(main);
stage.addEventListener(MouseEvent.MOUSE_DOWN, stage_mousedownHandler);
stage.addEventListener(MouseEvent.MOUSE_UP, stage_mouseupHandler);
function stage_mousedownHandler(event_object:Event) {
main.startDrag();
}
function stage_mouseupHandler(event_object:Event) {
main.stopDrag();
}
rt's constructor code:
public function rt() {
var bmp_bar:Bitmap;
var br_male:Bar_male; // Bar_male is a Bitmap in my library. (AS Linkage)
bmp_bar = new Bitmap(br_male);
this.addChild(bmp_bar);
}
Made this simple program to test and it works as expected in Flash Develop, there is a main sprite and a child bitmap when I mouse down any where on stage the main sprite is dragged and the child bitmap is moved.
So I am guessing there is something going on in your workflow in flash professional and linkage. Make sure the mouse event handlers are triggered put some breakpoints and debug.
package
{
import flash.display.Bitmap;
import flash.display.BitmapData;
import flash.display.Sprite;
import flash.events.Event;
import flash.events.MouseEvent;
import flash.geom.Rectangle;
public class Test extends Sprite
{
private var sp:Sprite = new Sprite();
public function Test()
{
addEventListener(Event.ADDED_TO_STAGE, onAddedToStage);
}
private function onAddedToStage(e:Event):void
{
removeEventListener(Event.ADDED_TO_STAGE, onAddedToStage);
var bmpData:BitmapData = new BitmapData(100, 100,false,0x000000);
bmpData.fillRect(new Rectangle(0, 0, 100, 100), 0xff0000);
var bmp:Bitmap = new Bitmap(bmpData);
sp.addChild(bmp);
addChild(sp);
stage.addEventListener(MouseEvent.MOUSE_DOWN, onMouseDown);
stage.addEventListener(MouseEvent.MOUSE_UP, onMouseUp);
}
private function onMouseUp(e:MouseEvent):void
{
sp.stopDrag();
}
private function onMouseDown(e:MouseEvent):void
{
sp.startDrag();
}
}
}

positioning bitmapdata

I have the following code:
public function Application()
{
loader = new Loader();
loader.contentLoaderInfo.addEventListener(Event.COMPLETE, completeHandler);
var urlRequest:URLRequest = new URLRequest("image/1.jpg");
loader.load(urlRequest);
addChild(loader);
}
private function completeHandler(e:Event):void{
loader.content.width = 800;
loader.content.scaleY = loader.content.scaleX;
piece = Math.round(loader.height/10);
drawBitmaps();
}
private function drawBitmaps():void{
var bmdata:BitmapData = new BitmapData(loader.width, piece, true, 0x000000);
bmdata.draw(loader);
var bitmap:Bitmap = new Bitmap(bmdata);
addChild(bitmap);
loader.visible = false;
}
the result is a bitmap wich contains a pice of the image. The height is 80. and it starts at the top of the image. But how can i tell the bitmapdata to start drawing the image from lets say 80pixels? so it draws a middle piece of the image? Because atm it allways draws from the top of the image.
You should use BitmapData::draw clipRect parameter.
Here is an example:
package {
import flash.geom.Rectangle;
import flash.display.Bitmap;
import flash.display.BitmapData;
import flash.display.Graphics;
import flash.display.Sprite;
public class BitmapDataTest extends Sprite {
public function BitmapDataTest() {
var c:Sprite = new Sprite();
var g:Graphics;
g = c.graphics;
g.beginFill(0xFF0000);
g.drawCircle(30,30,30);
g.endFill();
addChild(c);
c.x = 10;
c.y = 10;
var bmdata:BitmapData = new BitmapData(60, 60, true, 0x000000);
bmdata.draw(c,null,null,null, new Rectangle(0,30,30,30));
var bitmap:Bitmap = new Bitmap(bmdata);
addChild(bitmap);
bitmap.x = 80;
bitmap.y = 10;
}
}
}
Please notice that the target bitmap data should have the same dimensions as source or this won't work. If you really have to cut it down you should use BitmapData::copyPixels method.
The easiest way would be to apply a mask, dynamically. Here is a good example: Actionscript 3 and dynamic masks
You can create a rectangle, the height you're looking for and position it appropriately.
Hope this helps.

Need some help completing a bumptop-ish selection tool

I'm in the midst of creating a Bumptop styled selection tool. Right now I got as far as creating the tool itself (which actually works pretty good) and spreading some random square items on the stage. This is the class that creates the selection tool :
package com.reyco1.medusa.selectiontool
{
import flash.display.Sprite;
import flash.events.Event;
import flash.events.MouseEvent;
import flash.geom.Point;
public class SelectionBase extends Sprite
{
private var points:Array = [];
public function SelectionBase()
{
super();
addEventListener(Event.ADDED_TO_STAGE, initialize);
}
private function initialize(e:Event):void
{
removeEventListener(Event.ADDED_TO_STAGE, initialize);
points.push(new Point(mouseX, mouseY)); stage.addEventListener(MouseEvent.MOUSE_MOVE, handleMouseMove);
}
private function handleMouseMove(e:MouseEvent):void
{
graphics.clear();
graphics.beginFill(0x33CCFF, .5);
graphics.drawCircle(0, 0, 20);
graphics.endFill();
graphics.moveTo(0, 0);
graphics.lineStyle(1.5, 0x33CCFF, .5);
graphics.lineTo(mouseX, mouseY);
points.push(new Point(mouseX, mouseY));
graphics.beginFill(0x33CCFF, .1);
graphics.moveTo(points[0].x, points[0].y);
for (var i:uint = 1; i < points.length; i++)
{
graphics.lineTo(points[i].x, points[i].y);
}
graphics.lineTo(points[0].x, points[0].y);
graphics.endFill();
dispatchEvent(new Event("UPDATE"));
}
public function clear():void
{
stage.removeEventListener(MouseEvent.MOUSE_MOVE, handleMouseMove);
graphics.clear();
}
}
}
And this is the document class that implements it :
package
{
import com.reyco1.medusa.selectiontool.SelectionBase;
import flash.display.Sprite;
import flash.events.Event;
import flash.events.MouseEvent;
import flash.display.StageAlign;
import flash.display.StageQuality;
import flash.display.StageScaleMode;
[SWF(width = '1024', height = '768', backgroundColor = '0x000000')]
public class SelectionToolPrototype extends Sprite
{
private var selectionTool:SelectionBase;
public function SelectionToolPrototype()
{
stage.align = StageAlign.TOP_LEFT;
stage.scaleMode = StageScaleMode.NO_SCALE;
stage.quality = StageQuality.MEDIUM;
stage.addEventListener(MouseEvent.MOUSE_DOWN, handleDown);
stage.addEventListener(MouseEvent.MOUSE_UP, handleUp);
placeShapesRandomly();
}
private function placeShapesRandomly():void
{
for(var a:Number = 0; a<25; a++)
{
var s:Sprite = new Sprite();
s.graphics.beginFill(Math.random() * 0xCCCCCC);
s.graphics.drawRect(0, 0, 50, 50);
s.graphics.endFill();
s.x = Math.floor(Math.random() * 900 - 40) + 40;
s.y = Math.floor(Math.random() * 700 - 40) + 40;
s.rotation = Math.floor(Math.random() * 360 - 40) + 40;
s.buttonMode = true;
addChild(s);
}
}
private function handleUp(e:MouseEvent):void
{
selectionTool.removeEventListener("UPDATE", handleToolUpdate);
removeChild(selectionTool);
selectionTool = null;
}
private function handleDown(e:MouseEvent):void
{
selectionTool = new SelectionBase();
selectionTool.addEventListener("UPDATE", handleToolUpdate);
selectionTool.x = mouseX;
selectionTool.y = mouseY;
addChild(selectionTool);
}
private function handleToolUpdate(e:Event):void
{
// logic to determin if items are within selection goes here
}
}
}
I've tried using collision detection by means of BitmapData and even using collision libraries like CDK but I cant get anything to work. Anybody have an idea what I should use in the handleToolUpdate(e:MouseEvent); ? Thanks!
Update:
I'll break it down. Basically I am trying to create a prototype of the BumpTop Lasso or Selection tool.
I need help in finding out which objects either collide or have a point within the bounds of the drawn lasso.
I have upload what I have so far to my server here : http://labs.reyco1.com/bumptop/SelectionToolPrototype.html. You can see the source by right clicking and selecting "View Source".
Like I said in my earlier post, I tried using Bitmapdata collision testing and even tried using the Collision Detection Kit to no avail. Thanks in advance.
Loop through the display object you are attaching your random sprites to, and using for each, check their value of hitTestObject against your selectionTool instance.
Here are the Adobe docs for hitTestObject():
http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/flash/display/DisplayObject.html#hitTestObject%28%29

How to change the pixels in an image

i actually try to do the following: I have loaded an external image in a bitmapdata object and create a bitmap from it which i attach it to a sprite/MovieClip in order to have mouse events on it. Now under the previous logic i loaded two images (let's say circles) of the same size one that has a particular color and is covered by its black foreground circle. When i press left mouse button and hold it down i want while the mouse is moved to erase the foreground circle's pixels and so the background image starting to appear. I tried this to achieve but had no luck. In my best attempt i achieve to draw a line in the foreground image but i cannot reveal the background!
package
{
import flash.display.MovieClip;
import flash.display.Sprite;
import flash.events.Event;
import flash.events.MouseEvent;
import flash.display.Loader;
import flash.net.URLRequest;
import flash.display.BitmapData;
import flash.display.Bitmap;
import flash.display.BlendMode;
public class Test2 extends MovieClip
{
// properties - state
// to attach the image and have mouse events
private var frontImage:Sprite;
private var backImage:Sprite;
// to load the image
private var myLoader:Loader;
// to get the bitmap data of the image
private var frontBitmapData:BitmapData;
private var frontBitmap:Bitmap;
// test
private var frontMask:Bitmap;
// constructor
function Test2():void
{
// load the background image
backImage = new Sprite();
attachImageToSprite1(new URLRequest("btest.jpg"));
backImage.mouseEnabled = false;
this.addChild( backImage );
// load the front image
frontImage = new Sprite();
attachImageToSprite2(new URLRequest("test.jpg"));
frontImage.mouseEnabled = true; // enable mouse
frontImage.buttonMode = true; // set button mode
this.addChild(frontImage); // load to stage
this.frontImage.addEventListener(MouseEvent.MOUSE_DOWN, onMouseDown);
this.frontImage.addEventListener(MouseEvent.MOUSE_UP, onMouseUp);
}
// methods
private function attachImageToSprite1(Name:URLRequest):void
{
this.myLoader = new Loader();
this.myLoader.load(Name);
this.myLoader.contentLoaderInfo.addEventListener(Event.COMPLETE, onLoadComplete1);
}
private function attachImageToSprite2(Name:URLRequest):void
{
this.myLoader = new Loader();
this.myLoader.load(Name);
this.myLoader.contentLoaderInfo.addEventListener(Event.COMPLETE, onLoadComplete2);
}
private function getImageBitmapDataFromSprite(srcImage:Sprite):BitmapData
{
var tmpBitmapData:BitmapData = new BitmapData(frontImage.width, frontImage.height, true, 0xFFCCCCCC);
tmpBitmapData.lock();
tmpBitmapData.draw(frontImage);
tmpBitmapData.unlock();
return tmpBitmapData;
}
private function isPixelAlpha(bitmapdata:BitmapData):Boolean
{
var pixelValue:uint = bitmapdata.getPixel32(mouseX, mouseY);
var alphaValue:uint = pixelValue >> 24 & 0xFF;
//var red:uint = pixelValue >> 16 & 0xFF;
//var green:uint = pixelValue >> 8 & 0xFF;
//var blue:uint = pixelValue & 0xFF;
return (alphaValue == 0x00) ? true : false;
}
private function deletePixelUnderMouse(bitmapdata:BitmapData, bitmap:Bitmap):void
{
bitmapdata.lock();
if ( !isPixelAlpha(bitmapdata) ) {
bitmapdata.setPixel32(mouseX, mouseY, 0xFF << 24); // how to make the current pixel's alpha
} // equal to zero.
bitmap = new Bitmap(bitmapdata);
bitmap.x = frontImage.x;
bitmap.y = frontImage.y;
this.frontImage.addChild(bitmap);
bitmapdata.unlock();
}
// events
public function onLoadComplete1(e:Event):void
{
frontImage.addChild(this.myLoader.content);
}
public function onLoadComplete2(e:Event):void
{
backImage.addChild(this.myLoader.content);
}
public function onMouseDown(e:MouseEvent):void
{
// delete a pixel from the sprite under the mouse
frontBitmapData = getImageBitmapDataFromSprite(frontImage);
deletePixelUnderMouse(frontBitmapData, frontBitmap);
frontImage.addEventListener(MouseEvent.MOUSE_MOVE, onMouseDown);
trace("start");
}
public function onMouseUp(e:MouseEvent):void
{
frontImage.removeEventListener(MouseEvent.MOUSE_MOVE, onMouseDown);
trace("stop")
}
}
}
Not sure if I got it right, but if you want a 'reveal' effect, as in you draw a mask to display a hidden image for example, this could be achieved slightly easier:
var bitmapToReveal:BitmapData = new BitmapToReveal(0,0);
var brush:BitmapData = new Brush(0,0);
var canvasData:BitmapData = new BitmapData(bitmapToReveal.width,bitmapToReveal.height,true,0x00FFFFFF);
var cursor:Point = new Point();//used as destination point when painting
var zero:Point = new Point();//reused for painting
var reveal:Bitmap = new Bitmap(bitmapToReveal);
var canvas:Bitmap = new Bitmap(canvasData);
reveal.cacheAsBitmap = canvas.cacheAsBitmap = true;
addChild(reveal);
addChild(canvas);
reveal.mask = canvas;
stage.addEventListener(MouseEvent.MOUSE_DOWN, brushDown);
stage.addEventListener(MouseEvent.MOUSE_UP, brushUp);
function brushDown(event:MouseEvent):void {
this.addEventListener(Event.ENTER_FRAME, paint);
}
function brushUp(event:MouseEvent):void {
this.removeEventListener(Event.ENTER_FRAME, paint);
}
function paint(event:Event):void {
cursor.x = mouseX-brush.width*.5;
cursor.y = mouseY-brush.height*.5;
canvasData.copyPixels(brush,brush.rect,cursor,brush,zero,true);
}
I'm using two Bitmaps form the library(bitmapToReveal and brush).
The main thing to look at is the copyPixels() method. I copy
the brush bitmap into the canvas(an empty transparent bitmap data),
using the offset cursor position(so the brush centered), and using the
alpha channel to do that. Note that I've set cacheAsBitmap to true
for both mask and maskee. You need to do that to get a transparent mask,
which is key to the effect.
Here is the result:
You can 'paint' the mask here. CS4 Source is here.
HTH,
George