Tracking an imagebutton location within a screen

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
Mhyrria
Newbie
Posts: 10
Joined: Wed Jan 24, 2018 8:55 am
Contact:

Tracking an imagebutton location within a screen

#1 Post by Mhyrria »

I am in a bit of bind here. First off, I am noob in Python and Renpy in general but I have spent a great deal reading, testing and learning from this forum. I spent hours searching for this but came up empty. I don't even know if it is possible. Here is what I am trying to do.

I created a screen with some sort of map, this map will change depending if there is a "hidden" imagebutton from another screen. Example, if an imagebutton from another screen is placed in the northeast part of the gameplay area, the tracking screen should use the northeast map image. And should happen as soon as the screen with an imagebutton is shown, pseudo-realtime as it were. Here's the code:

Code: Select all

class MapDirection:
        def __init__(self,nw,ne,sw,se):
            self.nw = nw
            self.ne = ne
            self.sw = sw
            self.se = se

screen test_frame: #the screen that will track the image button
    frame:
        xalign 1.0
        yalign 0
        background None    
        vbox:
            showif MapDirection.nw == True:
                add "images/DirectionEyeNW.png"
            elif MapDirection.ne == True:
                add "images/DirectionEyeNE.png"
            elif MapDirection.sw == True:
                add "images/DirectionEyeSW.png"
            elif MapDirection.se == True:
                add "images/DirectionEyeSE.png"
            else: 
                add "images/DirectionEye.png"

screen test_button(direction):
    if direction == 1:
    	$ MapDirection.nw = True
    imagebutton auto "images/button_%s.png" action Jump("testnone") xpos 5 ypos 5 focus_mask True

screen test_button(direction):
    if direction == 2:
    	$ MapDirection.ne = True
    imagebutton auto "images/button_%s.png" action Jump("testnone") xpos 10 ypos 10 focus_mask True


label start:
"Frame"
show screen testframe
"Button1"
show screen test_button(1)
"Button2"
show screen test_button2(2)

Everything kinda works. The problem is that the test_frame will display both the NE and NW images BEFORE the buttons even appear. At the very start of the game where the buttons havent been called yet, it seems that the screen test_frame already recognizes the variables and adjust as so.

Is this a limitation in Renpy itself? I'm guessing Renpy executes all screen code and just waits for it to be shown. Is there anyway to make this happen?

kivik
Miko-Class Veteran
Posts: 786
Joined: Fri Jun 24, 2016 5:58 pm
Contact:

Re: Tracking an imagebutton location within a screen

#2 Post by kivik »

Ok a few things here:

1 - you're putting python code that executes logic inside a screen, that's bad. Renpy does screen prediction, and therefore any python code inside will get run before the screen is displayed, namely this block:

Code: Select all

    if direction == 1:
    	$ MapDirection.nw = True
2 - You don't have resets in your code - i.e. you never set any of the directions to False, so I think they'll eventually all be True. Since your test_frame uses a mutually exclusive condition, I assume you only want 1 direction to be opened at any one time, then whatever you're doing with true or false isn't going to work very well.

3 - You have two screens with the same name, the whole point of passing a parameter to a screen (direction) is that you can use one screen that does multiple things :)

4 - I don't know if this is an issue, but I'm very confused by your use of the MapDirection class. Instead of creating an instance of it, you are calling it as though as static class... I don't think that's how it works, but then I'm not that familiar with Python so maybe I'm missing something here.

5 - just noticed you're showing screen testframe but your declared it as test_frame


Can you walk back a step and tell us what your map is supposed to do, forgetting the code logic for a moment? I think there's a much much better way to do what you're trying to do.

A few suggestions:
- just pass the direction as a string into the test_frame as a parameter:

Code: Select all

screen test_frame(direction):

...

call screen test_frame("nw") # passes north west into the frame
call screen test_frame("se") # passes south east into the frame

Then check the direction in your test_frame screen and show the right image based on the direction. In fact, due to your image naming conventions, you don't even need a condition statement because you can do this:
[code]
add "images/DirectionEye%s.png" % direction.upper()
It basically inserts the uppercase version of the direction variable into the image you add.

- put the imagebutton in the test_frame itself, and use a condition to check which xpos ypos to use:

Code: Select all

    imagebutton auto "images/button_%s.png":
        action Jump("testnone_%s" % direction)
        focus_mask_True
        if direction == "nw":
            xpos 5 ypos 5
        elif direction == "ne":
            xpos 10 ypos 10
        # etc etc

