Articles Archive
Articles Search
Director Wiki
 

Jigsaw Puzzle behavior

March 12, 2000
by Pat McClellan

Dear Multimedia Handyman

I have this project I'm working on that has these elements of a painting that you snap together to form the final painting. It works similarly to the Drag and Drop puzzle that you have but instead of a fixed point on the stage I want the draggable elements to be able to snap to each other and then snap that grouping with another grouping until the puzzle is complete. The thing is I don't know how to have the snapped elements moveable so that they can snap with other snapped elements.

Arick

Dear Arick,

This is a tricky one indeed. We just need to think of a way to have pieces recognize when they are in alignment with one another -- rather than in alignment with a fixed spot on the stage. This got me thinking about registration points of the members and relative locs of the sprites.

Whenever I'm developing a project, I create my graphic assets in layered Photoshop files, with each layer registered to each other. Then, I import each layer as a separate cast member using PhotoCaster Xtra. One of the great things about PhotoCaster is that it allows you to import the members and retain their registration. That means that each cast member has a registration point that is not centered on the graphic itself, but rather is positioned at the center of the stage (or really, the center of your Photoshop image). That means that when you drag the group of members into your score, they will be aligned with each other onthe stage. More importantly, when they are aligned, they share the same loc. This is the key to how we'll make this work.

If you don't use PhotoCaster, you could probably achieve the same results with some tedious resetting of the regPoint for each member, but this would be a nightmare approach. If I haven't said so before, let me make it clear that I think PhotoCaster is an essential tool for development, saving me hours on each project.

