00:00
00:00
Newgrounds Background Image Theme

Peacock6k just joined the crew!

We need you on the team, too.

Support Newgrounds and get tons of perks for just $2.99!

Create a Free Account and then..

Become a Supporter!

As3: startDrag Area in a Circle

3,578 Views | 3 Replies
New Topic Respond to this Topic

As3: startDrag Area in a Circle 2012-10-07 17:26:50


AS3: Main

Making an Object Draggable Within the Bounds of a Circle

This is an issue that had bugged me in the past. Say you have something that you want to drag in a certain area of the stage. The only way you can do this through the startDrag() function is if you make that area a rectangle. If you want to make that area a circle, you have to find some other way to do it.

Here's an example of the finished product:
Circular Draggable Area

The way the code works is by taking the distance of the line between the 2 points every frame and if it is longer than 101 pixels, it shortens it based on which quadrant of a coordinate plane it is on.

Before you start coding, make a 20px by 20px circle on the stage, and make it a MovieClip. Give the movieClip a class of "pt", once the code starts, it will automatically create a class for it called pt and this will allow us to place the little black circle on the stage through the code.

So here's how you go about doing that. First of all, you declare the different components of the Flash API that you'll need to utilize in this code. There are 4:

import flash.events.MouseEvent; //For when you click on the point
import flash.display.Shape; //For the line drawn between the 2 points
import flash.events.Event; //For checking the line's distance every frame
import flash.geom.Point; //For declaring both the origin and the draggable point as points

So once you have those at the top of the code, you can continue to declare the variables used in the code.
Once again, there are 4:

var line:Shape = new Shape(); //Declares the line that will be drawn between the 2 points as a shape
var quadrant:int = 2; //Declares which of the 4 quadrants of a standard x,y coordinate plane the point is currently in
var p1 = new pt(); //Declares p1 as an instance of the movieClip of a 20px by 20px circle we made earlier
var p2 = new pt(); //Same as above