If however your directions aren't mutually exclusive, then maybe you do want an object to store that information with 4 booleans. If that's what you want (your code doesn't suggest it does, but at the same time it's weird to have a map where you can't go more than one direction), let us know and we'll give further advise.

User avatar
Mhyrria
Newbie
Posts: 10
Joined: Wed Jan 24, 2018 8:55 am
Contact:

Re: Tracking an imagebutton location within a screen

#3 Post by Mhyrria »

I'm really sorry for the confusing post. A few things to clear up, the test_buttons are named test_button1 and 2. And testframe is test_frame in the script. I was manually copying things from the script to here. Why? I cut a chuck of code since i can't mass comment code with Renpy/Python and I dont want to overwrite it. In hindsight, I should've just pasted said chunk of code to empty notepad. Stupid me. Anyway...

To better visualize what I am trying to do, think of the "map" as some sort of radar. The NE corner lights up when there's an imagebutton on the NE part of the main screen. And to the other directional corners as well. So if the game proceeds to a scene where there are 1 or more imagebuttons, 1 or more corners of the map light up realtime-ish. All four directional corners can be triggered one or multiple at a time.

The boolean flags turn to false upon clicking the imagebutton, which is what testnone does. Signalling to the player that that corner of the screen no longer has a hidden imagebutton.

I hope I am much clearer this time around.
1 - you're putting python code that executes logic inside a screen, that's bad. Renpy does screen prediction, and therefore any python code inside will get run before the screen is displayed, namely this block:
Regarding this one, so this is the reason why my map lights up despite the imagebuttons not being displayed yet. Is there any way to get around this? Or perhaps a better way to code what I'm trying to achieve?

User avatar
Remix
Eileen-Class Veteran
Posts: 1628
Joined: Tue May 30, 2017 6:10 am
Completed: None... yet (as I'm still looking for an artist)
Projects: An un-named anime based trainer game
Contact:

Re: Tracking an imagebutton location within a screen

#4 Post by Remix »

Mhyrria wrote: Fri Jun 08, 2018 10:58 amRegarding this one, so this is the reason why my map lights up despite the imagebuttons not being displayed yet. Is there any way to get around this? Or perhaps a better way to code what I'm trying to achieve?
Generally, within a screen, only alter external variables through use of Actions on buttons or other events. Ren'Py has a host of default Actions to cover most needs.

From a programming perspective I would suggest stepping back and thinking about code re-usability :

Are you going to be using the same process in multiple places?
How much of the associated display can be governed by variables (in most cases this is "All of it")
Basically, for example, if you have a background, some clickables and four direction buttons, can you pass all of that to a screen that handles it all cleverly?

Some pseudo code:

Code: Select all

screen spooky_diner_screen():
    add "spooky_background[direction].png"
    ... some clickables maybe
    
screen event_control( info={} ):
    fixed:
        if 'use' in info:
            $ renpy.use_screen( info['use'] ) # use screen inside this one
        if 'nw' in info:
            imagebutton:
                anchor (0.0,0.0)
                pos (0.0, 0.0)
                action [ SetVariable( 'direction', "NW" ) ] + info['nw']
        # etc for others

label spooky_diner:
    python:
        event_info = {
            'use' : 'spooky_diner_screen',
            'ne' : [ Jump('spooky_diner_lavs') ],
            'sw' : [ Jump('spooky_diner_kitchen') ],
        }
    show screen event_control( event_info )
    "..."
That way we can reuse screen event_control just by passing different dictionaries to it
Frameworks & Scriptlets:

kivik
Miko-Class Veteran
Posts: 786
Joined: Fri Jun 24, 2016 5:58 pm
Contact:

Re: Tracking an imagebutton location within a screen

#5 Post by kivik »

All four directional corners can be triggered one or multiple at a time.
Then I think my last comment about your buttons currently being mutually exclusive applies. This block of code:

Code: Select all

            showif MapDirection.nw == True:
                add "images/DirectionEyeNW.png"
            elif MapDirection.ne == True:
                add "images/DirectionEyeNE.png"
            elif MapDirection.sw == True:
                add "images/DirectionEyeSW.png"
            elif MapDirection.se == True:
                add "images/DirectionEyeSE.png"
            else: 
                add "images/DirectionEye.png"
If nw is True, it'll only show NW, and nothing else.

Here's how I'd do your code based on my limited understanding:

Code: Select all

init python:
    class MapDirection:
        def __init__(self, nw=False, ne=False, sw=False, se=False):
            self.nw = nw
            self.ne = ne
            self.sw = sw
            self.se = se

        def any_dir(self):
            return True in [self.nw, self.ne, self.sw, self.se]

screen test_frame(direction): #the screen that will track the image button
    frame:
        xalign 1.0
        yalign 0
        background None
        vbox:
            if direction.any_dir():
                showif direction.nw == True:
                    add "images/DirectionEyeneNW.png"
                showif direction.ne == True:
                    add "images/DirectionEyeneNE.png"
                showif direction.sw == True:
                    add "images/DirectionEyeneSW.png"
                showif direction.se == True:
                    add "images/DirectionEyeneSE.png"
            else:
                add "images/DirectionEye.png"

screen test_buttons(direction):
    if direction.nw:
        imagebutton auto "images/button_%s.png" action Jump("testnone_nw") xpos 5 ypos 5 focus_mask True
    if direction.ne:
        imagebutton auto "images/button_%s.png" action Jump("testnone_ne") xpos 10 ypos 10 focus_mask True
    if direction.sw:
        imagebutton auto "images/button_%s.png" action Jump("testnone_sw") xpos 5 ypos 10 focus_mask True
    if direction.se:
        imagebutton auto "images/button_%s.png" action Jump("testnone_se") xpos 10 ypos 5 focus_mask True

default my_direction = MapDirection()


label start:
    $ my_direction.nw = True
    show screen test_frame(my_direction)
    show screen test_buttons(my_direction)
    "showing buttons"
Note the difference with my vbox: block, there's a statement that checks whether any of the directions are True first, if so show all the relevant images, otherwise show the other image.

As for the test_button screen, rather than having to write code to show 4 different directions individually and having to call each one of them, if your MapDirection object is meant to hold that information, why not just use it directly?

I think that's what you're trying to do, without screenshots and better description it's the best of my understanding.

User avatar
Mhyrria
Newbie
Posts: 10
Joined: Wed Jan 24, 2018 8:55 am
Contact:

Re: Tracking an imagebutton location within a screen

#6 Post by Mhyrria »

Thanks for these guys or gals. I now have a rough idea to simplify and optimize the code. Will post here once I get it done in case someone needs to do the same.

EDIT:

So, changing variables in screen block itself, not in actions, really doesnt go well. I really tried to do it but it gave more problems than it solves. This is the code I came up with. I think it is more robust than the last one. Thanks once again kivik and Remix for the tips and advice.

Code: Select all

default counter = []
default maxhp = 100
default currenthp = 80

init python:
    def checker_func(indx1, indx2, key): #simple function to avoid retyping a lot
        counter[indx1][indx2] = 1
        event_info[key] -= 1
        
screen spooky_diner_screen: #this is basically the map and ui parts, added a bar
    fixed:
        xpos 1775
        ypos 5
        add "images/DirectionEye.png" ypos 40
        bar value currenthp range maxhp xoffset -70 xmaximum 200

screen imgbut1:
    imagebutton:
    	auto "images/DirectionEyeRed_%s.png" 
        focus_mask True
        anchor (0,0)
        pos (0.5, 0.0)
        action [ ui.callsinnewcontext('testnone') ] 
   
    imagebutton:
        auto "images/DirectionEyeRed_%s.png" 
        focus_mask True
        anchor (0,0)
        pos (0.0, 0.5)
        action [ ui.callsinnewcontext('testnone1') ]

screen event_control( info={} ): #this is the main screen for tracking, just set the position and it's good to go
    fixed:
        if 'use' in info:
            $ renpy.use_screen( info['use'] )                
        if 'nw' != 0:
            showif event_info['nw'] > 0:
                add "images/DirectionHighlight.png" pos (0.93,0.05)
	#can add more quadrants and directions

label start:
    
    show screen spooky_diner_screen
    $ counter.append(["but1",0])
    $ counter.append(["but2",0])     
    python:
        event_info = { #the only thing you need to change, increase values if more imgbuttons are on a quadrant
            'use' : 'spooky_diner_screen',
            'nw' : 2,
            'ne' : 1,
            'se' : 0,
            'sw' : 0,
        }
    show screen event_control( event_info )
    show screen imgbut1
    "end"
    return
    
label testnone:
        if counter[0][1]== 0:
            $ checker_func(0,1,'nw')
        "Just a rock."
        return    

 label testnone1:
        if counter[1][1]== 0:
            $ checker_func(1,1,'nw')
        "There's nothing here."
        return    

Post Reply

Who is online

Users browsing this forum: Bing [Bot], Dark79, Google [Bot]