[SOLVED] Drag and drop puzzle with multiple pieces

Discuss how to use the Ren'Py engine to create visual novels and story-based games. New releases are announced in this section.
Forum rules
This is the right place for Ren'Py help. Please ask one question per thread, use a descriptive subject like 'NotFound error in option.rpy' , and include all the relevant information - especially any relevant code and traceback messages. Use the code tag to format scripts.
Post Reply
Message
Author
User avatar
Kinmoku
Miko-Class Veteran
Posts: 591
Joined: Mon Aug 11, 2014 9:39 am
Completed: One Night Stand
Projects: VIDEOVERSE
Tumblr: gamesbykinmoku
itch: kinmoku
Location: Germany
Contact:

[SOLVED] Drag and drop puzzle with multiple pieces

#1 Post by Kinmoku »

Hi all,

I have a drag and drop puzzle I want to add to my game with pieces of torn paper. There are 15 odd-shaped pieces that need to fit into a square. I was thinking I would make a draggroup for each and have it register when each piece is in position.

What I have so far works but only for each piece. Let me show you my code:

In script.rpy:

Code: Select all

init python:
    def piece_dragged(drags, drop):
        if not drop:
            return
        
       # if drop:
            
        store.piece = drags[0].drag_name
        store.loc = drop.drag_name
        
        return True
        
label start:
label papertest:

    call screen drag_pieces
    
    if loc == "loc1":
        mil "I got it."
        
        $ paperpieces += 1.0
    
    elif loc == "loc2":
        mil "Yes!"
        
        $ paperpieces += 1.0
        
    ## etc for 15 pieces
        
    if paperpieces == 15.0:
        mil "That's it."
        
    else:
        jump papertest
In screens:

Code: Select all

screen drag_pieces():
    add "images/present/fortune/square.jpg"
    
    draggroup:
        # piece
        drag:
            drag_name "piece1"
            droppable False
            child "images/present/fortune/piece1.png"
            dragged piece_dragged
            xpos 0.1 ypos 0.7
        # bg
        drag:
            drag_name "loc1"
            draggable False
            child Solid("#0000", xysize=(605, 334))
            xpos 342 ypos 746
            
    draggroup:
        # piece 2
        drag:
            droppable False
            child "images/present/fortune/piece2.png"
            dragged piece_dragged
            xpos 0.9 ypos 0.7
        # bg
        drag:
            drag_name "loc2"
            draggable False
            child Solid("#0000", xysize=(486, 415))
            xpos 547 ypos 502
            
          ## etc for 15 pieces total
So the dialogue plays when a piece is in *rough* position. That's fine, but the code I have resets the screen once it calls "call screen drag_pieces" again. I figure what I need to do is only have it return once all 15 pieces are in position...but I don't know how to do that! I'm also concerned the player won't be able to save the game during the puzzle. (FYI I am not bothered if I cannot add dialogue, I am only using it as a test right now.)

Can anyone help?
Last edited by Kinmoku on Thu Mar 26, 2020 12:49 pm, edited 1 time in total.

User avatar
Alex
Lemma-Class Veteran
Posts: 3094
Joined: Fri Dec 11, 2009 5:25 pm
Contact:

Re: Drag and drop puzzle with multiple pieces

#2 Post by Alex »


User avatar
Kinmoku
Miko-Class Veteran
Posts: 591
Joined: Mon Aug 11, 2014 9:39 am
Completed: One Night Stand
Projects: VIDEOVERSE
Tumblr: gamesbykinmoku
itch: kinmoku
Location: Germany
Contact:

Re: Drag and drop puzzle with multiple pieces

#3 Post by Kinmoku »

Alex wrote: Fri Mar 20, 2020 4:42 pm This might help - viewtopic.php?f=51&t=16151#p294340
Thanks Alex! This is a great resource. I've tried to back-track to the basics of the jigsaw puzzle. Here's my new code so far:

Script.rpy:

