AS3 How to set boundries to Mouse Down drag? - actionscript-3

I'm new to AS3 and I have a square(1200w) that's bigger than the stage(200w). Right now you can keep dragging it left and right as far as you possibly can. How can I set a limit/boundry to how much of the square you can drag? So that it can't be dragged beyond it's maximum width?
Here's an image
this.addEventListener(MouseEvent.MOUSE_DOWN, mouseDownHandler);
this.addEventListener(MouseEvent.MOUSE_UP, mouseUpHandler);
function mouseDownHandler(e:MouseEvent) {
this.addEventListener(MouseEvent.MOUSE_MOVE, mouseMoveHandler);
}
function mouseMoveHandler(e:MouseEvent) {
square_mc.x = mouseX;
}
function mouseUpHandler(e:MouseEvent) {
removeEventListener(MouseEvent.MOUSE_MOVE, mouseMoveHandler);
}
Please note I do not want to use the startdrag() method.

Based on your image, assume that mc refers to the blue box.
var ox:Number = 0;
mc.addEventListener(MouseEvent.MOUSE_DOWN, mouseDownHandler);
function mouseDownHandler(e:MouseEvent):void
{
ox = mc.mouseX;
stage.addEventListener(MouseEvent.MOUSE_MOVE, mouseMoveHandler);
stage.addEventListener(MouseEvent.MOUSE_UP, mouseUpHandler);
}
function mouseUpHandler(e:MouseEvent):void
{
stage.removeEventListener(MouseEvent.MOUSE_MOVE, mouseMoveHandler);
}
function mouseMoveHandler(e:MouseEvent):void
{
mc.x = mc.parent.mouseX - ox;
if(mc.x > 0) mc.x = 0;
if(mc.x + mc.width < stage.stageWidth) mc.x = stage.stageWidth - mc.width;
}
Hopefully this is what you were after.

Related

How can I set the draggin box mc 50px from the bottom

I Have a mc called box.
All I want is when dragging up I want the bottom of box mc 50px from the bottom of the stage.
Now it's in the middle of the stage.
Here is my code.
var topY:int = stage.stageHeight - box.height;
var botY:int = 0;
box.addEventListener(MouseEvent.MOUSE_DOWN, onDown);
box.addEventListener(MouseEvent.MOUSE_UP, onUp);
var constrainY:Rectangle = new Rectangle(box.x, topY ,0, box.y+ (box.height-stage.stageHeight) );
var dragxy:String = "";
function onDown(e:MouseEvent):void
{
dragxy = mouseX + "_" + mouseY;
e.currentTarget.startDrag(false, constrainY);
}
function onUp(e:MouseEvent):void
{
e.currentTarget.stopDrag();
}
One bad thing I'm seeing is you are adding an eventListener every frame that the mouse button is down ( I think ). To not do that, just call start drag once when Mouse Down is true and then stop listening for mouse down. When mouse goes back up, start listening for down again.
function onDown(e:MouseEvent):void
{
dragxy = mouseX + "_" + mouseY;
e.currentTarget.startDrag(false, constrainY);
removeEventListener(MouseEvent.MOUSE_DOWN, onDown);
addEventListener(MouseEvent.MOUSE_UP, onUp);
}
function onUp(e:MouseEvent):void
{
e.currentTarget.stopDrag();
addEventListener(MouseEvent.MOUSE_DOWN, onDown);
removeEventListener(MouseEvent.MOUSE_UP, onUp);
}

How to prevent a drag action to affect the childrens of a MovieClip

