Dragging objects to a specific location, like a puzzle

@tlf09a,

First I must say, “I love questions like this because answering them is so much fun!”

Corona SDK is quite powerful and easy to use and it especially shines when implementing basic visuals + mechanics like this.

In this case, there are many possible answers to your question, but I’ll go for a simple solution to demonstrate my prior statements.

Using the SSK sampler, and basic Corona SDK code (no SSK special stuff) I did the following:

  1. Took an image off my drive and sliced it into 3 by 3 segments using PaintShopPro.

  2. Wrote some Corona Code to:
    a. Lay the puzzle out in the solved position.
    b. Store pertinent data regarding the solved and last positions of the tiles.
    c. Make some rectangles to act as outlines of the images
    d. Add a touch function to each tile

  • The touch code handles the drag-n-drop functionality I need to move puzzle piecens
    e. Swizzle the board. i.e. Rearranged the board.
  1. Wrote some code to handle the drop calculation. This code basically tests to see if I’m dropping the tile over another tile and if I’m ‘close enough’ to the center of the tile to swap places. If I am the tiles swap positions. If not, the tile I’m dragging moves back to its last position.

  2. Wrote some code to see if the puzzle was solved. This code iterates over the table of pieces and compares each piece’s current position to the initial solved position (before the swizzle). If all of the pieces are in their original locations, this code hides the outlines and removes all of the touch listeners.

Here is a video of the final results: http://www.youtube.com/watch?v=gFDAjtjfAJY

Here is the code I wrote:

local puzzleTable  
local currentDragPiece  
  
