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.
Hi all,
I'm creating a deli mini-game and trying to change the way a drag looks after it has been dropped. For example, I want a draggable image of a closed burger bun to change to an open bun image once it has been dropped onto the plate. I tried using
def ingredient_dragged(drags, drop):
#drop will be the drag object it was dropped onto.
#id not placed on a droppable, do nothing
if not drop:
return
#currently snaps whenever the drag and drop overlap.
if drop: #id the drag was successfully dropped onto something
drags[0].snap(drop.x,drop.y) #drop onto coordinates of droppable
drags[0].draggable = False #makes it so image is no longer draggable
drags[0].set_child("images/deli/bar/sauces.png") #change child to a different image
return
Here is the traceback:
File "game/My Screens/deli/make.rpy", line 162, in ingredient_dragged
drags[0].set_child("images/deli/bar/sauces.png") #change child to a different image. not working?
AttributeError: 'str' object has no attribute 'per_interact'
Last edited by jepp21 on Mon Mar 27, 2023 3:26 pm, edited 1 time in total.
Note: You are setting the child for the drag. It may not work because the drag image would be replaced with the previous one (supplied as drag child in screen language) with the next interaction.
Note: You are setting the child for the drag. It may not work because the drag image would be replaced with the previous one (supplied as drag child in screen language) with the next interaction.
Thanks for the reply. I tried your code out and the error disappeared, but the image never changed. I also tried drags[0].set_child(Transform("images/deli/bar/sauces.png"))
and drags[0].set_child(Image("images/deli/bar/sauces.png"))
and again, the error disappeared but the image never changed. I also tried using a file that doesn't exist and nothing happened. Maybe ren'py isnt executing this code for some reason? Or is it what you're saying about the next iteration?
Note: You are setting the child for the drag. It may not work because the drag image would be replaced with the previous one (supplied as drag child in screen language) with the next interaction.
Thanks for the reply. I tried your code out and the error disappeared, but the image never changed. I also tried drags[0].set_child(Transform("images/deli/bar/sauces.png"))
and drags[0].set_child(Image("images/deli/bar/sauces.png"))
and again, the error disappeared but the image never changed. I also tried using a file that doesn't exist and nothing happened. Maybe ren'py isnt executing this code for some reason? Or is it what you're saying about the next iteration?
I just did some testing that may be useful. I changed the drag from:
and it worked this way. So it seems like after the function ingredient_dragged is called, it goes back to the screen with the drags and then child "images/deli/bar/bread.png" changes the image back. I still want the drag to be an image instead of text but at least we know where the issue lies
jepp21 wrote: ↑Wed Mar 08, 2023 2:34 pm
So it seems like after the function ingredient_dragged is called, it goes back to the screen with the drags and then child "images/deli/bar/bread.png" changes the image back. I still want the drag to be an image instead of text but at least we know where the issue lies
EDIT: I missed that you didn't end the interaction. Ignore this post.
Right. It happens because you set the drag child as a static image. That is what I was trying to say in the "Note".
def ingredient_dragged(drags, drop):
#drop will be the drag object it was dropped onto.
#id not placed on a droppable, do nothing
if not drop:
return
#currently snaps whenever the drag and drop overlap.
if drop: #id the drag was successfully dropped onto something
drags[0].snap(drop.x,drop.y) #drop onto coordinates of droppable
drags[0].draggable = False #makes it so image is no longer draggable
store.drag_child = "images/deli/bar/sauces.png"
return True
PS: You can use dictionaries with drag_name as keywords, or class variables, etc., instead of global variables to have cleaner code.
Check this example. It can be a bit complicated due to 2D array but still useful.
Last edited by _ticlock_ on Tue Mar 14, 2023 2:03 pm, edited 4 times in total.
I think this might be a bug. But it's possible that I don't understand what the intention of this function actually is. Also the reason it raises an exception when you provide a string (which usually works, since Renpy creates simple Displayables by default), is inside the code:
jepp21 wrote: ↑Wed Mar 08, 2023 2:34 pm
So it seems like after the function ingredient_dragged is called, it goes back to the screen with the drags and then child "images/deli/bar/bread.png" changes the image back. I still want the drag to be an image instead of text but at least we know where the issue lies
Right. It happens because you set the drag child as a static image. That is what I was trying to say in the "Note".
def ingredient_dragged(drags, drop):
#drop will be the drag object it was dropped onto.
#id not placed on a droppable, do nothing
if not drop:
return
#currently snaps whenever the drag and drop overlap.
if drop: #id the drag was successfully dropped onto something
drags[0].snap(drop.x,drop.y) #drop onto coordinates of droppable
drags[0].draggable = False #makes it so image is no longer draggable
drags[0].set_child(renpy.displayable("images/deli/bar/sauces.png"))
store.drag_child = "images/deli/bar/sauces.png"
return
PS: You can use dictionaries with drag_name as keywords, or class variables, etc., instead of global variables to have cleaner code.
Check this example. It can be a bit complicated due to 2D array but still useful.
Okay, this makes sense. However, I tried your example and for some reason it still didn't work. I checked the console and drag_child was not altered to the new file name. I'll check out the tutorial when I have more time, but would you happen to know why your example may not have worked?
jepp21 wrote: ↑Wed Mar 08, 2023 4:39 pm
Okay, this makes sense. However, I tried your example and for some reason it still didn't work. I checked the console and drag_child was not altered to the new file name. I'll check out the tutorial when I have more time, but would you happen to know why your example may not have worked?
I am sorry. I did not pay attention. I missed that you didn't end the interaction.
I agree, I don't see a reason to have per_interact() before self.child = renpy.easy.displayable(d).
Although, I don't think it is related to why the image is not changed. I'll have a look at it a bit later.
jepp21 wrote: ↑Wed Mar 08, 2023 4:39 pm
Okay, this makes sense. However, I tried your example and for some reason it still didn't work.
While I still think set_child() has a bug, the suggestion by @_ticlock_ works if you insert renpy.restart_interaction() after changing the drag variable. And you don't have to use set_child() anymore at all.
if drop: #id the drag was successfully dropped onto something
drags[0].snap(drop.x,drop.y) #drop onto coordinates of droppable
drags[0].draggable = False #makes it so image is no longer draggable
store.drag_child = "images/deli/bar/sauces.png"
renpy.restart_interaction()
return
jepp21 wrote: ↑Wed Mar 08, 2023 4:39 pm
Okay, this makes sense. However, I tried your example and for some reason it still didn't work.
While I still think set_child() has a bug, the suggestion by @_ticlock_ works if you insert renpy.restart_interaction() after changing the drag variable. And you don't have to use set_child() anymore at all.
if drop: #id the drag was successfully dropped onto something
drags[0].snap(drop.x,drop.y) #drop onto coordinates of droppable
drags[0].draggable = False #makes it so image is no longer draggable
store.drag_child = "images/deli/bar/sauces.png"
renpy.restart_interaction()
return
Thanks for the reply. This worked, but it yielded an issue with other parts of my function. The image does change, but now the draggable is set to True again, so the user can move the object again when I want it to remain locked in place.
jepp21 wrote: ↑Wed Mar 08, 2023 5:13 pm
... The image does change, but now the draggable is set to True again, so the user can move the object again when I want it to remain locked in place.
So, you've made a drag's child be a variable - go farther, make all the drag's properties be variables...))
jepp21 wrote: ↑Wed Mar 08, 2023 5:13 pm
... The image does change, but now the draggable is set to True again, so the user can move the object again when I want it to remain locked in place.
So, you've made a drag's child be a variable - go farther, make all the drag's properties be variables...))
def ingredient_dragged(drags, drop):
global drag_child, ingredients_draggable
#drop will be the drag object it was dropped onto
#if not placed on droppable, snap back to original pos
if not drop:
return
#currently snaps whenever the drag and drop overlap. can mess w/ if statement to change this
if drop: #if the drag was successfully dropped onto something
drags[0].snap(drop.x,drop.y) #drop onto coordinates of droppable
store.drag_child = "images/deli/bar/sauces.png" #change image
store.ingredients_draggable["[drags[0]]"] = False #sets item from dictionary draggable to false
renpy.restart_interaction() #changes child but restart interaction undoes the previous lines
return
jepp21 wrote: ↑Wed Mar 08, 2023 11:47 pm
The image is again changing as expected but I can still drag it. The console says that the key value is still set to True
If you use renpy.restart_interaction() you need to use variables for all properties that you want to change. Something like this:
def ingredient_dragged(drags, drop):
#drop will be the drag object it was dropped onto
#if not placed on droppable, snap back to original pos
if not drop:
return
#currently snaps whenever the drag and drop overlap. can mess w/ if statement to change this
if drop: #if the drag was successfully dropped onto something
drags[0].snap(drop.x,drop.y) #drop onto coordinates of droppable
store.drag_dict["child"] = "images/deli/bar/sauces.png"
store.drag_dict["draggable"] = False
store.drag_dict["x"] = drags[0].x
store.drag_dict["y"] = drags[0].y
renpy.restart_interaction() #changes child but restart interaction undoes the previous lines
return
Last edited by _ticlock_ on Thu Mar 09, 2023 10:56 am, edited 1 time in total.
jepp21 wrote: ↑Wed Mar 08, 2023 11:47 pm
The image is again changing as expected but I can still drag it. The console says that the key value is still set to True
If you use renpy.restart_interaction() you need to use variables for all properties that you want to change. Something like this:
def ingredient_dragged(drags, drop):
global drag_child, ingredients_draggable
#drop will be the drag object it was dropped onto
#if not placed on droppable, snap back to original pos
if not drop:
return
#currently snaps whenever the drag and drop overlap. can mess w/ if statement to change this
if drop: #if the drag was successfully dropped onto something
drags[0].snap(drop.x,drop.y) #drop onto coordinates of droppable
store.drag_dict["child"] = "images/deli/bar/sauces.png"
store.drag_dict["draggable"] = False
store.drag_dict["x"] = drags[0].x
store.drag_dict["y"] = drags[0].y
renpy.restart_interaction() #changes child but restart interaction undoes the previous lines
return
Thanks! This worked as expected. I've been wondering how to go about adding a few other drags. I'm hoping I won't have to create new dictionaries and ingredient_dragged functions for every individual drag.