Code: Select all

        
init python:
    def piece_dragged(drags, drop):
        if not drop:
            store.piecelist[(int(drags[0].drag_name[0]) * 10 + int(drags[0].drag_name[1]))][0] = drags[0].x
            store.piecelist[(int(drags[0].drag_name[0]) * 10 + int(drags[0].drag_name[1]))][1] = drags[0].y
            return
       
        store.movedpiece = int(drags[0].drag_name[0]) * 10 + int(drags[0].drag_name[1])
        store.movedplace = [int(drop.drag_name[0]), int(drop.drag_name[1])]
            
        return True

label start:
    
    python:
        coorlistx = [10, 130, 250] #370]
        coorlisty = [10, 217, 424, 600]
        piecelist = [[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]]
        for i in range(12):
            x = renpy.random.randint(0, 59) + 621
            y = renpy.random.randint(0, 480)
            piecelist[i] = [x,y]
        movedpiece = 0
        movedplace = [0, 0]
    
    jump puzzle
    
label puzzle:
    call screen drag_pieces
    if ([coorlistx[movedplace[0]], coorlisty[movedplace[1]]] in piecelist):
        python:
            t1 = piecelist[movedpiece]
            t2 = piecelist.index([coorlistx[movedplace[0]], coorlisty[movedplace[1]]])
            piecelist[movedpiece] = [coorlistx[movedplace[0]],coorlisty[movedplace[1]]]
            piecelist[t2] = t1
    else:
        $ piecelist[movedpiece] = [coorlistx[movedplace[0]],coorlisty[movedplace[1]]]
    if piecelist == [[coorlistx[0],coorlisty[0]],
                        [coorlistx[1],coorlisty[0]],
                        [coorlistx[2],coorlisty[0]],
                        #[coorlistx[3],coorlisty[0]],
                        [coorlistx[0],coorlisty[1]],
                        [coorlistx[1],coorlisty[1]],
                        [coorlistx[2],coorlisty[1]],
                        #[coorlistx[3],coorlisty[1]],
                        [coorlistx[0],coorlisty[2]],
                        [coorlistx[1],coorlisty[2]],
                        [coorlistx[2],coorlisty[2]],
                        #[coorlistx[3],coorlisty[2]]]:
                        [coorlistx[0],coorlisty[3]],
                        [coorlistx[1],coorlisty[3]],
                        [coorlistx[2],coorlisty[3]]]:
        return
    jump puzzle
In screens.rpy:

Code: Select all

