Director News
Director Web Sites
Mailing Lists
News Groups
Project Examples
Useful Web Sites

Don't miss these
XTRA dmmXSC - a client for XML Socket Server
3D, Lingo, Director Forums
Set Rotation angle of a Flash Member
selling|sound - royalty free music
Install QuickTime
De-Xplode that text random
PowerPoint / PPT Access



Article Puzzling!

Added on 8/9/1999


D7 D8 Mac PC Shockwave

This item has not yet been rated

Author: MediaMacros (website)

I am trying to create a puzzle where I can assemble the pieces and move completed chunks around. How can I do this with lingo?

Get the source
Ah, puzzles.  The first thing most people love to do with Director is create games, and it is a great tool for this.  The thing that gets a lot of people is that a puzzle appears simple, but there are a few layers of programming that must go into this.  It isn't especially hard, but you need to plan it out carefully from the beginning.  To make the system work there are three main areas we need to look at:

We need to be able to move "groups" of pieces

We need to be able to know if a piece is dropped close to one of the pieces it connects to

We need to test if the puzzle is complete

So where do we start?  For this example I am using a simple rectangular puzzle that is broken down on a grid pattern, with a slight tab for overlap.   This allows us to just let lingo check where pieces intersect.  The most valuable lingo command for something like this is the sendAllSprites command.  This command is basically like running a loop that gets each sprite to execute a piece of code, but it happens much faster.  The other nice thing is that if the sprite does not have that script attached to it, the command is ignored.  This way we can have one behavior on all the pieces and each can tell all the others to check and do things.  

When we start off the puzzle will be in one piece.  Since everything is in place we can do a number of things.  The beginSprite handler can record the location of the piece, its locZ (height in 3d space), and give it a group number.  The group is how we know when a sprite is connected to other sprites.  When 2 sprites "lock" together we set their group numbers the same.  Then when we click on a sprite we can start a loop that moves them all together.  We also need to get a list of all the sprites that are touching the current sprite.  We can not execute this in the beginSprite handler as all of the sprites will not respond.  (On sprite 1's beginSprite handler, 2,3,4, etc. do not yet exist on the stage).  So how do we do this?  For simplicity we can set a property in the beginSprite handler telling it that it has not initialized yet.  On the exitFrame handler it checks this property and runs the initialization script the first time it exits the frame, then resets the variable so the loop will not happen again.

So now what?  Well, once we know all this, we can turn the box upside down and throw everything out on the table!  You will see below that there is a command called shuffle.  This takes the pieces size and the stage size and picks a random place inside the stage to drop the piece.  Now the fun starts.  We don't want to set the sprites to moveable as it is harder to track them.  We only want them to move when we let them.  This way we can make everything move together using one handler.  Every time the sprite is clicked a loop starts that runs until the mouse is released.  In this loop we do a sendAllSprites call that tells every sprite to check its group number, and if they match, move the same amount as the mouse.  Then when we let go we tell all the sprites to gather a list of all the sprites they can connect to and send them back.  With this list we use the sendSprite command to send each sprite in the list a command to see if they are the same amount of space away from their original location as our clicked piece.  If so we change the group and lock them.  For ease of use I have also added a number of pixels that the user can be within and the pieces will snap together, as well as using the locZ to put the clicked piece above the other pieces.  Below is the code...

property spriteNum, origonalLoc, group, lockSpritesList, baseMove, initialized
global topZ, allPieces, tollerance, amDone

on beginSprite me
  if amDone = void then amDone = false
  if tollerance = void then tollerance = 5
  if allPieces = void then allPieces = 0
  allPieces = allPieces + 1
  topZ = sprite(spriteNum).locZ
  origonalLoc = sprite(spriteNum).loc
  group = spriteNum
  baseMove = sprite(spriteNum).loc
  initialized = false

on mouseDown me
  if amDone = false then
    topZ = topZ + 1
    sendAllSprites(#pullUp, group)
    startClick = the mouseLoc
    repeat while the stilldown
      sendAllSprites(#moveMe, group, (the mouseLoc - startClick))
    end repeat  
    --see if it "locked" with another bordering sprite
    searchList = []
    --get a searchList
    sendAllSprites(#checkGroup, group, searchList)
    returnList = []
    --check the locking location of all touching sprites
    repeat with x = 1 to searchList.count
      sendSprite(searchList[x], #checkLock, group, origonalLoc - sprite(spriteNum).loc, returnList)
      if returnList <> [] then
        sendAllSprites(#lockGroup, group, returnList[1][1], returnList[1][2])
        exit repeat
      end if
    end repeat
    winList = []
    sendAllSprites(#checkWin, winList, group)
    if winList.count = 0 then
      --all true, winner
      amDone = true
      go "win"
    end if
  end if

on exitFrame me
  if initialized = false then
    lockList = []
    sendAllSPrites(#getConnected, spriteNum, lockList)
    lockSpritesList = lockList
    initialized = true
  end if

on getConnected me, whatSprite, lockList
  if sprite(spriteNum).intersects(whatSprite) and whatSprite <> spriteNum then
end if

on shuffle me
  okW = the stageRight - the stageLeft - sprite(spriteNum).width
  okH = the stageBottom - the stageTop - sprite(spriteNum).height
  randomH = random(okW)
  randomV = random(okH)
  sprite(spriteNum).rect = rect(randomH, randomV, randomH + sprite(spriteNum).width, randomV + sprite(spriteNum).height)

on pullUp me, whatGroup
  if whatGroup = group then
    sprite(spriteNum).locZ = topZ
  end if

on getBase me
  baseMove = sprite(spriteNum).loc

on checkGroup me, theGroup, searchList
  if theGroup = group then
    if lockSpritesList.count > 0 then
      repeat with x = 1 to lockSpritesList.count
        if searchList.getOne(lockSpritesList[x]) = 0 then searchList.add(lockSpritesList[x])
      end repeat
    end if
  end if

on lockGroup me, whatGroup, newGroup, newOffset
  if group = whatGroup then
    group = newGroup
    sprite(spriteNum).loc = origonalLoc - newOffset
  end if

on moveMe me, whatGroup, whereMove
  if group = whatGroup then
    sprite(spriteNum).loc = sprite(spriteNum).baseMove + whereMove
  end if

on dropDown me, howMany
  sprite(spriteNum).locZ = sprite(SpriteNum).locZ - howMany

on checkLock me, whatGroup, whatOffset, returnList
  --if already linked no reason to run
  if whatGroup <> group then
    myOffset = origonalLoc - sprite(spriteNum).loc
    compareOffset = (myOffset - whatOffset)
    if abs(compareOffset.locH) <= tollerance and abs(compareOffset.locV) <= tollerance then
      --it is "snapable"
      returnList.add([group, myOffset])
    end if
  end if

on checkWin me, winList, whatGroup
  if group <> whatGroup then
  end if

This may look like a lot at first, but basically all that we have done is taken each command out as a separate handler and allowed it to be accessible through a sendSprite or sendAllSprites command.  From here we make our pieces, set them to background transparent (matte will not work as no pixels actually overlap), and make a marker called "win" to go to when all pieces match.  Put the assembled puzzle on the stage, drop on the behavior, and let it go.  To mix them up just issue the following command...


That's it!  Here is an example of the finished script inaction.   All that was added were the two buttons, an exitFrame handler to issue the shuffle command, and a basic "go to the frame" script.



36 South Court Sq
Suite 300
Newnan, GA 30263

Send e-mail