The 2 points then have to be placed on the stage. p2 is going to be the origin in the middle of the stage while p1 is going to be the one within a 100px radius of it. We first give them x and y coordinates (without an underscore before "x"and "y", that's AS2) and then use the addChild(); function to place them both on the stage:

p2.x = 400; //Puts p2 at an x coordinate of 400, the center of an 800 by 450 stage, which is used in the example
p2.y = 225; // Same as above but relative to y
p1.x = p2.x-75; //Puts p1 75 pixels behind p2
p1.y = p2.y-75; //Same as above, but remember, in Flash the y axis starts in the top left and goes up towards the bottom
addChild(p2);
addChild(p1);

If you were to test your code right now, it should look like the example when it starts up except for the line connecting the 2 points. The next thing we have to add are the event listeners for p1 and the stage itself as to when things change. If you're unfamiliar with events, check out some of the other tutorials on the AS3: Main, such as MSGHero's. There are once again 4 eventListeners we must declare:

p1.addEventListener(MouseEvent.MOUSE_DOWN, drag); //When you click and hold p1, execute the "drag" function
stage.addEventListener(Event.EXIT_FRAME, checkQuad); //Every time you leave a frame, execute function "checkQuad"
stage.addEventListener(MouseEvent.MOUSE_UP, drop); //When you let go of p1, execute the "drop" function
stage.addEventListener(Event.ENTER_FRAME, drawLine); //Every time you enter a frame, execute function "drawLine"

Once we have declared those, we have to make the functions that coincide with those eventListeners. The first function, drag, is very simple:

function drag(e:MouseEvent){ //declares this as a function with the variable e representing what was clicked
e.target.startDrag(false); //Sets the target - in this case, p1 - to start dragging, the false saying you don't want the target to //center with the mouse when you finish dragging the object
}//closes the function

The second function, "drop", is comparitively simple:

function drop(e:MouseEvent){//declares this as a function with the variable e being what's no longer clicked on
stopDrag(); //Makes the target - in this case, p1 - stop dragging
} //Closes the function

The rest isn't quite as simple. The third function, drawLine, is very involved. The first thing we do is draw the line connecting the 2 points p1 and p2:

function drawLine(e:Event){ //Establishes this as a function dealing with an event
line.graphics.clear(); //Clears the line from the previous frame
line.graphics.lineStyle(3); //Establishes the line as a line that is 3 pixels wide
line.graphics.moveTo(p1.x,p1.y); //Makes the starting point p1
line.graphics.lineTo(p2.x,p2.y); //Draws the line from p1 to p2
addChild(line); //Puts the line on the stage

So now we have the line connecting the 2 and you can drag p1 anywhere and the line will stay connected. Next, we want to limit how far from p2 p1 can go in any direction. Remember the distance formula from geometry class? If not, it's the square root of ((x2 - x1) squared + (y2 - y1) squared). Look it up to see it with the radical extending across the whole thing. Anyway, we will apply the formula to this code by rewriting it like this:

Math.sqrt(((p2.x-p1.x)*(p2.x-p1.x))+((p2.y-p1.y)*(p2.y-p1.y)))

A bit daunting, but that allows us to establish how long the line between p2 and p1 is. And then, we adjust it accordingly based on which of the 4 quadrants p1 is in:

if(Math.sqrt(((p2.x-p1.x)*(p2.x-p1.x))+((p2.y-p1.y)*(p2.y-p1.y))) > 101){ //If the line is longer than 101 pixels
		if(quadrant == 1){
			p1.x-=(Math.sqrt(((p2.x-p1.x)*(p2.x-p1.x))+((p2.y-p1.y)*(p2.y-p1.y)))-100);
			p1.y+=(Math.sqrt(((p2.x-p1.x)*(p2.x-p1.x))+((p2.y-p1.y)*(p2.y-p1.y)))-100);
		}else
		if(quadrant == 2){
			p1.x+=(Math.sqrt(((p2.x-p1.x)*(p2.x-p1.x))+((p2.y-p1.y)*(p2.y-p1.y)))-100);
			p1.y+=(Math.sqrt(((p2.x-p1.x)*(p2.x-p1.x))+((p2.y-p1.y)*(p2.y-p1.y)))-100);
		}else
		if(quadrant == 3){
			p1.x+=(Math.sqrt(((p2.x-p1.x)*(p2.x-p1.x))+((p2.y-p1.y)*(p2.y-p1.y)))-100);
			p1.y-=(Math.sqrt(((p2.x-p1.x)*(p2.x-p1.x))+((p2.y-p1.y)*(p2.y-p1.y)))-100);
		}else
		if(quadrant == 4){
			p1.x-=(Math.sqrt(((p2.x-p1.x)*(p2.x-p1.x))+((p2.y-p1.y)*(p2.y-p1.y)))-100);
			p1.y-=(Math.sqrt(((p2.x-p1.x)*(p2.x-p1.x))+((p2.y-p1.y)*(p2.y-p1.y)))-100);
		}
	}
} //Closes the function

What we did just there is take the total distance of the line and subtract that from the length of the line except for 100 pixels, which is how much we want, and then applied that to all 4 of the quadrants' x and y coordinates. Finallly, we need the function that checks which quadrant p1 is in. We do this by checking if its x is more or less than p2's, as well as its y and use that information to determine which quadrant it's in. Like so:

function checkQuad(e:Event){
	if(p1.x < p2.x && p1.y > p2.y){ //If it's to the left and lower than p2
		quadrant = 3;
	}else
	if(p1.x > p2.x && p1.y > p2.y){ //If it's to the right and lower than p2
		quadrant = 4;
	}else
	if(p1.x > p2.x && p1.y < p2.y){ //If it's to the right and higher than p2
		quadrant = 1;
	}else
	if(p1.x < p2.x && p1.y < p2.y){ //If it's to the left and higher than p2
		quadrant = 2;
	}
}

And that's all of the code. If you applied it correctly, that should allow you to have the same effect as the one in the example. Thanks for reading!


BBS Signature

Response to As3: startDrag Area in a Circle 2012-10-07 19:15:46


Most of the text is unrelated to the actual problem and when it comes to that, the solution is best described as overcomplicated.

The Point class is imported, but never used.
Instead, all formulas are rewritten by hand.

And copy&paste is really handy, especially when it comes to huge chunks of code, isn't it?

Response to As3: startDrag Area in a Circle 2012-10-07 20:19:40


At 10/7/12 07:15 PM, milchreis wrote: Most of the text is unrelated to the actual problem and when it comes to that, the solution is best described as overcomplicated.

The Point class is imported, but never used.
Instead, all formulas are rewritten by hand.

And copy&paste is really handy, especially when it comes to huge chunks of code, isn't it?

Thanks for the condescending advice, I'll consolidate more next time


BBS Signature

Response to As3: startDrag Area in a Circle 2012-10-07 20:51:10


The code below just gives another way to accomplish something similar. The main difference is that I am using the MOUSE_MOVE event. Also, if you are dragging the dot outside of the boundary, it calculates the vector from p1 to p2 and scales it to desired length.

const length:int = 100;

var dragging:Boolean = false;
var tx:int;
var ty:int;

var p1:Sprite = new Sprite();
var p2:Sprite = new Sprite();

p1.graphics.beginFill(0);
p1.graphics.drawCircle(0, 0, 10);
p1.graphics.endFill();

p2.graphics.copyFrom(p1.graphics);

p1.x = stage.stageWidth / 2;
p1.y = stage.stageHeight / 2;

p2.x = p1.x + length;
p2.y = p1.y;

addChild(p1);
addChild(p2);

p2.addEventListener(MouseEvent.MOUSE_DOWN, mouseDown);
stage.addEventListener(MouseEvent.MOUSE_UP, mouseUp);
stage.addEventListener(MouseEvent.MOUSE_MOVE, mouseMove);

function mouseDown(event:MouseEvent):void
{
	dragging = true;
}

function mouseUp(event:MouseEvent):void
{
	dragging = false;
}

function mouseMove(event:MouseEvent):void
{
	if (dragging)
	{
		tx = event.stageX - p1.x;
		ty = event.stageY - p1.y;
		if (tx * tx + ty * ty > length * length)
		{
			p2.x = p1.x + tx / Math.sqrt(tx * tx + ty * ty) * length;
			p2.y = p1.y + ty / Math.sqrt(tx * tx + ty * ty) * length;
		}
		else
		{
			p2.x = event.stageX;
			p2.y = event.stageY;
		}
	}
}

BBS Signature