screen drag_pieces():
    add "images/present/fortune/square.jpg"
    
    draggroup:
        # piece 0
        drag:
            drag_name "00 piece"
            droppable False
            child "images/present/fortune/piece0.png"
            dragged piece_dragged
            xpos piecelist[0][0] ypos piecelist[0][1]
        # bg
        drag:
            drag_name "00"
            draggable False
            child Solid("#0000", xysize=(605, 334))
            xpos coorlistx[0] ypos coorlisty[0]
            
        # piece 1
        drag:
            drag_name "01 piece"
            droppable False
            child "images/present/fortune/piece1.png"
            dragged piece_dragged
            #xpos 0.9 ypos 0.7
            xpos piecelist[1][0] ypos piecelist[1][1]
        # bg
        drag:
            drag_name "01"
            draggable False
            child Solid("#0000", xysize=(486, 415))
            #xpos 547 ypos 502
            xpos coorlistx[0] ypos coorlisty[1]
            
        # piece 2
        drag:
            drag_name "02 piece"
            droppable False
            child "images/present/fortune/piece2.png"
            dragged piece_dragged
            xpos piecelist[2][0] ypos piecelist[2][1]
        # bg
        drag:
            drag_name "02"
            draggable False
            child Solid("#0000", xysize=(486, 415))
            xpos coorlistx[0] ypos coorlisty[2]
            
        # piece 3
        drag:
            drag_name "03 piece"
            droppable False
            child "images/present/fortune/piece3.png"
            dragged piece_dragged
            xpos piecelist[3][0] ypos piecelist[3][1]
        # bg
        drag:
            drag_name "03"
            draggable False
            child Solid("#0000", xysize=(486, 415))
            xpos coorlistx[0] ypos coorlisty[3]
            
        # piece 4
        drag:
            drag_name "04 piece"
            droppable False
            child "images/present/fortune/piece4.png"
            dragged piece_dragged
            xpos piecelist[4][0] ypos piecelist[4][1]
        # bg
        drag:
            drag_name "10"
            draggable False
            child Solid("#0000", xysize=(486, 415))
            xpos coorlistx[1] ypos coorlisty[0]
            
        # piece 5
        drag:
            drag_name "05 piece"
            droppable False
            child "images/present/fortune/piece5.png"
            dragged piece_dragged
            xpos piecelist[5][0] ypos piecelist[5][1]
        # bg
        drag:
            drag_name "11"
            draggable False
            child Solid("#0000", xysize=(486, 415))
            xpos coorlistx[1] ypos coorlisty[1]
            
        # piece 6
        drag:
            drag_name "06 piece"
            droppable False
            child "images/present/fortune/piece6.png"
            dragged piece_dragged
            xpos piecelist[6][0] ypos piecelist[6][1]
        # bg
        drag:
            drag_name "12"
            draggable False
            child Solid("#0000", xysize=(486, 415))
            xpos coorlistx[1] ypos coorlisty[2]
            
        # piece 7
        drag:
            drag_name "07 piece"
            droppable False
            child "images/present/fortune/piece7.png"
            dragged piece_dragged
            xpos piecelist[7][0] ypos piecelist[7][1]
        # bg
        drag:
            drag_name "13"
            draggable False
            child Solid("#0000", xysize=(486, 415))
            xpos coorlistx[1] ypos coorlisty[3]
            
        # piece 8
        drag:
            drag_name "08 piece"
            droppable False
            child "images/present/fortune/piece8.png"
            dragged piece_dragged
            xpos piecelist[8][0] ypos piecelist[8][1]
        # bg
        drag:
            drag_name "20"
            draggable False
            child Solid("#0000", xysize=(486, 415))
            xpos coorlistx[2] ypos coorlisty[0]
            
        # piece 9
        drag:
            drag_name "09 piece"
            droppable False
            child "images/present/fortune/piece9.png"
            dragged piece_dragged
            xpos piecelist[9][0] ypos piecelist[9][1]
        # bg
        drag:
            drag_name "21"
            draggable False
            child Solid("#0000", xysize=(486, 415))
            xpos coorlistx[2] ypos coorlisty[1]
            
        # piece 10
        drag:
            drag_name "10 piece"
            droppable False
            child "images/present/fortune/piece10.png"
            dragged piece_dragged
            xpos piecelist[10][0] ypos piecelist[10][1]
        # bg
        drag:
            drag_name "22"
            draggable False
            child Solid("#0000", xysize=(486, 415))
            xpos coorlistx[2] ypos coorlisty[2]
            
        # piece 11
        drag:
            drag_name "11 piece"
            droppable False
            child "images/present/fortune/piece11.png"
            dragged piece_dragged
            xpos piecelist[11][0] ypos piecelist[11][1]
        # bg
        drag:
            drag_name "23"
            draggable False
            child Solid("#0000", xysize=(486, 415))
            xpos coorlistx[2] ypos coorlisty[3]
I haven't changed the xysize of the background locations yet. Obviously, the co-ordinates for these are not correct either. However, the "code" works... I just need to figure out how to do it for a shape like this:
fortune_torn.jpg
I was planning on using the co-ordinates of the red boxes:
fortune_locs.jpg
There are some overlays, but they'll hopefully snap into position and look neat by the end. Note: There are also some "holes" in the artwork, as pieces of the paper fortune are missing.

With an irregular shape like this, how best to change my code to work?

User avatar
Alex
Lemma-Class Veteran
Posts: 3094
Joined: Fri Dec 11, 2009 5:25 pm
Contact:

Re: Drag and drop puzzle with multiple pieces

#4 Post by Alex »

Hm, you could cut your map on rectangles (like the red ones) to make draggable pieces. And also made a set of droppables - color the lands in all pieces the same color and leave some transparent canvas around. Then place those droppables at the right positions over the same color background. This should look like colored background, but droppables should work when non-transparent part of draggable piece will overlap the non-transparent part of the droppable.

