Tutorial: Drag and throw an object on stage

In this tutorial we will build a movie that contains a simple movieclip that allows the user to click and drag the clip, and when released exhibits some inertia and continues to move and bounce off walls like an air hockey puck.

[kml_flashembed movie="http://www.theorigin.net/tutorialFiles/HockeyPuck/hockeypuck.swf" height="400" width="550" /]

To setup the stage, create a movieclip that contains a square aligned at 0,0. Place an instance on the main stage and give it a name of “puck”.

First we need to enable a click and drag on the puck. To do this we’ll use the onPress Event handler to create an onEnterFrame function that keeps the puck aligned with our mouse.

puck.onPress = function(){
	this.onEnterFrame = function(){
        this._x = _root._xmouse;
        this._y = _root._ymouse;
    }
}

This script is OK, but rather than smoothly picking up the puck where the mouse touched it, the puck snaps its top corner to our mouse. To alleviate this we need to subtract the mouse position relative to the puck from the mouse position relative to the stage by setting an anchor when the mouse is pressed. We won’t worry about garbage collecting this onEnterFrame because we are going to overwrite it when the mouse releases.

Note: in general you shouldn’t just leave open onEnterFrame functions running. When you get a number of onEnterFrame functions going at the same time the performance of your movie can suffer greatly or even crash. For this reason you should terminate your onEnterFrame functions when they have finished their purpose. This is called garbage collection.

puck.onPress = function(){
     anchorX = this._xmouse;
	anchorY = this._ymouse;
	this.onEnterFrame = function(){
        this._x = _root._xmouse - anchorX;
        this._y = _root._ymouse - anchorY;
    }
}

Now we need a way to determine which way the mouse was moving when it released the puck. For this we can record _xmouse and _ymouse values every frame that the mouse is down and store them for use in the onEnterFrame we are going to create onRelease.

puck.onPress = function(){
    anchorX = this._xmouse;
	anchorY = this._ymouse;
	this.onEnterFrame = function(){
        lastXMouse = _root._xmouse;
        lastYMouse = _root._ymouse;

        this._x = _root._xmouse - anchorX;
        this._y = _root._ymouse - anchorY;
    }
}

To make the calculation as to which direction to send the puck after release, we only need the mouse position one frame back, but if we were to record more frames, we could get an average direction just prior to release or even find a curve for the puck to follow. Here though we’ll just send the puck in a straight line.

The math to determine which direction the mouse was moving is a simple slope equation using two points (our current and previous mouse points) We’ll define this function for onRelease and onReleaseOutside just in case your mouse is a bit quicker than your movie.

puck.onRelease = puck.onReleaseOutside = function(){
    xDirection = (_root._xmouse - lastXMouse);
    yDirection  = (_root._yMouse - lastYMouse);
}

We use the direction data to create a new movieclip that moves the puck around.

puck.onRelease = puck.onReleaseOutside = function(){
    xDirection = (_root._xmouse - lastXMouse);
    yDirection  = (_root._yMouse - lastYMouse);
    this.onEnterFrame = function(){
	    this._x += xDirection;
	    this._y += yDirection;
    }
}

In order to make the puck bounce off the walls we need to check to see whether or not it has reached the edge of the stage, and have it reverse direction if it has.

puck.onRelease = puck.onReleaseOutside = function(){
    xDirection = (_root._xmouse - lastXMouse);
    yDirection  = (_root._yMouse - lastYMouse);
    this.onEnterFrame = function(){

	// Bounce off the Walls
	if (this._x < 0){
			xDirection *= -1;
			this._x = 0;
		}
		if (this._x + this._width > Stage.width){
			xDirection *= -1;
			this._x = Stage.width - this._width;
		}
		if (this._y < 0){
			yDirection *= -1;
			this._y = 0;
		}

		if (this._y + this._height > Stage.height){
			yDirection *= -1;
			this._y = Stage.height - this._height;
		}

        // Set New Position
        this._x += xDirection;
	    this._y += yDirection;
     }
}

Finally we want the puck to slow down and eventually stop over time so we’ll add a decay function to the x and Y directions that reduce them by 10% every frame.

Additionally once our puck has stopped, we need to terminate and garbage collect the onEnterFrame function.

puck.onRelease = puck.onReleaseOutside = function(){
    xDirection = (_root._xmouse - lastXMouse);
    yDirection  = (_root._yMouse - lastYMouse);
	this.onEnterFrame = function(){

		// decay x
		if (Math.abs(xDirection) > 0.5){
			xDirection *= .9;
		} else {
			xDirection = 0;
		}

		// decay y
		if (Math.abs(yDirection) > 0.5){
			yDirection *= .9;
		} else {
			yDirection = 0;
		}

		//Garbage Collection
		if (xDirection == 0 && yDirection==0){
			this.onEnterFrame = null;
		}

		//Bounce off the walls
		if (this._x < 0){
			xDirection *= -1;
			this._x = 0;
		}
		if (this._x + this._width > Stage.width){
			xDirection *= -1;
			this._x = Stage.width - this._width;
		}
		if (this._y < 0){
			yDirection *= -1;
			this._y = 0;
		}

		if (this._y + this._height > Stage.height){
			yDirection *= -1;
			this._y = Stage.height - this._height;
		}

		//Position
		this._x += xDirection;
		this._y += yDirection;

	}
}

Download Source

Patrick Gunderson is a Flash Designer and Developer at TBWA\Tequila\ in Los Angeles.