My problem is: I have a MovieClip (obj) that users can drag to both sides to navigate, the code I use for this:
import flash.events.MouseEvent;
import flash.geom.Point;
import flash.events.Event;
import flash.geom.Rectangle;
var destination: Point = new Point();
var dragging: Boolean = false;
var speed: Number = 10;
var offset: Point = new Point();
var bounds: Rectangle = new Rectangle(0, 0, stage.stageWidth, stage.stageHeight);
obj.addEventListener(MouseEvent.MOUSE_DOWN, startdrag);
stage.addEventListener(MouseEvent.MOUSE_UP, stopdrag);
obj.addEventListener(Event.ENTER_FRAME, followmouse);
function startdrag(e: MouseEvent): void {
offset.x = obj.mouseX * obj.scaleX;
dragging = true;
}
function stopdrag(e: MouseEvent): void {
dragging = false;
}
function followmouse(e: Event): void {
if (obj) {
if (dragging) {
destination.x = mouseX;
}
obj.x -= (obj.x - (destination.x - offset.x)) / speed;
if (obj.x > bounds.left) {
obj.x = bounds.left;
}
if (obj.x < -obj.width + bounds.right) {
obj.x = -obj.width + bounds.right;
}
}
}
So far so good, the problem comes up when I put some clickable elements inside that MovieClip (obj), here are the code for the clickable elements:
objA.addEventListener(MouseEvent.CLICK, objATrigger);
objB.addEventListener(MouseEvent.CLICK, objBTrigger);
objC.addEventListener(MouseEvent.CLICK, objCTrigger);
function objATrigger(event: MouseEvent): void {
MovieClip(this.parent).gotoAndPlay(1, "Main");
}
function objBTrigger(event: MouseEvent): void {
MovieClip(this.parent).gotoAndPlay(1, "Main");
}
function objCTrigger(event: MouseEvent): void {
MovieClip(this.parent).gotoAndPlay(1, "Main");
}
The problem is: When I drag the MovieClip (obj) there is a conflict with the event, when release the mouse after the drag, the event Click of MovieClips inside the MovieClip (obj) is fired, how can I fix this? They should only be triggered when there is no drag action.
This is how I handle dragging a parent that has clickable children. The benefit of this method, is that you don't need to do anything to the children (no extra conditions in their click handlers etc), the click event simply doesn't reach them.
You can also hopefully gleam some efficiency tips from the code/comments below:
var wasDragged:Boolean = false;
var dragThreshold:Point = new Point(10,10);
// ^ how many pixels does it need to move before it's considered a drag
//this is good especially on touchscreens as it's easy to accidentally drag the item a couple pixels when clicking.
var dragStartPos:Point = new Point(); //to store drag origin point to calculate whether a drag occured
var dragOffset:Point = new Point(); //to track the gap between the mouse down point and object's top left corner
obj.addEventListener(MouseEvent.MOUSE_DOWN, startdrag);
obj.addEventListener(MouseEvent.CLICK, dragClick, true); //listen on the capture phase of the event.
//the only reason we listen for click on the draggable object, is to cancel the click event so it's children don't get it
function dragClick(e:Event):void {
//if we deemed it a drag, stop the click event from reaching any children of obj
if(wasDragged) e.stopImmediatePropagation();
}
function startdrag(e: MouseEvent): void {
//reset all dragging vars
wasDragged = false;
dragStartPos.x = obj.x;
dragStartPos.y = obj.y;
//set the offset so the object doesn't jump when first clicked
dragOffset.x = stage.mouseX - obj.x;
dragOffset.y = stage.mouseY - obj.y;
//only add the mouse up listener AFTER the mouse down
stage.addEventListener(MouseEvent.MOUSE_UP, stopdrag);
//mouse_move is more efficient that enter_frame, and only listen for it when dragging
stage.addEventListener(MouseEvent.MOUSE_MOVE, followmouse);
}
function stopdrag(e:MouseEvent = null): void {
//remove the dragging specific listeners
stage.removeEventListener(MouseEvent.MOUSE_UP, stopdrag);
stage.removeEventListener(MouseEvent.MOUSE_MOVE, followmouse);
}
function followmouse(e:MouseEvent): void {
if (obj) {
//do what you need to move the object
obj.x = stage.mouseX - dragOffset.x;
obj.y = stage.mouseY - dragOffset.y;
//check if the obj moved far enough from the original position to be considered a drag
if(!wasDragged
&& (Math.abs(obj.x - dragStartPos.x) > dragThreshold.x
|| Math.abs(obj.y - dragStartPos.y) > dragThreshold.y)
){
wasDragged = true;
}
}
}
I don`t know if this is the best approach, but it was possible to check using the code below:
stage.addEventListener(MouseEvent.MOUSE_DOWN, setmousepos);
brose_trigger.addEventListener(MouseEvent.MOUSE_UP, broseTrigger);
denso_trigger.addEventListener(MouseEvent.MOUSE_UP, densoTrigger);
honda_trigger.addEventListener(MouseEvent.MOUSE_UP, hondaTrigger);
var mousePos: Point = new Point();
function setmousepos(e:MouseEvent): void {
mousePos.x = mouseX;
}
function broseTrigger(e:MouseEvent): void {
if(mousePos.x == mouseX){
MovieClip(this.parent).gotoAndPlay(1, "Main");
}
}
function densoTrigger(event:MouseEvent): void {
if(mousePos.x == mouseX){
MovieClip(this.parent).gotoAndPlay(1, "Main");
}
}
function hondaTrigger(event:MouseEvent): void {
if(mousePos.x == mouseX){
MovieClip(this.parent).gotoAndPlay(1, "Main");
}
}
When MOUSE_DOWN event is triggered, I store the mouse.x position in a variable, after that in the MOUSE_UP event, I compare the stored position with the actual position, if equals, TÃDÃ!
Do add condition in objATrigger function to check if dragging is false
function objATrigger(event: MouseEvent): void {
if(!MovieClip(root).dragging){
MovieClip(this.parent).gotoAndPlay(1, "Main");
}
}

AS3: why does the MC "tremble" when selected?

I'm trying to make a simple function to select and drag a MovieClip, without using the startDrag() function.
I have a few MCs on the stage, when mouse down on a MC, I want the MC to move with the mouse. But when I hold the mouse down, the MC starts to "tremble" and I'm not sure why.
I have the code inside each MC for other reasons. Here is what I have so far:
var selectX:Number; //x coordinate of mouse click (to select right point on mc on mouse down)
this.addEventListener(MouseEvent.MOUSE_DOWN, mouseDownHandler);
this.addEventListener(MouseEvent.MOUSE_UP, mouseUpHandler);
function mouseDownHandler(e:MouseEvent):void {
selectX = this.x - mouseX;
addEventListener(Event.ENTER_FRAME, onEnterFrameHandler);
}
function mouseUpHandler(e:MouseEvent):void {
mouseX2 = mouseX;
removeEventListener(Event.ENTER_FRAME, onEnterFrameHandler);
}
function onEnterFrameHandler(e:Event):void {
this.x = mouseX + selectX + stage.x;
}
This is happening because you are using mouseX of the inside of the movieclip. but when you are trying to set the x of the movieClip it sets the x on the parent movieclip.
e.g.:
mainClip
|-- DragableButton
when you adding DragableButton.x = 100, it is x position insided of the mainClip.
and when your code is taking mouseX inside of the DragableButton, the real mouseX = x + mouseX. and since mouseX inside of the DragableButton is equals e.g. 20, and you adding: selectX = this.x - mouseX -> if you have selectX = 100 - 20. but not 100 - 120 as it should be.
so if you still want to keep to your code change it a bit:
var selectX:Number; //x coordinate of mouse click (to select right point on mc on mouse down)
var mouseX2:Number;
this.addEventListener(MouseEvent.MOUSE_DOWN, mouseDownHandler);
this.addEventListener(MouseEvent.MOUSE_UP, mouseUpHandler);
function mouseDownHandler(e:MouseEvent):void {
selectX = this.x - parent.mouseX;
// selectX = this.x - stage.mouseX;
addEventListener(Event.ENTER_FRAME, onEnterFrameHandler);
}
function mouseUpHandler(e:MouseEvent):void {
mouseX2 = parent.mouseX;
removeEventListener(Event.ENTER_FRAME, onEnterFrameHandler);
}
function onEnterFrameHandler(e:Event):void {
this.x = parent.mouseX + selectX;
// this.x = stage.mouseX + selectX;
}
p.s. stage.x = 0, it will be always. unless you change the property.
p.s.s. stage is only one and same instance no matter from which MC you are trying to get it.
my suggestion draging would be:
this.addEventListener(MouseEvent.MOUSE_DOWN, mouseDownHandler);
function mouseDownHandler(e:MouseEvent):void
{
this.startDrag();
stage.addEventListener(MouseEvent.MOUSE_UP, mouseUpHandler);
}
function mouseUpHandler(e:MouseEvent):void
{
this.stopDrag();
stage.addEventListener(MouseEvent.MOUSE_UP, mouseUpHandler);
}
I think the movieclip is trembling because during your drag, your application keeps calling mouseDownHandler, changing the selectX.
Try removing the MOUSE_DOWN event listener. In mouseDownHandler, make that the first thing you do (this is also a good practice for preventing memory leaks). You can add the listener back when you mouse up (and then remove the mouse up listener).
why are you using Event.ENTER_FRAME event (costly), try to use MouseEvent.MOUSE_MOVE like this.
function mouse_move(e:Event)
{
this.x = mouseX + selectX + stage.x;
}
and remove this event handler on mouse up.

startDrag - stop diagonal movement

Is there a way to have a MovieClip with startDrag, but to force only horizontal and vertical (i.e. not diagonal) movement?
Here is my solution. It tracks the mouseX and mouseY on click and compares it to last position. Finds out which direction the mouse is mainly moving then moves the object there. You may want to add some extra logic to lock the object to the nearest 10th unit or whatever unit size you want to form a snap grid for use in games or organized placement of the object.
Update: I went ahead and added a snapNearest function to help control the movement.
import flash.events.MouseEvent;
import flash.events.Event;
dragObj.addEventListener(MouseEvent.MOUSE_DOWN, dragIt);
var curX:Number = 0;
var curY:Number = 0;
var oldX:Number = 0;
var oldY:Number = 0;
var gridUnit:Number = 25;
function dragIt(e:MouseEvent):void
{
// set x,y on down
oldX = mouseX;
oldY = mouseY;
// add mouse up listener so you know when it is released
stage.addEventListener(MouseEvent.MOUSE_UP, dropIt);
stage.addEventListener(Event.ENTER_FRAME, moveIt);
trace("Start Drag")
}
function moveIt(e:Event):void
{
// figure out what the main drag direction is and move the object.
curX = mouseX;
curY = mouseY;
// figure out which is the larger number and subtract the smaller to get diff
var xDiff:Number = curX > oldX ? curX - oldX : oldX - curX;
var yDiff:Number = curY > oldY ? curY - oldY : oldY - curY;
if(xDiff > yDiff) {
dragObj.x = snapNearest(mouseX, gridUnit);
}else{
dragObj.y = snapNearest(mouseY, gridUnit);
}
oldX = mouseX;
oldY = mouseY;
}
function dropIt(e:MouseEvent):void
{
//remove mouse up event
stage.removeEventListener(MouseEvent.MOUSE_UP, dropIt);
stage.removeEventListener(Event.ENTER_FRAME, moveIt);
trace("Stop Drag")
}
// snap to grid
function snapNearest(n:Number, units:Number):Number
{
var num:Number = n/units ;
num = Math.round(num);
num *= units;
return num;
}
yes. there are a few options.
A. you can choose to use the startDrag() function and supply it's 2nd parameter with a bounds rectangle for your draggable object. something like;
dragSprite.startDrag(false, new Rectangle(0, 0, 20, stage.stageHeight));
B. you can set your draggable object to listen for mouse events, moving it according to the mouse movements. something like:
dragSprite.addEventListener(MouseEvent.MOUSE_DOWN, mouseDownEventHandler);
function mouseDownEventHandler(evt:MouseEvent):void
{
stage.addEventListener(MouseEvent.MOUSE_MOVE, mouseMoveEventHandler);
stage.addEventListener(MouseEvent.MOUSE_UP, mouseUpEventHandler);
}
function mouseMoveEventHandler(evt:MouseEvent):void
{
//only move dragSprite horizontally
//dragSprite.y = evt.stageY;
dragSprite.x = evt.stageX;
}
function mouseUpEventHandler(evt:MouseEvent):void
{
stage.removeEventListener(MouseEvent.MOUSE_MOVE, mouseMoveEventHandler);
stage.removeEventListener(MouseEvent.MOUSE_UP, mouseUpEventHandler);
}
You could use a modifier key, for instance normal behavior would be horizontal & press down the shift key to move vertically.
function mouseMoveEventHandler(evt:MouseEvent):void
{
if(!evt.shiftKey)
dragSprite.x = evt.stageX;
else
dragSprite.y = evt.stageY;
}
You can only constrain to one axis or the other (using a constraint rectangle) But diagonal movement would be possible in the method you propose, unless you also define some other limits... for example a grid.

buttons inside mc affecting the code

I have a movieClip with two buttons inside.
The problem is that when the mouse is over these two buttons, the code that manages the movieClip stops working, as if the mouse is not over the MC (the buttons are children of the MC, shouldn't it work regardless?).
Could you please share some advice?
Thanks
/*mc follows mouse. I can't click btns because when mouse rollover btns the mc moves*/
function showImgOptions (e:Event):void{
if (mc.hitTestPoint(mouseX,mouseY,false)){
mc.y = mc.y;
mc.x = mc.x;
}else{
var delayX:int = mc.x - mouseX;
var delayY:int = mc.y - mouseY;
mc.x -= delayX / 6;
mc.y -= delayY/6;
}
}
mc.btn1.addEventListener (MouseEvent.CLICK, closeClick);
mc.btn2.addEventListener (MouseEvent.CLICK, zoomClick);
function closeClick (e:MouseEvent):void{}
function zoomClick (e:MouseEvent):void{}
stage.addEventListener (Event.ENTER_FRAME, showImgOptions);
addChild (mc);
Changed the code to:
var mc:menuMC = new menuMC();
addChild(mc);
var p:Point = mc.localToGlobal(new Point(mc.mouseX,mc.mouseY));
/*mc follows mouse. I can't click btns because when mouse rollover btns the mc moves*/
function showImgOptions (e:Event):void
{
if (! mc.hitTestPoint(p.x,p.y,false))
{
mc.y = mc.y;
mc.x = mc.x;
}else{
//move mc towards mc.parent's mouseX and mouseY
var delayX:int = mc.x - mouseX;
var delayY:int = mc.y - mouseY;
mc.x -= delayX / 6;
mc.y-=delayY/6;
}
}
mc.btn1.addEventListener (MouseEvent.CLICK, closeClick);
mc.btn2.addEventListener (MouseEvent.CLICK, zoomClick);
function closeClick (e:MouseEvent):void
{
}
function zoomClick (e:MouseEvent):void
{
}
stage.addEventListener (Event.ENTER_FRAME, showImgOptions);
And now I get this error:
TypeError: Error #1010: A term is undefined and has no properties.
Here you can download an FLA. Test it and try to click on the buttons 1 and 2, inside the MC following the mouse
hitTestPoint expects stage coordinates:
The x and y parameters specify a point in the coordinate space of the Stage, not the display object container that contains the display object (unless that display object container is the Stage).
Use localToGlobal to get the stage coordinates:
var p:Point = mc.localToGlobal(new Point(mc.mouseX, mc.mouseY));
if(!mc.hitTestPoint(p.x, p.y,false))
{
//move mc towards mc.parent's mouseX and mouseY
}
Solved it!!
Changed the code. I don't know if this helps anybody, but I hope so. Thanks to you all.
stage.addEventListener(Event.ENTER_FRAME, moveMC);
var mc:menuMC = new menuMC();
addChild(mc);
function moveMC(e:Event):void {
if (mc.hitTestObject(big_mc)) {
mc.visible = true;
} else {
mc.visible = false;
}
if (mc.hitTestPoint(mouseX,mouseY,false)) {
mc.y = mc.y;
mc.x = mc.x;
} else {
var delayX:int = mc.x - mouseX;
var delayY:int = mc.y - mouseY;
mc.x -= delayX / 6;
mc.y-=delayY/6;
}
}
mc.btn1.addEventListener(MouseEvent.CLICK, onBtn1);
mc.btn2.addEventListener(MouseEvent.CLICK, onBtn2);
function onBtn1(e:MouseEvent):void {
trace("do something");
}
function onBtn2(e:MouseEvent):void {
trace("do something else");
}