User avatar
Kinmoku
Miko-Class Veteran
Posts: 591
Joined: Mon Aug 11, 2014 9:39 am
Completed: One Night Stand
Projects: VIDEOVERSE
Tumblr: gamesbykinmoku
itch: kinmoku
Location: Germany
Contact:

Re: Drag and drop puzzle with multiple pieces

#5 Post by Kinmoku »

Alex wrote: Mon Mar 23, 2020 4:10 pm Hm, you could cut your map on rectangles (like the red ones) to make draggable pieces. And also made a set of droppables - color the lands in all pieces the same color and leave some transparent canvas around. Then place those droppables at the right positions over the same color background. This should look like colored background, but droppables should work when non-transparent part of draggable piece will overlap the non-transparent part of the droppable.
Ah, okay. I think I understand. I'll try this.

How do I change the co-ordinates so I don't break the code? Putting in the exact co-ordinates stopped it working. I guess I'm still struggling to get my head around how "coorlisty" and "piecelist" work.

User avatar
Alex
Lemma-Class Veteran
Posts: 3094
Joined: Fri Dec 11, 2009 5:25 pm
Contact:

Re: Drag and drop puzzle with multiple pieces

#6 Post by Alex »

Kinmoku wrote: Tue Mar 24, 2020 4:49 am ... How do I change the co-ordinates so I don't break the code? ...
Try something like

Code: Select all

init python:
    
    def piece_dragged(drags, drop):
        
        if not drop:
            return
        
        if drop.drag_name == drags[0].drag_name:
            i = drags[0].drag_name # 'cause we used "ind" as a name of draggable piece
            drags[0].snap(drop.x, drop.y, delay=0.1)
            store.pieces_list[i]["placed"] = True
            store.pieces_list[i]["x_pos"] = drop.x
            store.pieces_list[i]["y_pos"] = drop.y
            drags[0].draggable = False # sets placed piece undraggable
            
            for piece in pieces_list:
                if not piece["placed"]:
                    return
            return True
            
        return
                
        
screen drag_drop_scr():
    key "rollback" action NullAction()
    key "rollforward" action NullAction()
    key "dismiss" action NullAction()
    
    textbutton "Quit" action Jump("drag_quit") align (0.5, 0.05)
    
    draggroup:
        
        drag:
            child "bg"
            xpos field_x_pos ypos field_y_pos
            draggable False
            droppable False
            
        for piece in pieces_list:
            drag:
                drag_name piece["ind"] # use "ind" as the name
                child piece["img"]
                dragged piece_dragged
                draggable can_move
                xpos piece["x_pos"] ypos piece["y_pos"]
                #mouse_drop True # try and see if it behave better
                
                
        for place in places_list:
            drag:
                drag_name place["ind"]
                child place["img"]
                draggable False
                xpos (field_x_pos + place["x_pos"]) ypos (field_y_pos + place["y_pos"])

image bg:
    Solid("#ccc") # change the color to "#fff" to make spots invisible
    size (350, 350)
    
        
# The game starts here.

label start:
    "..."
    call drag_pieces
    "Well done."
    jump start

    return

label drag_pieces:
    $ can_move = False
    $ field_x_pos = 250
    $ field_y_pos = 150
    # list of pieces
    $ pieces_list =[
        {"ind":0, "img":"a_drag.png", "x_pos":0, "y_pos":0, "placed": False},
        {"ind":1, "img":"b_drag.png", "x_pos":0, "y_pos":0, "placed": False},
        {"ind":2, "img":"c_drag.png", "x_pos":0, "y_pos":0, "placed": False},
        {"ind":3, "img":"x_drag.png", "x_pos":0, "y_pos":0, "placed": False},
        ] # "ind" is an index of the item in the list, so we can use it later
    
    # list of corresponding places ("ind" value does matter)
    $ places_list = [
        {"ind":0, "img":"a_drop.png", "x_pos":10, "y_pos":70},
        {"ind":1, "img":"b_drop.png", "x_pos":100, "y_pos":200},
        {"ind":2, "img":"c_drop.png", "x_pos":200, "y_pos":50},
        {"ind":3, "img":"x_drop.png", "x_pos":200, "y_pos":150},
        ]
    
    # sets random coordinates for pieces
    python:
        for piece in pieces_list:
            pieces_list[piece["ind"]]["x_pos"] = renpy.random.randint(10, 100)
            pieces_list[piece["ind"]]["y_pos"] = renpy.random.randint(10, 100)
            
    show screen drag_drop_scr
    $ can_move = True
    "Place images."
    $ can_move = False
    $ renpy.pause(1.0)
    hide screen drag_drop_scr
    return
    
