[SOLVED] How to display screen elements with nested for loop exactly once?

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
Syrale
Regular
Posts: 101
Joined: Sun Oct 25, 2015 10:28 am
Completed: Robot Daycare, Deep Sea Valentine, Locke(d)
Projects: Artificial Selection, Artificial Fashionista, rei_carnation
Github: kigyo
itch: kigyo
Discord: kigyodev
Contact:

[SOLVED] How to display screen elements with nested for loop exactly once?

#1 Post by Syrale »

So I'm currently working on a flowchart screen. To save myself the effort of defining each node's position manually, I tried defining only the first position, and made the screen figure out the rest by telling it how the nodes are connected:

Code: Select all

screen flowchart():
    tag menu
    use game_menu(_("Flowchart")):
        default nodes = [("start",(2,1))]
        for node in nodes:
            $ node_name = node[0]
            $ xp = node[1][0]*72*2
            $ yp = node[1][1]*72*2
            fixed xpos xp ypos yp:
                textbutton node_name action NullAction() tooltip node_name
                $ tooltip = GetTooltip()
                if tooltip == node_name:
                    frame yoffset 50:
                        label node_name
            for connection in flowchart[node_name]["connections"]:
                if connection[0] == "d":
                    null xsize 72 ysize 72 xpos xp ypos yp+72
                    $nodes.append((connection[1], (node[1][0],node[1][1]+1)))
                elif connection[0] == "u":
                    null xsize 72 ysize 72 xpos xp ypos yp-72
                    $nodes.append((connection[1], (node[1][0],node[1][1]-1)))
                elif connection[0] == "r":
                    null xsize 72 ysize 72 xpos xp+72 ypos yp
                    $nodes.append((connection[1], (node[1][0]+1,node[1][1])))
                else:
                    null xsize 72 ysize 72 xpos xp-72 ypos yp
                    $nodes.append((connection[1], (node[1][0]-1,node[1][1])))
                    
define flowchart = {
    "start": {"label":"start", "connections":[["r","ch1"]]},
    "ch1": {"label":"ch1", "connections":[["r","ch2"]]},
    "ch2": {"label":"ch2", "connections":[["r","ch3"]]},
    "ch3": {"label":"ch3", "connections":[["d","ch4"]]},
    "ch4": {"label":"ch4", "connections":[["r","ch5A"],["d","ch5B"]]},
    "ch5A": {"label":"ch5A", "connections":[["d","ch6A"],["r","ch6C"]]},
    "ch5B": {"label":"ch5B", "connections":[["l","ch6B"]]},
    "ch6A": {"label":"ch6A", "connections":[]},
    "ch6B": {"label":"ch6B", "connections":[]},
    "ch6C": {"label":"ch6C", "connections":[]},
    }
This displays perfectly when the screen first shows up, but as soon as you hover over one of the nodes, it does the for loop over and over again and displays hundreds of copies of the same button, turning the screen into a laggy mess...

Is there a way to make it not do that, aka display every button exactly once?
Or should I try a different approach entirely?
Last edited by Syrale on Sun Jun 12, 2022 2:57 pm, edited 1 time in total.
ImageArtificial Selection (ongoing) |ImageDeep Sea Valentine | ImageRobot Daycare (NaNo19)
| ImageArtificial Fashionista (NaNo24)

My Developer Tools: Ren'Py Minimap/Location System | Ren'Py Word Counter+

User avatar
zmook
Veteran
Posts: 421
Joined: Wed Aug 26, 2020 6:44 pm
Contact:

Re: How to display screen elements with nested for loop exactly once?

#2 Post by zmook »

The important thing to recognize is that all code in the screen will be run over and over again, so if there's data set-up you need to do you need to wrap in in some structure that makes sure it's done only once.

The specific lines that are screwing up for you are the '$node.append()'s, because they keep making the node list longer every time they're run, regardless of whether the node is already in the list or not.

I think there are sort of four ways to do this, in order of preference:

1. create the data structure ahead of time. I'm not sure I see any reason you couldn't build the 'nodes' list at the same time you define 'flowchart'. Arguably you'd prefer to keep the specific positioning stuff inside the screen, but you could have an abstract node list passed into the screen, iterate over it, and do the calculations to turn relative positions into pixels on the spot.

2. Create the node list by initializing a python object in the screen. Something like this:

Code: Select all

screen flowchart():
    tag menu
    use game_menu(_("Flowchart")):
        default flowchartController = FlowchartController([("start", (2,1))])
        for node in flowchartController.nodes:
3. Create the node list in an "on show" event handler:

Code: Select all

def create_flowchart_nodes(starting_node):
     # fill in/update a global variable 'nodes'

screen flowchart():
    tag menu
    use game_menu(_("Flowchart")):
        on "show" action Function(create_nodes, ("start", (2,1)))
4. Be more careful with your coding so that you don't append nodes if they already exist. This ought to work, but it's trickiest and hardest to debug if you get it wrong. Something like:

Code: Select all

           for connection in flowchart[node_name]["connections"]:
                # check if the connection is already there before trying to add it again
                if not connection[1] in nodes:    # this probably won't work as is, but you can maybe figure it out
                    if connection[0] == "d":
colin r
➔ if you're an artist and need a bit of help coding your game, feel free to send me a PM

User avatar
Syrale
Regular
Posts: 101
Joined: Sun Oct 25, 2015 10:28 am
Completed: Robot Daycare, Deep Sea Valentine, Locke(d)
Projects: Artificial Selection, Artificial Fashionista, rei_carnation
Github: kigyo
itch: kigyo
Discord: kigyodev
Contact:

Re: How to display screen elements with nested for loop exactly once?

#3 Post by Syrale »

zmook wrote: Sun Jun 12, 2022 2:33 pm 1. create the data structure ahead of time. I'm not sure I see any reason you couldn't build the 'nodes' list at the same time you define 'flowchart'.
I think I love screen language a little too much, because this didn't even occur to me... But thank you, this worked like a charm!
ImageArtificial Selection (ongoing) |ImageDeep Sea Valentine | ImageRobot Daycare (NaNo19)
| ImageArtificial Fashionista (NaNo24)

My Developer Tools: Ren'Py Minimap/Location System | Ren'Py Word Counter+

Post Reply

Who is online

Users browsing this forum: No registered users