Now we are going to introduce some target movie clips. We are getting closer to being able to make a drag-and-drop game with some intended purpose.
The first kind of drag-and-drop game we will make is one in which you have to drag a clip to a hidden matching clip of the same size and shape. This kind of game is one in which the user has to put together some kind of picture or map. In other words, by dragging clips they are assembling parts into some kind of overall thing. We'll call it a build game.
We'll create three matching clips. In fact, we'll use the same symbols in the library and just make a new instance of each one. So drag another square, circle , and triangle out of the library, and give them instance names of squareMatch, circleMatch, and triangleMatch. These clips will be mostly hidden from view using their alpha property. We'll just set their alpha to 0.2 (20%) for our purposes, so that you can still see where to drag the clips to (line 11 in the code below). But when making a real game, you might actually set their alpha to 0. But you would need some kind of gimmick, like an overall outline, to let the user know the general area where they need to drag the parts to.
The three clips we just introduced are placed into an array called matchArray. We need to order the parts in the array so that each clip in the dragArray has its counterpart, or match, in the matchArray. But on the screen, of course, they don't have to be in any certain order. The code works no matter where they are. Here's our new code:
var dragArray:Array = [square, circle, triangle];
var matchArray:Array = [squareMatch, circleMatch, triangleMatch];
var currentClip:MovieClip;
var startX:Number;
var startY:Number;
for(var i:int = 0; i < dragArray.length; i++) {
dragArray[i].buttonMode = true;
dragArray[i].addEventListener(MouseEvent.MOUSE_DOWN, item_onMouseDown);
matchArray[i].alpha = 0.2;
}
function item_onMouseDown(event:MouseEvent):void {
currentClip = MovieClip(event.currentTarget);
startX = currentClip.x;
startY = currentClip.y;
addChild(currentClip); //bring to the front
currentClip.startDrag();
stage.addEventListener(MouseEvent.MOUSE_UP, stage_onMouseUp);
}
function stage_onMouseUp(event:MouseEvent):void {
stage.removeEventListener(MouseEvent.MOUSE_UP, stage_onMouseUp);
currentClip.stopDrag();
var index:int = dragArray.indexOf(currentClip);
var matchClip:MovieClip = MovieClip(matchArray[index]);
if(currentClip.hitTestObject(matchClip)) {
//a match was made! position the clip on the matching clip:
currentClip.x = matchClip.x;
currentClip.y = matchClip.y;
//make it not draggable anymore:
currentClip.removeEventListener(MouseEvent.MOUSE_DOWN, item_onMouseDown);
currentClip.buttonMode = false;
} else {
//match was not made, so send the clip back where it started:
currentClip.x = startX;
currentClip.y = startY;
}
}
This should be fairly straightforward. In the initial for loop, we add a line that affects the items in the matchArray. We don't need to create a new loop, since dragArray and matchArray must always have the same number of items, we can just use the existing loop to affect the matchArray, too.
The other interesting stuff is going on in the stage_onMouseUp function. At this point, the user has let go of the mouse after having dragged a movie clip. Our first task is to identify the location in the dragArray of the currentClip. This is accomplished by using the indexOf() method of the array class. The indexOf() method finds out which array element matches the argument given to it in the parentheses. So if we say dragArray.indexOf(currentClip) it will compare every item in the dragArray and find out which array element matches currentClip, and return its index number into the array. This is very handy, it saves us from having to loop through the array ourselves. With our small number of array items, it will return either 0, 1, or 2, depending on whether the square, circle, or triangle was being dragged.
The reason for finding out this index number is so that we can find out which clip in the matchArray that currentClip ought to be a match for. As long as we always keep the two arrays parallel, with the same number of items, and arranged so that each clip in the dragArray is at the same position in the array that its counterpart occupies in the matchArray, this system works great (and it works just as well with a few items as it would with tens or hundreds of them)! So, anyway, when we find out which clip in the matchArray is currentClip's counterpart (matchArray[index]), we save that reference as the local variable matchClip, and we can then use that reference to do some hit testing.
The easiest hit testing method is hitTestObject. This method is already built into the MovieClip class. It takes one argument, which is the display object that we want to run the hit test against. The two objects we are hit testing, then, are currentClip and matchClip:
if(currentClip.hitTestObject(matchClip)) {
The hitTestObject method tests the bounding boxes of the two movie clips, and returns true if the bounding boxes intersect. This means that it is not very accurate. For example, the triangle and its matching clip can register a hit even if the actual graphics of the objects aren't touching (See illustration below). The circle has the same problem with its matching clip. Only the hit test between the square and its matching clip is really accurate. So, the more rectangular the objects being tested, the more accurate this hit test is. But for irregular objects, it's not all that great. It's too loose, it makes getting a match too easy. However, there may be projects in which this kind of hit test might be considered good enough, or even desirable.
Anyway, if the two objects are colliding, a match was made, so we set the currentClip's x and y to the same x and y of the matchClip. At that point, we also remove the event listener for MOUSE_DOWN from the currentClip, and also set its buttonMode property to false. This effectively takes it out of the game, preventing it from being dragged again, and the hand cursor going away helps let the user know that this is so.
If no match was made, the else part executes (line 35), and the currentClip snaps back to its origin, just like we made it do previously.
Here's how the swf behaves now. Notice as you drag the items how easy it is to make a match:
On the next page, we will explore using the other built-in hit testing method, hitTestPoint, and make it a bit stricter regarding what it takes to make a match.