label drag_quit:
    $ can_move = False
    "so long...{w=1.0}{nw}"
    hide screen drag_drop_scr
    return
a_drag.png
a_drag.png (7.42 KiB) Viewed 852 times
a_drop.png
a_drop.png (505 Bytes) Viewed 852 times
b_drag.png
b_drag.png (10.24 KiB) Viewed 852 times
b_drop.png
b_drop.png (560 Bytes) Viewed 852 times
c_drag.png
c_drag.png (12.63 KiB) Viewed 852 times
c_drop.png
c_drop.png (758 Bytes) Viewed 852 times
x_drag.png
x_drag.png (143 Bytes) Viewed 852 times
x_drop.png
x_drop.png (143 Bytes) Viewed 852 times

User avatar
Kinmoku
Miko-Class Veteran
Posts: 591
Joined: Mon Aug 11, 2014 9:39 am
Completed: One Night Stand
Projects: VIDEOVERSE
Tumblr: gamesbykinmoku
itch: kinmoku
Location: Germany
Contact:

Re: Drag and drop puzzle with multiple pieces

#7 Post by Kinmoku »

Oh wow, thank you Alex! I've implemented this with my 15 pieces and it works! :) There were just a couple of hiccups that I managed to fix. 1) The drop images initially display above the drag ones. To fix this, I just changed the order of the drag code in screens.rpy. 2) When I tested a couple of times, some pieces wouldn't register on the first drop. They tended to be smaller pieces (between 100-200px). Then I realised this was because I switched "mouse_drop" on. It works better without it!

Here's my final code in case it can help others:

In screens.rpy:

Code: Select all

screen drag_pieces():
    key "rollback" action NullAction()
    key "rollforward" action NullAction()
    key "dismiss" action NullAction()
    
    #add "images/present/fortune/floor.jpg"
    
    draggroup:
        
        drag:
            child "images/present/fortune/square.jpg"
            xpos field_x_pos ypos field_y_pos
            draggable False
            droppable False
            
        for place in places_list:
            drag:
                drag_name place["ind"]
                child place["img"]
                draggable False
                xpos (field_x_pos + place["x_pos"]) ypos (field_y_pos + place["y_pos"])
            
        for piece in pieces_list:
            drag:
                drag_name piece["ind"] # use "ind" as the name
                child piece["img"]
                dragged piece_dragged
                draggable can_move
                xpos piece["x_pos"] ypos piece["y_pos"]
                #mouse_drop True # try and see if it behave better
In script.rpy:

Code: Select all

init python:
    def piece_dragged(drags, drop):
        if not drop:
            return
        
        if drop.drag_name == drags[0].drag_name:
            i = drags[0].drag_name # 'cause we used "ind" as a name of draggable piece
            drags[0].snap(drop.x, drop.y, delay=0.1)
            store.pieces_list[i]["placed"] = True
            store.pieces_list[i]["x_pos"] = drop.x
            store.pieces_list[i]["y_pos"] = drop.y
            drags[0].draggable = False # sets placed piece undraggable
            
            for piece in pieces_list:
                if not piece["placed"]:
                    return
                    
            return True
            
        return
        
label start:
    call puzzle_pieces
    
    "You won"
    