local createPuzzle  
local doDrop  
local swizzlePuzzle  
local testCompleted  
createPuzzle = function ( )  
  
 local offsetX = centerX - 106  
 local offsetY = centerY - 80   
  
 puzzleTable = {}  
  
 for x = 1, 3 do  
 for y = 1, 3 do  
 -- EFM you need your own puzzle pieces if you want to try this  
 local piece = display.newImageRect( imagesDir .. "forumHelp/puzzle/puzzleBase\_" .. y .. "x" .. x .. ".jpg", 106, 80)  
 local outline = display.newRect( 0, 0, 106, 80)   
  
 piece.myOutline = outline  
  
 piece.x = (x - 1) \* 106 + offsetX  
 piece.y = (y - 1) \* 80 + offsetY   
  
 piece.startX = piece.x  
 piece.startY = piece.y  
  
 piece.lastX = piece.x  
 piece.lastY = piece.y  
  
 outline.x = piece.x  
 outline.y = piece.y  
 outline:setFillColor(0,0,0,0)  
 outline.strokeWidth = 3  
 outline:setStrokeColor(255,255,255)  
  
 function piece:touch( event )  
 local target = event.target  
  
 if( event.phase == "began" ) then  
 target:toFront()  
 target.myOutline:toFront()  
 currentDragPiece = target  
 return true  
  
 elseif( event.phase == "moved" and target == currentDragPiece ) then  
 currentDragPiece.x = event.x - event.xStart + currentDragPiece.lastX  
 currentDragPiece.y = event.y - event.yStart + currentDragPiece.lastY  
 currentDragPiece.myOutline.x = currentDragPiece.x  
 currentDragPiece.myOutline.y = currentDragPiece.y  
 return true  
  
 elseif( event.phase == "ended" and target == currentDragPiece ) then  
 doDrop( target, 20 )  
 testCompleted()  
 end  
  
 end  
  
 piece:addEventListener( "touch", piece )  
  
 puzzleTable[#puzzleTable+1] = piece  
 end  
 end  
end  
  
doDrop = function ( curPiece, snapDist )  
  
 local snapDistSquared = snapDist \* snapDist  
  
 for i = 1, #puzzleTable do  
 local piece = puzzleTable[i]  
  
 if(piece ~= curPiece) then  
  
 local dx = piece.x - curPiece.x  
 local dy = piece.y - curPiece.y  
  
 local lengthSquared = dx \* dx + dy \* dy  
  
 print(lengthSquared, snapDistSquared)  
  
 if( lengthSquared \<= snapDistSquared ) then  
 curPiece.x = piece.x  
 curPiece.y = piece.y  
  
 piece.x = curPiece.lastX  
 piece.y = curPiece.lastY  
  
 curPiece.lastX = curPiece.x  
 curPiece.lastY = curPiece.y  
  
 piece.lastX = piece.x  
 piece.lastY = piece.y  
  
 curPiece.myOutline.x = curPiece.x  
 curPiece.myOutline.y = curPiece.y  
  
 piece.myOutline.x = piece.x  
 piece.myOutline.y = piece.y  
  
 return true  
 end  
  
 end  
 end  
  
 curPiece.x = curPiece.lastX  
 curPiece.y = curPiece.lastY  
  
 curPiece.myOutline.x = curPiece.x  
 curPiece.myOutline.y = curPiece.y  
  
end  
swizzlePuzzle = function ( )  
  
 for i = 1, 100 do  
 local pieceA = puzzleTable[math.random(1, #puzzleTable)]  
 local pieceB = puzzleTable[math.random(1, #puzzleTable)]  
  
 if(pieceB ~= pieceA) then  
  
 pieceA.x = pieceB.x  
 pieceA.y = pieceB.y  
  
 pieceB.x = pieceA.lastX  
 pieceB.y = pieceA.lastY  
  
 pieceA.lastX = pieceA.x  
 pieceA.lastY = pieceA.y  
  
 pieceB.lastX = pieceB.x  
 pieceB.lastY = pieceB.y  
  
 pieceA.myOutline.x = pieceA.x  
 pieceA.myOutline.y = pieceA.y  
  
 pieceB.myOutline.x = pieceB.x  
 pieceB.myOutline.y = pieceB.y  
  
 end  
 end  
  
end  
  
testCompleted = function ()  
  
 for i = 1, #puzzleTable do  
 local piece = puzzleTable[i]  
 if( piece.x ~= piece.startX or piece.y ~= piece.startY ) then  
 return false  
 end  
 end  
  
 for i = 1, #puzzleTable do  
 local piece = puzzleTable[i]  
 piece.myOutline.isVisible = false  
 piece:removeEventListener( "touch", piece.touch )  
 end  
  
 return true  
end  
  
createPuzzle()  
swizzlePuzzle()  

Note: This code probably won’t work right ‘out-of-the-box’, but it will be close. Regardless, I hope this helps get you started!

Note2: The entire demo has been uploaded to SSK so you can download it and look at the sampler if you want. I warning however, SSK and the sampler are still very messy. I’m reorganizing, adding features, and working on docs now.

Cheers,
Ed
Roaming Gamer, LLC.
SSK for Corona SDK (github) (videos)
[import]uid: 110228 topic_id: 32243 reply_id: 128328[/import]

That’s basically what I do. I record the center point of each piece, then do a small invisible square centered on that piece. Using non-physics based bounding box testing I see if my piece collieds with the small box (you can also use small boxes with the pieces for more precision, just move the box and piece at the same time . . . When you detect the collision, transition the piece to the X, Y of where it goes and then dispose of the touch handler on the piece so it cant be moved again [import]uid: 19626 topic_id: 32243 reply_id: 128329[/import]

@tlf09a,

First I must say, “I love questions like this because answering them is so much fun!”

Corona SDK is quite powerful and easy to use and it especially shines when implementing basic visuals + mechanics like this.

In this case, there are many possible answers to your question, but I’ll go for a simple solution to demonstrate my prior statements.

Using the SSK sampler, and basic Corona SDK code (no SSK special stuff) I did the following:

  1. Took an image off my drive and sliced it into 3 by 3 segments using PaintShopPro.

  2. Wrote some Corona Code to:
    a. Lay the puzzle out in the solved position.
    b. Store pertinent data regarding the solved and last positions of the tiles.
    c. Make some rectangles to act as outlines of the images
    d. Add a touch function to each tile

  • The touch code handles the drag-n-drop functionality I need to move puzzle piecens
    e. Swizzle the board. i.e. Rearranged the board.
  1. Wrote some code to handle the drop calculation. This code basically tests to see if I’m dropping the tile over another tile and if I’m ‘close enough’ to the center of the tile to swap places. If I am the tiles swap positions. If not, the tile I’m dragging moves back to its last position.

  2. Wrote some code to see if the puzzle was solved. This code iterates over the table of pieces and compares each piece’s current position to the initial solved position (before the swizzle). If all of the pieces are in their original locations, this code hides the outlines and removes all of the touch listeners.

Here is a video of the final results: http://www.youtube.com/watch?v=gFDAjtjfAJY

Here is the code I wrote:

local puzzleTable  
local currentDragPiece  
  
local createPuzzle  
local doDrop  
local swizzlePuzzle  
local testCompleted  
createPuzzle = function ( )  
  
 local offsetX = centerX - 106  
 local offsetY = centerY - 80   
  
 puzzleTable = {}  
  
 for x = 1, 3 do  
 for y = 1, 3 do  
 -- EFM you need your own puzzle pieces if you want to try this  
 local piece = display.newImageRect( imagesDir .. "forumHelp/puzzle/puzzleBase\_" .. y .. "x" .. x .. ".jpg", 106, 80)  
 local outline = display.newRect( 0, 0, 106, 80)   
  
 piece.myOutline = outline  
  
 piece.x = (x - 1) \* 106 + offsetX  
 piece.y = (y - 1) \* 80 + offsetY   
  
 piece.startX = piece.x  
 piece.startY = piece.y  
  
 piece.lastX = piece.x  
 piece.lastY = piece.y  
  
 outline.x = piece.x  
 outline.y = piece.y  
 outline:setFillColor(0,0,0,0)  
 outline.strokeWidth = 3  
 outline:setStrokeColor(255,255,255)  
  
 function piece:touch( event )  
 local target = event.target  
  
 if( event.phase == "began" ) then  
 target:toFront()  
 target.myOutline:toFront()  
 currentDragPiece = target  
 return true  
  
 elseif( event.phase == "moved" and target == currentDragPiece ) then  
 currentDragPiece.x = event.x - event.xStart + currentDragPiece.lastX  
 currentDragPiece.y = event.y - event.yStart + currentDragPiece.lastY  
 currentDragPiece.myOutline.x = currentDragPiece.x  
 currentDragPiece.myOutline.y = currentDragPiece.y  
 return true  
  
 elseif( event.phase == "ended" and target == currentDragPiece ) then  
 doDrop( target, 20 )  
 testCompleted()  
 end  
  
 end  
  
 piece:addEventListener( "touch", piece )  
  
 puzzleTable[#puzzleTable+1] = piece  
 end  
 end  
end  
  
doDrop = function ( curPiece, snapDist )  
  
 local snapDistSquared = snapDist \* snapDist  
  
 for i = 1, #puzzleTable do  
 local piece = puzzleTable[i]  
  
 if(piece ~= curPiece) then  
  
 local dx = piece.x - curPiece.x  
 local dy = piece.y - curPiece.y  
  
 local lengthSquared = dx \* dx + dy \* dy  
  
 print(lengthSquared, snapDistSquared)  
  
 if( lengthSquared \<= snapDistSquared ) then  
 curPiece.x = piece.x  
 curPiece.y = piece.y  
  
 piece.x = curPiece.lastX  
 piece.y = curPiece.lastY  
  
 curPiece.lastX = curPiece.x  
 curPiece.lastY = curPiece.y  
  
 piece.lastX = piece.x  
 piece.lastY = piece.y  
  
 curPiece.myOutline.x = curPiece.x  
 curPiece.myOutline.y = curPiece.y  
  
 piece.myOutline.x = piece.x  
 piece.myOutline.y = piece.y  
  
 return true  
 end  
  
 end  
 end  
  
 curPiece.x = curPiece.lastX  
 curPiece.y = curPiece.lastY  
  
 curPiece.myOutline.x = curPiece.x  
 curPiece.myOutline.y = curPiece.y  
  
end  
swizzlePuzzle = function ( )  
  
 for i = 1, 100 do  
 local pieceA = puzzleTable[math.random(1, #puzzleTable)]  
 local pieceB = puzzleTable[math.random(1, #puzzleTable)]  
  
 if(pieceB ~= pieceA) then  
  
 pieceA.x = pieceB.x  
 pieceA.y = pieceB.y  
  
 pieceB.x = pieceA.lastX  
 pieceB.y = pieceA.lastY  
  
 pieceA.lastX = pieceA.x  
 pieceA.lastY = pieceA.y  
  
 pieceB.lastX = pieceB.x  
 pieceB.lastY = pieceB.y  
  
 pieceA.myOutline.x = pieceA.x  
 pieceA.myOutline.y = pieceA.y  
  
 pieceB.myOutline.x = pieceB.x  
 pieceB.myOutline.y = pieceB.y  
  
 end  
 end  
  
end  
  
testCompleted = function ()  
  
 for i = 1, #puzzleTable do  
 local piece = puzzleTable[i]  
 if( piece.x ~= piece.startX or piece.y ~= piece.startY ) then  
 return false  
 end  
 end  
  
 for i = 1, #puzzleTable do  
 local piece = puzzleTable[i]  
 piece.myOutline.isVisible = false  
 piece:removeEventListener( "touch", piece.touch )  
 end  
  
 return true  
end  
  
createPuzzle()  
swizzlePuzzle()  

Note: This code probably won’t work right ‘out-of-the-box’, but it will be close. Regardless, I hope this helps get you started!

Note2: The entire demo has been uploaded to SSK so you can download it and look at the sampler if you want. I warning however, SSK and the sampler are still very messy. I’m reorganizing, adding features, and working on docs now.

Cheers,
Ed
Roaming Gamer, LLC.
SSK for Corona SDK (github) (videos)
[import]uid: 110228 topic_id: 32243 reply_id: 128328[/import]

That’s basically what I do. I record the center point of each piece, then do a small invisible square centered on that piece. Using non-physics based bounding box testing I see if my piece collieds with the small box (you can also use small boxes with the pieces for more precision, just move the box and piece at the same time . . . When you detect the collision, transition the piece to the X, Y of where it goes and then dispose of the touch handler on the piece so it cant be moved again [import]uid: 19626 topic_id: 32243 reply_id: 128329[/import]

@emaurina

Thanks for all the info, it seems to make sense.
I will try to get mine working based off this and I will post on this thread again if it is still not working. [import]uid: 174439 topic_id: 32243 reply_id: 129252[/import]

@emaurina

Thanks for all the info, it seems to make sense.
I will try to get mine working based off this and I will post on this thread again if it is still not working. [import]uid: 174439 topic_id: 32243 reply_id: 129252[/import]

@emaurina
So a couple more things that would help me.
If it wouldn’t be too much trouble, I think having more comments in your code would be extremly helpful in understanding the logic of it and what exactly you are doing.

Also, I am using Storyboard API and was wondering which parts of my puzzle should go where? Like should these functions be outside of createScene or enterScene or would they be better within one of those?

Thanks for the help. [import]uid: 174439 topic_id: 32243 reply_id: 130013[/import]

@emaurina
So a couple more things that would help me.
If it wouldn’t be too much trouble, I think having more comments in your code would be extremly helpful in understanding the logic of it and what exactly you are doing.

Also, I am using Storyboard API and was wondering which parts of my puzzle should go where? Like should these functions be outside of createScene or enterScene or would they be better within one of those?

Thanks for the help. [import]uid: 174439 topic_id: 32243 reply_id: 130013[/import]

@tlf09a,

  1. Sorry about the lack of comments. I coded it up pretty fast, but should probably have put a few notes in there. 8)

  2. I would use the enterScene() callback to build the puzzle and the exitScene() callback to tear it down.

The problem with using createScene() and destroyScene() is that they only get called once unless the scene is purged, thus leaving lots of stuff behind when you exit.

That said, it really depends on how you structure your game. If you have one scene file responsible for loading one puzzle then you could use the create-/destroy- Scene. However, if you use one scene file to load multiple puzzles then using enter-/exit- Scene() or willEnter-/didExit- Scene would be easiest and cleanest.

I hope this helps. I’m sick today so I may not be making a lot of sense. Sigh…

-Ed [import]uid: 110228 topic_id: 32243 reply_id: 130088[/import]

@tlf09a,

  1. Sorry about the lack of comments. I coded it up pretty fast, but should probably have put a few notes in there. 8)

  2. I would use the enterScene() callback to build the puzzle and the exitScene() callback to tear it down.

The problem with using createScene() and destroyScene() is that they only get called once unless the scene is purged, thus leaving lots of stuff behind when you exit.

That said, it really depends on how you structure your game. If you have one scene file responsible for loading one puzzle then you could use the create-/destroy- Scene. However, if you use one scene file to load multiple puzzles then using enter-/exit- Scene() or willEnter-/didExit- Scene would be easiest and cleanest.

I hope this helps. I’m sick today so I may not be making a lot of sense. Sigh…

-Ed [import]uid: 110228 topic_id: 32243 reply_id: 130088[/import]

I am planning on doing multiple puzzles eventually so I guess I will try the enter/exit scene method.

Would it possible to modify your code to have pieces that are not perfect squares or rectangle, but maybe have an L shape or something more ridiculous like a cross (+)?

Thanks Ed.

-Taylor [import]uid: 174439 topic_id: 32243 reply_id: 130155[/import]

I am planning on doing multiple puzzles eventually so I guess I will try the enter/exit scene method.

Would it possible to modify your code to have pieces that are not perfect squares or rectangle, but maybe have an L shape or something more ridiculous like a cross (+)?

Thanks Ed.

-Taylor [import]uid: 174439 topic_id: 32243 reply_id: 130155[/import]

@tlf09a,

I have “make a jigsaw puzzle tutorial OR sample” on my list, but that won’t happen right away.

That said, "Yes you can make non-uniform puzzle pieces. The easiest way I can think of to do this is by using masks. Use the Corona Mask feature to make an image look like it has been cut out.

Off the top of my head there seem to be two ways to assemble the pieces:

A. Oversized slices - In this case the puzzle pieces are slices such that adjoining pieces contain part of the same images. Then, when they are combined with masks the images align/ovlap properly.

B. Use the full image and on each piece and position the mask such that it looks like just part of the puzzle. You can position masks with maskX/maskY. Now, I’m not sure how this would affect memory usage, but if the full image can be stored in video memory, this might actually be cheaper.

I hope what I’ve said has made a little sense? If you want to get updates, you can subscribe to my RSS feed on my site. I will make a blog announcement when the tutorials/sample is done.
[import]uid: 110228 topic_id: 32243 reply_id: 130305[/import]

@tlf09a,

I have “make a jigsaw puzzle tutorial OR sample” on my list, but that won’t happen right away.

That said, "Yes you can make non-uniform puzzle pieces. The easiest way I can think of to do this is by using masks. Use the Corona Mask feature to make an image look like it has been cut out.

Off the top of my head there seem to be two ways to assemble the pieces:

A. Oversized slices - In this case the puzzle pieces are slices such that adjoining pieces contain part of the same images. Then, when they are combined with masks the images align/ovlap properly.

B. Use the full image and on each piece and position the mask such that it looks like just part of the puzzle. You can position masks with maskX/maskY. Now, I’m not sure how this would affect memory usage, but if the full image can be stored in video memory, this might actually be cheaper.

I hope what I’ve said has made a little sense? If you want to get updates, you can subscribe to my RSS feed on my site. I will make a blog announcement when the tutorials/sample is done.
[import]uid: 110228 topic_id: 32243 reply_id: 130305[/import]

I’m not sure I quite understand the second example, because you would use masks to display each image to look like a piece, but how would it look like they fit together tightly.

Also, I was wondering if you could explain the doDrop function a little more because I’m not sure what snapDist and snapDistSquared does? Also not sure what comparing to lengthSquared does. At the end of the function you set the curPiece = to the curPiece.last of x and y, why do you do this?

In swizzlePuzzle, is all you are really doing is switching out the pieces A and B that were randomly picked out of the pieces already created? Why do you loop through 100 times?

A lot of questions, sorry. [import]uid: 174439 topic_id: 32243 reply_id: 130748[/import]

I’m not sure I quite understand the second example, because you would use masks to display each image to look like a piece, but how would it look like they fit together tightly.

Also, I was wondering if you could explain the doDrop function a little more because I’m not sure what snapDist and snapDistSquared does? Also not sure what comparing to lengthSquared does. At the end of the function you set the curPiece = to the curPiece.last of x and y, why do you do this?

In swizzlePuzzle, is all you are really doing is switching out the pieces A and B that were randomly picked out of the pieces already created? Why do you loop through 100 times?

A lot of questions, sorry. [import]uid: 174439 topic_id: 32243 reply_id: 130748[/import]

@tlf09a,

  1. I made a video showing how they could look. Not great art or puzzle pieces, but I think you’ll get the gist: http://www.youtube.com/watch?v=UMfqmeKDK8E
    (I’ll upload the code and binary later today to gitHub)

  2. I measure the distance between where a puzzle piece is dropped and the nearest puzzle piece center.

To calculate the length of a vector you do this:
[lua]local x1, y1 = 100, 100
local x2, y2 = 100, 150

local dx = x2-x1
local dy = y2-y1

local squaredLength = dx * dx + dy * dy

local length = math.sqrt ( squaredLength )

print("squaredLength == ", squaredLength ) – prints 2500
print("length == ", length ) – prints 50[/lua]

This calculation is ‘expensive’ because of the math.sqrt() call. Sure, a few times you can afford, but 100’s or 1000’s of calls and you’re wasting time. So, a cheat is to simply calculate squaredLength and compare that to ‘snapDist * snapDist’.

  1. Why 100 times? Just to mix it up a bit more. math.random() may return the same value on subsequent calls, so I call it many more times than I might need for a perfect unique random generator to be sure I get enough ‘new and non-repeating’ values during the swizzle.
  2. Don’t be sorry questions are good.
    Note: The sample I’ll release later is by no means a ‘tutorial grade’ example, but I tried to put a few more comments in the code. [import]uid: 110228 topic_id: 32243 reply_id: 130864[/import]

I uploaded updated source code and binaries.
https://github.com/roaminggamer/
[import]uid: 110228 topic_id: 32243 reply_id: 130872[/import]

@tlf09a,

  1. I made a video showing how they could look. Not great art or puzzle pieces, but I think you’ll get the gist: http://www.youtube.com/watch?v=UMfqmeKDK8E
    (I’ll upload the code and binary later today to gitHub)

  2. I measure the distance between where a puzzle piece is dropped and the nearest puzzle piece center.

To calculate the length of a vector you do this:
[lua]local x1, y1 = 100, 100
local x2, y2 = 100, 150

local dx = x2-x1
local dy = y2-y1

local squaredLength = dx * dx + dy * dy

local length = math.sqrt ( squaredLength )

print("squaredLength == ", squaredLength ) – prints 2500
print("length == ", length ) – prints 50[/lua]

This calculation is ‘expensive’ because of the math.sqrt() call. Sure, a few times you can afford, but 100’s or 1000’s of calls and you’re wasting time. So, a cheat is to simply calculate squaredLength and compare that to ‘snapDist * snapDist’.

  1. Why 100 times? Just to mix it up a bit more. math.random() may return the same value on subsequent calls, so I call it many more times than I might need for a perfect unique random generator to be sure I get enough ‘new and non-repeating’ values during the swizzle.
  2. Don’t be sorry questions are good.
    Note: The sample I’ll release later is by no means a ‘tutorial grade’ example, but I tried to put a few more comments in the code. [import]uid: 110228 topic_id: 32243 reply_id: 130864[/import]

I uploaded updated source code and binaries.
https://github.com/roaminggamer/
[import]uid: 110228 topic_id: 32243 reply_id: 130872[/import]