To begin this project, I opened a stock photo in Photoshop. I chopped it up into 9 pieces, with each layer (piece) aligned with the rest. (I didn't bother doing the typical interlocking puzzle shapes, but I'd recommend it because it will be an intuitive cue for your users so you won't have to explain what to do.) I kept a copy of the full photo in the bottom layer of the Photoshop file for two reasons. First, it's a good reference for lining the pieces up. Second, when I import the pieces using Photocaster, they'll be antialiased to the full picture, so there won't be any white lines in between pieces. Here's what PhotoCaster looks like when I import the pieces (all at once.)

Note that button at the bottom that says "Keep RegPoint". That's what keeps the pieces aligned with each other. Of course, we'll start the puzzle program with the pieces moved around the stage to mix them up (though mine aren't very mixed up.)

Now let's think about the Lingo involved in moving the pieces around and linking them together in groups. What is required? Any two or more pieces that share the same loc (locH and locV) are linked together and should move as a unit. When all the pieces move as a single group then that means the puzzle is solved. Play with this demo to see how it works. I'll explain what's going on down in that status display later.

A sample movie is available for download in Mac or PC format. This includes a D7 movie and a Photoshop 5.5 source file.

When the player moves a piece, on mouseUp we need to check to see if that piece shares a loc with any of the other piece (or groups of pieces). If so, the piece we were moving should be added to the group -- or to the other piece to create a group. When we start and none of the pieces are linked, think of that as having nine groups with one sprite in each group. Reset the demo above. That's what you're seeing displayed in the status window. Now, link two pieces together and you'll see one of the groups disappear as it's linked to another group. Eventually, you'll have all nine sprites in the same group and the game is over.

For each group, we're not only keeping track of all of the sprites in that group, but also the loc of that group (remember, all the sprites in that group share the same loc). That means that when we move a sprite that's part of a group, we'll update the group's loc value and then update the loc of all the sprites in the group to match it. I've put all of this into a behavior called "PuzzleLock". I recommend that you download the demo movie above and follow along in the Lingo as I describe what's going on in each handler.

In the beginSprite handler, it checks to see if gGroupList exists -- only the first instance of the behavior (puzzle piece) will find that it doesn't exist. In that case, it creates an empty property list called gGroupList. It then creates a group name based on the sprite channel that it's in. For example, if this particular puzzle piece is in channel 3, it will add a property to gGroupList called #group3, which has values for the spriteNum and the loc of that sprite. As other sprites are linked to this one, #group3's spriteList will grow and as it is moved around, #group3's loc value will change.

We'll want to have a little "slop" allowed so that the user doesn't have to align the pieces exactly for a link. I've included a property called pSnapMargin, which is set to 10 pixels h and v. You can adjust this margin if you want. The last part of the beginSprite handler sets a flag property, pFlag, to #release -- meaning that the piece is not currently being dragged.

When the user clicks the mouseDown, we need to start keeping track of how much the person has moved the mouse -- and thus how far and which direction the group of pieces gets dragged. To do this, it sets pClickLoc and pStartLoc for the sprite. A property called pSpriteList is set to a list of all the sprites currently in the group. It also sets the pFlag to #drag... and that's all.

The dragging motion is achieved in the exitFrame handler. on exitframe, if pFlag is #drag then a newLoc is calculated for the group. All of the sprites in pSpriteList are then set to the newLoc. That's what keeps them moving as a group.

When the user releases the mouse, the mouseUp handler (and also the mouseUpOutside handler -- just to be safe) reset pFlag to #release. Now the real fun starts. This is when we have to find out if the piece (or group) has been linked to another. I separated out this method as a handler called checkGroup.

The on checkGroup handler looks through gGroupList, checking the groupLoc of each group. If it finds one that matches (within our margin of 10,10) it merges the groups together. Have a look:

on checkGroup me
  groups = gGroupList.count
  myLoc = pSprite.loc
  
  repeat with thisGroupPos = 1 to groups
  
    thisGroup = gGroupList.getPropAt(thisGroupPos)
    if thisGroup = pMyGroupName then next repeat
    thisLoc = gGroupList[thisGroup].groupLoc
    deltaH = abs(myLoc[1] - thisLoc[1])
    snapHmargin = pSnapMargin[1]
    
    if deltaH < snapHmargin then
    
      deltaV = abs(myLoc[2] - thisLoc[2])
      snapVmargin = pSnapMargin[2]
      if deltaV < snapVmargin then
      
        -- make the link   
        gGroupList.deleteProp(pMyGroupName)
        
        repeat with thisSprite in pSpriteList
          sendSprite(thisSprite, #resetGroup, thisGroup)
        end repeat
        if gGroupList.count = 1 then alert "Puzzle solved."
        exit repeat
        
      end if
      
    end if
    
  end repeat
  
end checkGroup

If all the conditions are met to make the link, the groups are merged by calling the resetGroup handler below.
on resetGroup me, newGroup
  pMyGroupName = newGroup
  pSprite.loc = gGroupList[pMyGroupName]
  gGroupList[pMyGroupName].spriteList.add(spriteNum)
  
end resetGroup

That's the basic functionality you requested. There are some other things you could do to polish it up. For example, this behavior allows you to link any two pieces that share the same loc -- not just two adjacent pieces. So you could have a group of several pieces moving around together that aren't even touching each other. I'm not sure how to approach that one. If anyone has a suggestion on that one, please post it in the DOUGthread forum for this article. Another issue is that the pieces are locked into their initial z-order. That means that the piece you're dragging might temporarily disappear behind another piece. I did an article a while back about re-shuffling the z-order so that the "active" piece is always on top. I'll leave it to someone else to figure out how to integrate those two concepts.

Finally, I'll recommend that you visit Jim Collins' Smoke and Mirrors site. Jim is the undisputed king of the Director jigsaw puzzle. He's even got a jigsaw puzzle maker on shockwave.com. Good luck with your project.

Patrick McClellan is Director Online's co-founder. Pat is Vice President, Managing Director for Jack Morton Worldwide, a global experiential marketing company. He is responsible for the San Francisco office, which helps major technology clients to develop marketing communications programs to reach enterprise and consumer audiences.

Copyright 1997-2024, Director Online. Article content copyright by respective authors.