label puzzle_pieces:

    $ can_move = False
    $ field_x_pos = 0
    $ field_y_pos = 0
    # list of pieces
    $ pieces_list =[
        {"ind":0, "img":"present/fortune/0_drag.png", "x_pos":0, "y_pos":0, "placed": False},
        {"ind":1, "img":"present/fortune/1_drag.png", "x_pos":0, "y_pos":0, "placed": False},
        {"ind":2, "img":"present/fortune/2_drag.png", "x_pos":0, "y_pos":0, "placed": False},
        {"ind":3, "img":"present/fortune/3_drag.png", "x_pos":0, "y_pos":0, "placed": False},
        {"ind":4, "img":"present/fortune/4_drag.png", "x_pos":0, "y_pos":0, "placed": False},
        {"ind":5, "img":"present/fortune/5_drag.png", "x_pos":0, "y_pos":0, "placed": False},
        {"ind":6, "img":"present/fortune/6_drag.png", "x_pos":0, "y_pos":0, "placed": False},
        {"ind":7, "img":"present/fortune/7_drag.png", "x_pos":0, "y_pos":0, "placed": False},
        {"ind":8, "img":"present/fortune/8_drag.png", "x_pos":0, "y_pos":0, "placed": False},
        {"ind":9, "img":"present/fortune/9_drag.png", "x_pos":0, "y_pos":0, "placed": False},
        {"ind":10, "img":"present/fortune/10_drag.png", "x_pos":0, "y_pos":0, "placed": False},
        {"ind":11, "img":"present/fortune/11_drag.png", "x_pos":0, "y_pos":0, "placed": False},
        {"ind":12, "img":"present/fortune/12_drag.png", "x_pos":0, "y_pos":0, "placed": False},
        {"ind":13, "img":"present/fortune/13_drag.png", "x_pos":0, "y_pos":0, "placed": False},
        {"ind":14, "img":"present/fortune/14_drag.png", "x_pos":0, "y_pos":0, "placed": False},
        ] # "ind" is an index of the item in the list, so we can use it later
    
    # list of corresponding places ("ind" value does matter)
    $ places_list = [
        {"ind":0, "img":"present/fortune/0_drop.png", "x_pos":762, "y_pos":746},
        {"ind":1, "img":"present/fortune/1_drop.png", "x_pos":752, "y_pos":0},
        {"ind":2, "img":"present/fortune/2_drop.png", "x_pos":966, "y_pos":502},
        {"ind":3, "img":"present/fortune/3_drop.png", "x_pos":759, "y_pos":571},
        {"ind":4, "img":"present/fortune/4_drop.png", "x_pos":982, "y_pos":336},
        {"ind":5, "img":"present/fortune/5_drop.png", "x_pos":986, "y_pos":199},
        {"ind":6, "img":"present/fortune/6_drop.png", "x_pos":750, "y_pos":143},
        {"ind":7, "img":"present/fortune/7_drop.png", "x_pos":1236, "y_pos":440},
        {"ind":8, "img":"present/fortune/8_drop.png", "x_pos":1112, "y_pos":176},
        {"ind":9, "img":"present/fortune/9_drop.png", "x_pos":1322, "y_pos":578},
        {"ind":10, "img":"present/fortune/10_drop.png", "x_pos":1605, "y_pos":652},
        {"ind":11, "img":"present/fortune/11_drop.png", "x_pos":1466, "y_pos":226},
        {"ind":12, "img":"present/fortune/12_drop.png", "x_pos":1620, "y_pos":0},
        {"ind":13, "img":"present/fortune/13_drop.png", "x_pos":1348, "y_pos":0},
        {"ind":14, "img":"present/fortune/14_drop.png", "x_pos":1222, "y_pos":0},
        ]
    
    # sets random coordinates for pieces
    python:
        for piece in pieces_list:
            pieces_list[piece["ind"]]["x_pos"] = renpy.random.randint(10, 320)
            pieces_list[piece["ind"]]["y_pos"] = renpy.random.randint(10, 720)
            
    show screen drag_pieces
    $ can_move = True
    "Place images."
    $ can_move = False
    $ renpy.pause(1.0)
    hide screen drag_pieces
    return
    
Thanks so much for helping me! ^^/

Post Reply

Who is online

Users browsing this forum: Google [Bot]