mouseX/Y confusion when dragging a child of a container - actionscript-3

I've got a grid of images which are added to a imagecontainer(Sprite) in a class.
When I click a button, the imagecontainer gets tweened(scaled) to 0.2
Now I would like to start dragging around the images. on mouse-down I add an enterFrame Event:
function onEnterFrame(e:Event):void
{
imagecontainer.image.x = this.mouseX;
imagecontainer.image.y = this.mouseY;
}
Unfortunately the image is never on the mouseposition but has a increasing/decreasing offset to the mouse pointer.
The alternative, startDrag/stopDrag works perfectly, but I still need the mouseX/mouseY for placing the image on a grid…
I tried also parent.mouseX, no luck with that.
Why is this happening? I thought mouseX/mouseY is always depending on the stage-dimension.

have you tried:
function onEnterFrame(e:Event):void
{
imagecontainer.image.x = stage.mouseX;
imagecontainer.image.y = stage.mouseY;
}

If you want to get the mouseX and mouseY relative to the mouse position on the stage then why don't you use:
stage.mouseX,
stage.mouseY
Also, if you scale something up (let's say to a total size of 200%), then 50 pixels across on the stage is actually 25 pixels across in the container that has been scaled.
Use this as a document class to see what I mean:
package
{
import flash.display.MovieClip;
import flash.events.Event;
public class Main extends MovieClip
{
private var _box:MovieClip;
public function Main()
{
addEventListener(Event.ENTER_FRAME, _move);
_box = new MovieClip();
_box.scaleX = _box.scaleY = 2;
addChild(_box);
}
private function _move(e:Event):void
{
trace("stage: " + stage.mouseX + ", " + stage.mouseY);
trace("box: " + _box.mouseX + ", " + _box.mouseY);
}
}
}

If you are still looking for this maybe you can use startDrag(); and stopDrag();
for dragging images.
like this:
image.addEventListener(MouseEvent.MOUSE_DOWN, onMouseDownMC);
image.addEventListener(MouseEvent.MOUSE_UP, onMouseUpMC);
function onMouseDownMC(e:MouseEvent):void
{
e.currentTarget.startDrag(true);
}
function onMouseUpMC(e:MouseEvent):void
{
e.currentTarget.stopDrag();
}
Update : You can set ur function like this:
image.addEventListener(MouseEvent.MOUSE_DOWN, onMouseMC);
image.addEventListener(MouseEvent.MOUSE_UP, onMouseMC);
function onMouseMC(e:MouseEvent):void
{
var type = e.type;
if(type == MouseEvent.MOUSE_DOWN) /// u can use "mouseDown" accept MouseEvent.MOUSE_DOWN
{
e.currentTarget.startDrag(true);// if you set true its gonna drag the obj from center
}
else if(type == MouseEvent.MOUSE_UP)
{
e.currentTarget.stopDrag(); //u can use "mouseUp" accept MouseEvent.MOUSE_UP
}
}

Related

Restore dragged movieclips to their original position as they are dragged outside the stage in action script 3

I am creating a drag and drop the game for kids and I realized that when the kid drags a movie clip by mistake outside the stage the movie clip hangs where it is dragged onto and doesn't return back to its original position or it overlaps over other movie clips.
Thanks in advance
the code I am basically using is:
* square_mc.addEventListener(MouseEvent.MOUSE_DOWN, pickUp2);
square_mc.addEventListener(MouseEvent.MOUSE_UP, dropIt2);
function pickUp2(event: MouseEvent): void {
square_mc.startDrag(true);
event.target.parent.addChild(event.target);
startX = event.currentTarget.x;
startY = event.currentTarget.y;
}
function dropIt2(event: MouseEvent): void {
square_mc.stopDrag();
var myTargetName:String = "target" + event.target.name;
var myTarget:DisplayObject = getChildByName(myTargetName);
if (event.target.dropTarget != null && event.target.dropTarget.parent ==
myTarget){
event.target.removeEventListener(MouseEvent.MOUSE_DOWN, pickUp2);
event.target.removeEventListener(MouseEvent.MOUSE_UP, dropIt2);
event.target.buttonMode = false;
event.target.x = myTarget.x;
event.target.y = myTarget.y
} else {
event.target.x = startX;
event.target.y = startY;
}
}*
What you need to do is to preserve the initial position of the clip somewhere and then use it.
Basic algorithm I can think of can look as follows: 1 - player starts dragging a clip, init position is saved 2 - player releases the clip.
Two outcomes are possible: (a): the clip is on the right position or (b): it is misplaced. In case (b) you just need to assign initial coordinates back to the clip.
Very simple example:
private const initialCoords: Dictionary = new Dictionary();
private function saveInitialCoords(clip: DisplayObject): void {
initialCoords[clip] = new Point(clip.x, clip.y);
}
private funciton retreiveInitialCoords(clip: DisplayObject): Point {
return initialCoords[clip]; // it would return null if there is no coords
}
UPD: As it was pointed by #kaarto my answer might be irrelevant. If the issue is to keep the dragging movieclip
within some bounds I usually use something like this:
private var currentTarget: DisplayObject; // clip we're dragging currently
private var areaBounds: Rectangle = new Rectangle(0, 0, stage.stageWidth, stage.stageHeight); // you might want to ensure stage is not null.
private function startDrag(e: MouseEvent): void {
// I'll drop all checks here. Normally you want to ensure that currentTarget is null or return it to initial position otherwise
currentTarget = e.currentTarget as DisplayObject;
saveInitialCoords(currentTarget);
addEventListener(MouseEvent.MOUSE_MOVE, checkAreaBounds);
}
private funciton stopDrag(e: MouseEvent): void {
// some checks were dropped.
if (clipIsOnDesiredPlace(currentTarget)) {
// do something
} else {
const initialCoords: Point = retreiveInitialCoords(currentTarget);
currentTarget.x = initialCoords.x;
currentTarget.y = initialCoords.y;
}
currentTarget = null;
removeEventListener(MouseEvent.MOUSE_MOVE, checkAreaBounds);
}
private funciton checkAreaBounds(e: MouseEvent): void {
// you might need to convert your coords localToGlobal depending on your hierarchy
// this function gets called constantly while mouse is moving. So your clip won't leave areaBounds
var newX: Number = e.stageX;
var newY: Number = e.stageY;
if (currentTarget) {
if (newX < areaBounds.x) newX = areaBounds.x;
if (newY < areaBounds.y) newY = areaBounds.y;
if (newX + currentTarget.width > areaBounds.x + areaBounds.width) newX = areaBounds.x + areaBounds.width - currentTarget.width;
if (newY + currentTarget.height > areaBounds.y + areaBounds.heght) newY = areaBounds.y + areaBounds.height - currentTarget.height;
currentTarget.x = newX;
currentTarget.y = newY;
}
}
... somewhere ...
// for each clip you're going to interact with:
for each (var c: DisplayObject in myDraggableClips) {
c.addEventListener(MouseEvent.MOUSE_DOWN, startDrag);
c.addEventListener(MouseEvent.MOUSE_UP, stopDrag);
}
Another option (if you don't want to limit the area with some bounds) is to use MouseEvent.RELEASE_OUTSIDE event and return a clip to it's initial position once it gets released:
stage.addEventListener(MouseEvent.RELEASE_OUTSIDE, onMouseReleaseOutside);
private function onMouseReleaseOutside(e:MouseEvent):void {
// return clip to it's position.
}
More info you can find in documentation: https://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/flash/events/MouseEvent.html#RELEASE_OUTSIDE

Check when a part of the MovieClip leaves the Stage

I'm creating a Drag and Drop game using AS3, i want to check when a apart of a Movieclip is outside the screen to move the View behind and let the user choose where to drop it.
I cant' test if the MovieClip credentials are bigger that the stage (scaleMode = NO_SCALE) Width/Height, because there is a part of the stage that it's hidden behind the browser window.
It's the same aspect as MOUSE_LEAVE just this time it has to be for MovieClips, i tried to see the code behind MOUSE_LEAVE but i couldn't reach it.
Thank You.
MAIN CLASS
[SWF(width='800', height='800',backgroundColor='#CC99FF', frameRate='60')]
public class DragTest extends Sprite
{
public function DragTest()
{
addChild(new World(this));
this.stage.scaleMode = "noScale";
this.stage.align = "TL";
this.graphics.lineStyle(5,0x555555,0.5);
this.graphics.drawRect(0,0,800,800);
}
}
WORLD CLASS
public class World extends Container // Container from my SWC
{
private var _display:Sprite;
private var _dragPt:Point;
private var _dragedObject:MovieClip;
public function World(display:Sprite)
{
super();
_display = display;
myMC.addEventListener(MouseEvent.MOUSE_DOWN, onPickUp, false, 0, true );
display.stage.addEventListener(MouseEvent.MOUSE_UP, onDrop, false, 0, true );
display.stage.addEventListener(Event.MOUSE_LEAVE, onMouseLeave, false, 0, true );
}
protected function onMouseLeave(event:Event):void
{
trace("Mouse Is Leaving The Stage");
}
protected function onDrop(e:MouseEvent):void
{
_display.stage.removeEventListener(MouseEvent.MOUSE_MOVE, onMoveObject);
}
private function onPickUp(e:MouseEvent)
{
_dragedObject = e.currentTarget as MovieClip;
_display.stage.addEventListener(MouseEvent.MOUSE_MOVE, onMoveObject, false, 0, true);
}
protected function onMoveObject(e:MouseEvent):void
{
var point:Point = new Point(_display.stage.mouseX, _display.stage.mouseY);
(_dragedObject as MovieClip).x = point.x;
(_dragedObject as MovieClip).y = point.y;
}
}
Here is an Example :
Simple Code
The easiest approach would probably be to use getBounds(stage) and compare with stageWidth and stageHeight:
var bounds:Rectangle = _draggedObject.getBounds(stage);
if (bounds.left < 0) {
// left part of object is off-screen
} else if (bounds.right > stage.stageWidth) {
// right part of object is off-screen
}
if (bounds.top < 0) {
// top part of object is offscreen
} else if (bounds.bottom > stage.stageHeight) {
// bottom part of object is off-screen
}
You could move the display in each of these cases.
You can try to create an invisible zone that's a little bit smaller than your stage.
So you can add the MOUSE_LEAVE event to the zone, and when your mouse leaves that zone, you can do what you want.
Check the example here.
In response to Aaron Beall's response:
For a more interesting effect, if you want to wait until the movie clip is completely off stage, you can swap the boundaries you check on the object
var bounds:Rectangle = object.getBounds(stage);
if (bounds.right < 0) {
// do thing
} else if (bounds.left > stage.stageWidth) {
// do thing
}
if (bounds.bottom < 0) {
// do thing
} else if (bounds.top > stage.stageHeight) {
// do thing
}
Make sure you have import flash.geom.Rectangle; imported if this is inside a class.

AS3 Right mouse button drag & drop

With Flash Player 11.2, we now have access to mouse events for the right mouse button:
MouseEvent.RIGHT_CLICK
MouseEvent.RIGHT_MOUSE_DOWN
MouseEvent.RIGHT_MOUSE_UP
However, when I try to use these to implement a right-mouse-button drag&drop, it seems that once the right mouse button is down, I no longer get MouseEvent.MOUSE_MOVE and the stage's MouseX and MouseY members stop updating until the button is released. This makes it effectively impossible to implement that drag&drop.
Use the code example in Flash Builder below to see what I mean:
Launch it, move your mouse around and you'll see logs of your mouse position updating.
Press and hold LMB, move your mouse around, you'll see more logs, great!
Release LMB, press and hold LMB, move your mouse around, no logs, bad!
Is this a limitation of AS3? I can't seem to find any documentation about it.
Code Example:
package
{
import flash.display.Sprite;
import flash.events.MouseEvent;
public class TestFlash extends Sprite
{
public function TestFlash()
{
if(stage){
stage.addEventListener(MouseEvent.MOUSE_MOVE, onMouseMove);
stage.addEventListener(MouseEvent.MOUSE_DOWN, onLMBDown);
stage.addEventListener(MouseEvent.MOUSE_UP, onLMBUp);
stage.addEventListener(MouseEvent.RIGHT_MOUSE_DOWN, onRMBDown);
stage.addEventListener(MouseEvent.RIGHT_MOUSE_UP, onRMBUp);
}
}
private function onMouseMove(event:MouseEvent) : void
{
trace("Mouse Pos, from event: x = " + event.localX + " y = " + event.localY + " from stage: x = " + stage.mouseX + " y = " + stage.mouseY);
}
private function onRMBDown(event:MouseEvent) : void
{
trace("RMB down");
}
private function onRMBUp(event:MouseEvent) : void
{
trace("RMB up");
}
private function onLMBDown(event:MouseEvent) : void
{
trace("LMB down");
}
private function onLMBUp(event:MouseEvent) : void
{
trace("LMB up");
}
}
}
Judging by the problem you are describing, I think you need to add the enter frame function, otherwise the trigger will only activate on the frame that the mouse is clicked and when its released
addEventListener(Event.ENTER_FRAME, dragdrop)
function dragdrop(event:Event):void {
"add dragging event here"
}

HitTest for objects not yet on Stage

I need to add a MovieClip to stage, the limitation being that it should only be added to an empty area on the stage. The stage itself either contains complex shapes or is manipulable by the user i.e. he can drag/move objects to change the empty area. The hitTest and hitTestObject methods need DisplayObject already available on the stage. What is the right way to go - the only solution I can imagine is having added my object on the stage and then repeatedly doing hit tests?
[Imagine it to something like adding sprites in a video game - they must spawn in empty regions; if they pop out from inside of each other, then it'll look really odd.]
Well, when you create a new class, just turn it off with a variable and set the visibility to false, then loop until there is no hitTest.
A silly example:
public class someClass extends Sprite
{
private var objectsOnStage:Array;
public function someClass(objectsArray:Array) {
objectsOnStage = objectsArray;
visible = false;
addEventListener(Event.ADDED_TO_STAGE, init);
}
private function init(e:Event){
removeEventListener(Event.ADDED_TO_STAGE, init);
addEventListener(Event.ENTER_FRAME, SEARCH);
}
private function SEARCH(e:Event) {
var doesHit:Boolean = false;
x = Math.round(Math.random() * (550 - 0)) + 0;
y = Math.round(Math.random() * (400 - 0)) + 0;
for (var i:int = 0; i < objectsOnStage; i++) {
if (doesHit) break;
if (this.hitTestObject(objectsOnStage[i])) {
doesHit = true;
}
}
if (doesHit) return;
placedInit();
}
private function placedInit() {
visible = true;
removeEventListener(Event.ENTER_FRAME, SEARCH);
//now init the stuff you want.
}
}
You just check if bounding boxes of both clips overlaps. Like this:
import flash.geom.Rectangle;
import flash.display.MovieClip;
// create simple movie clips that has a rectangle shape inside
var sym1 : MovieClip = new Sym1();
var sym2 : MovieClip = new Sym2();
// get a rectanle of both clipt
var boundingBox1 : Rectangle = sym1.getBounds(this);
var boundingBox2 : Rectangle = sym2.getBounds(this);
// check if bounding boxes of both movie clips overlaps
// so it works like hitTestObject() method
trace( boundingBox1.intersects( boundingBox2) )
I know this post is super old, but in case it helps anybody --
If you need to do a hit test on a movieclip that isn't on the stage. A workaround is to rasterize it to a bitmap first.
var bitmapData:BitmapData = new BitmapData(mc.width, mc.height, true, 0x0000000);
bitmapData.draw(mc);
if (bitmapData.getPixel32(x, y) > 0) {
// Hit true.
}

AS3 - If symbol's coordinates arrive here?

I'm using Flash Professional CS5.5 and I need to make an app where there is a ball (symbol) that moves using the accelerometer and I want that, when the ball coordinates A reach this coordinates B it goes to frame 2 (gotoAndPlay(2)). I have to find the ball coord first, right? How do I make this?
Here is the code I've now
c_ball.addEventListener(MouseEvent.MOUSE_DOWN, fl_ClickToDrag);
function fl_ClickToDrag(event:MouseEvent):void{
c_ball.startDrag();}
stage.addEventListener(MouseEvent.MOUSE_UP, fl_ReleaseToDrop);
function fl_ReleaseToDrop(event:MouseEvent):void{
c_ball.stopDrag();}
would it work if, after retriving the coordinates?
function f_level (e) if (c_ball.x==100 && c_ball.y==100) {
gotoAndStop(2);}
MOUSE_UP and MOUSE_DOWN are not what you need if you're looking for Accelerometer data. You want the Accelerometer class and associated events.
Try something like this:
import flash.sensors.Accelerometer;
import flash.events.AccelerometerEvent;
var accel:Accelerometer = new Accelerometer();
accel.addEventListener(AccelerometerEvent.UPDATE, handleAccelUpdate);
Update handler:
function handleAccelUpdate(e:AccelerometerEvent):void{
//inside this function you now have access to acceleration x/y/z data
trace("x: " + e.accelerationX);
trace("y: " + e.accelerationY);
trace("z: " + e.accelerationZ);
//using this you can move your MC in the correct direction
c_ball.x -= (e.accelerationX * 10); //using 10 as a speed multiplier, play around with this number for different rates of speed
c_ball.y += (e.accelerationY * 10); //same idea here but note the += instead of -=
//you can now check the x/y of your c_ball mc
if(c_ball.x == 100 && c_ball.y == 100){
trace("you win!"); //fires when c_ball is at 100, 100
}
}
Now this will let you "roll" your MC off the screen so you're probably going to want to add some kind of bounds checking.
Check out this great writeup for more info:
http://www.republicofcode.com/tutorials/flash/as3accelerometer/
An easy and save way is to use colission detection, instead of testing for exectly one position ( what is hard to meet for users) you go for a target area :
package
{
import flash.display.Sprite;
import flash.events.Event;
import flash.events.MouseEvent;
public class Hittester extends Sprite
{
var ball:Sprite = new Sprite();
var testarea:Sprite = new Sprite();
public function Hittester()
{
super();
ball.graphics.beginFill(0xff0000);
ball.graphics.drawCircle(0,0,10);
testarea.graphics.beginFill(0x00ff00);
testarea.graphics.drawRect(0,0,50,50);
testarea.x = 100;
testarea.y = 100;
// if testarea should be invisble
/*testarea.alpha = 0;
testarea.mouseEnabled = false;
*/
ball.addEventListener(MouseEvent.MOUSE_DOWN, startDragging);
addChild(testarea);
addChild(ball);
}
private function startDragging( E:Event = null):void{
ball.startDrag();
stage.addEventListener(MouseEvent.MOUSE_UP, stopDragging);
}
private function stopDragging( E:Event = null):void{
stage.removeEventListener(MouseEvent.MOUSE_UP, stopDragging);
ball.stopDrag();
test();
}
private function test():void{
if( ! ball.hitTestObject(testarea) ){
ball.x = 10;
ball.y = 10;
}
else{
// here goes next frame command ;)
}
}
}
}