Ren'Py Free Roam using UI (Minimap)

Jun 21, 2020
67
86
Edit. Solution (or at least one approach to it):
=========

Ok, so first of all let me clarify: I am new to renpy, but not to python. So I mostly have trouble with the renpy side of the code.

Having said that: what I want is to create a minimap of a house that allows the user to click on a room of that house and display the image of that room (meaning the user traveled there). Now the good news is that I managed to pull that off with a little help of renpy documentation (and mostly python code).
Here's the main code, where I call the minimap, and whatever it returns (when I click on a minimap room it returns the name of a location and) its room image appears.
Python:
$ game_running = True

while game_running:
    python: # this is just validation that gets the room name used in the image url
        location_img_string = location.lower()
        location_img_string = location_img_string.replace("'s", "")
        location_img_string = location_img_string.replace(" ", "_")

    if renpy.has_image(location_url_string, exact=True):
        show expression location_url_string # shows the room image using that validate string
   
    $ location = renpy.call_screen("minimap") # and here I call the screen with the minimap (an image with imagebuttons that each return a string containing the room name)
The problem arises now that I want to create yet another way of moving: a room selector (basically two arrows pointing left and right that change the current room)
Python:
$ location = renpy.call_screen("room_selector") # this obviously doesn't work
I don't really need help with the logic of how to achieve the selector, but instead I need help with how to have both the minimap AND the selector working at the same time (being able to click one or the other and changing rooms), since now, I can't have the two $ location = renpy.call_screen() at the same time, the code stops to get the first location and then shows the selector.

Thanks in advance, any kind of help is appreciated... even a nudge in the right direction.

PS. I'm guessing there should be a better way to read a minimap (or any kind of UI) input that doesn't require for the code to "stop" to get that info. If you know of any better way to do this, please let me know. Thanks!
 
Last edited:

osanaiko

Engaged Member
Modder
Jul 4, 2017
2,547
4,629
One way to approach this is as follows, will need some conceptual realignment in your brain:

1. Move away from the concept of needing a "game loop". Renpy (based on the pygame libraries underneath) handles the interactivity loop for you.

2. Instead, treat it more like a GUI implementation - you have elements that respond to user events.

3. If you look at it like a GUI, you've got the main status (the background room image), and two major UI elements: the minimap and the navigation arrows. In Renpy you would implement these as "screens" - UI elements (or groups of elements) that overlay the background image and provide information / interactivity.

4. You've already got a start on the Minimap screen (although you have not provided the code) but again, you have presumably coded it like a function that returns a value, the location string. Instead, the minimap screen should be a standalone element that influences the global state (room location) by directly changing the relevant global variable, and does not "return" (disappear) after it was clicked.

It's a lot to try to explain, so as a first step I'd strongly recommend you take the time to grab the Renpy v8.1 release package and try the "Tutorial" game example. It's a game, written in renpy, that educates you how to use almost all the functionality available. There's a whole section about Screens. The best part about all this is that you can also just open and read the code examples that make up the tutorial game itself.

1706413000674.png

Once you've been through that, please revisit your implementation attempt and then come back with some more questions (and all the relevant code) if you would like some more advice/help.
 
Last edited:
  • Like
Reactions: The Lewd Gamer
Jun 21, 2020
67
86
Great advice! I'll check out this tutorial and come back if any questions are left. I will leave my minimap code though, so you can see what I've done (it's probably messy, so any tips on the code arrangement are also welcome)...

The following code is on another .rpy file called map_screen.rpy:
Python:
init 1 python:
    class Place(object):
        def __init__(self, x, y, xsize, ysize, name):
            self.x = x
            self.y = y

            self.xsize = xsize
            self.ysize = ysize
            
            self.name = name

    locations = []

    locations.append(Place(...)) # and I append all my locations as instances of Place
    
screen minimap():
    vbox:
        xalign 1.0
        yalign 0.0
        xsize 192*2
        ysize 108*2

        frame:
            xfill True
            style 'minimap_container'

            frame:
                xoffset 15
                yoffset 15
                style 'minimap_background'

                for location in locations: # then I create every room as a button using the instances' parameters 
                    button:
                        xsize location.xsize
                        ysize location.ysize
                        xpos location.x
                        ypos location.y
                        # text location.name size 8 xalign 0.5 yalign 0.5
                        
                        background "#16b102f1"
                        hover_background "#24c010"

                        action Return(location.name)

                frame:
                    style 'minimap_linework'
                    
## -- then there are some irrelevant style definitions
I didn't think this code was important to my specific issue, but now you gave me the idea to share it so you can review it and give feedback (again, I'm mostly concerned about the code arrangement, so if anyone has tips on how to approach this, you're welcome).

In the mean time I'll be checking on this renpy tutorial I somehow missed. If I end up getting a solution I'll update you. Thanks again!
 
Jun 21, 2020
67
86
Ok, I'm back already... just read though all the screens tutorial and I learned a lot (like for example that the main issue with my code is that I "call_screen" instead of showing it, which "stops" the game until a return value is given (like calling a function).

But a question (that I don't know that it has to do with screens) remains: if I don't have a game loop, won't just clicking on any minimap button just "advance" though the game and reach the return in my start?

I'll keep reading to see if I learn how to do what I want, since it's not the typical VN.... and again, any help or nudge in the right direction is appreciated, thanks!

Edit: or maybe I got it all wrong. the problem is actually "returning" something right? that's what you osanaiko meant by saying I should instead "change a global variable" on the button click, right?

Ok, follow up question then: how can I affect a global variable from a screen? since I just learned from the tutorial that you can't do that using python code in screens, I guess there's a renpy way of doing it...

I'll keep my investigation going and will be alert when someone answers. I'll update you on any progress. Thanks again!
 

osanaiko

Engaged Member
Modder
Jul 4, 2017
2,547
4,629
After reading through the Tutorial for Imagemaps / Screens myself, I realized that it still didn't really give enough information, so I spent the afternoon messing around and finally got a working demo (btw, Renpy official docs remain cursed. The info they contain is technically correct, but infuriatingly sparse and confusing compared to almost any other doco I work with in $DAYJOB)

Anyway, here is some code for an example "Minimap and Arrows navigation" implementation. It that works okay, but no guarantees it is great style. Anne'O'Nymous will probably hate it, lol.

Drop the code into the "Tutorial" game folder as it uses some assets for background - it overrides the main menu so launching the tute goes straight into my example.
Code:
# override the "Tutorial" game start label so it runs my code instead
define config.label_overrides = {
    "start": "nav_demo_start"
}


label nav_demo_start:
    scene bg cave
    "Hello, welcome to the nav demo"
    jump nav_hub

label nav_hub:
    call screen nav_map(True) # (using call) show nav_map screen and wait for input, param makes it interactable
    scene expression locations[location]["bg_image"]
    show screen nav_map(False)  # (using show) show nav_map screen but don't wait for input, param makes it non-interactable so players cannot change location mid dialogue : remove this line if you don't want the map showing during the dialogue
    call expression location
    jump nav_hub

label room_a:
    "This is ROOM_A dialogue!"
    "Fascinating..."
    return

label room_b:
    "ROOM_B also has dialogue!"
    "Marvellous..."
    return

label room_c:
    "In ROOM_C: Omae wa mou shindeiru!"
    "NANI!?"
    return


default location = "room_a"

define room_a = {"bg_image": "bg cave",       "move_left" : False,     "move_right" : "room_b"}
define room_b = {"bg_image": "bg washington", "move_left" : "room_a",  "move_right" : "room_c"}
define room_c = {"bg_image": "bg whitehouse", "move_left" : "room_b",  "move_right" : False}

define locations = {
    "room_a": room_a,
    "room_b": room_b,
    "room_c": room_c,
}


init python:

    def Move(direction):
        global location
        global locations
        if direction == "left":
            if locations[location]["move_left"] != False:
                location = locations[location]["move_left"]
        if direction == "right":
            if locations[location]["move_right"] != False:
                location = locations[location]["move_right"]

    def CanMove(direction):
        global location
        global locations
        if direction == "left":
            return locations[location]["move_left"] != False
        if direction == "right":
            return locations[location]["move_right"] != False


screen nav_map(can_interact = False):

    sensitive can_interact

    imagemap:
        auto "nav_map %s"
        hotspot (841, 34, 101, 72)  action [If(location != "room_a", [SetVariable("location", "room_a"), Return()],[NullAction()]) ] selected (location == "room_a")
        hotspot (977, 30, 103, 74)  action [If(location != "room_b", [SetVariable("location", "room_b"), Return()],[NullAction()]) ] selected (location == "room_b")
        hotspot (1113, 32, 103, 76) action [If(location != "room_c", [SetVariable("location", "room_c"), Return()],[NullAction()]) ] selected (location == "room_c")

  imagebutton:
        xpos 50
        yalign 0.5
        idle "arrow left"
        hover "arrow left hover"
        sensitive CanMove("left")
        action [Function(Move, "left"), Return()]

    imagebutton:
        xpos 1130
        yalign 0.5
        idle "arrow right"
        hover "arrow right hover"
        sensitive CanMove("right")
        action [Function(Move, "right"), Return()]
It also needs some images for the arrows and imagemap, so I'll attach those in an archive as well.
 
Last edited:

osanaiko

Engaged Member
Modder
Jul 4, 2017
2,547
4,629
But a question (that I don't know that it has to do with screens) remains: if I don't have a game loop, won't just clicking on any minimap button just "advance" though the game and reach the return in my start?
You do have a "loop" but implement it using the Renpy code, not python.

There is an example in my code above:
1. Game flow reaches the point the user can navigate, jump to the navigation hub label
2. Call an interactive map screen, so it waits for user input
3. the Nav screen sets a global variable when the user selects something.
4. control returns to the renpy line immediately after the call screen.
5. do a renpy call to the label that corresponds to the global variable. I used "call expression" to do this, namign conventions for the win.
6. The location label implements the details for that place (diualogue, images etc). player can do stuff at the location.
7. return from the location label to the navigation hub label
8. loop to the navigation hub label start


Note: of course you do not need to use an imagemap and hotspots as per my example, you can certainly build up the navmap using your loop code and imagebuttons instead.
 
Last edited:
  • Like
Reactions: The Lewd Gamer

osanaiko

Engaged Member
Modder
Jul 4, 2017
2,547
4,629
BTW dude I just noticed you posted identical topics in both the "Programming and Development" and the "Dev help" forums.

Spam/cross posting is not permitted, careful or you will get a ban and that would suck. :sick:
 

anne O'nymous

I'm not grumpy, I'm just coded that way.
Modder
Donor
Respected User
Jun 10, 2017
10,959
16,193
Now the good news is that I managed to pull that off with a little help of renpy documentation (and mostly python code).
Hmm...


Python:
$ game_running = True

while game_running:
    python: # this is just validation that gets the room name used in the image url
        location_img_string = location.lower()
        location_img_string = location_img_string.replace("'s", "")
        location_img_string = location_img_string.replace(" ", "_")

    if renpy.has_image(location_url_string, exact=True):
        show expression location_url_string # shows the room image using that validate string
   
    $ location = renpy.call_screen("minimap") # and here I call the screen with the minimap (an image with imagebuttons that each return a string containing the room name)
What a heavy way to do this.

Firstly you don't need to adjust the location name, having "location" directly hosting a valid name would be better.
Secondly, you don't need the show part at all.
Thirdly, you don't need to rely on the Python equivalent to have an interactive screen.

Python:
screen minimap():

    add "path/to/background/{}.jpg".format( location )

    imagebutton:
        idle "location 1.png"
        xpos 100
        ypos 1000
        action Return( "location1" )

    [...]
    imagebutton:
        idle "location n.png"
        xpos 300
        ypos 1000
        action Return( "locationn" )

    [...]
    imagebutton:
        idle "location x.png"
        xpos 500
        ypos 1000
        action Return( "locationx" )

label whatever:
    while game_running:
        call screen minimap
        jump expression _return

The problem arises now that I want to create yet another way of moving: a room selector (basically two arrows pointing left and right that change the current room)
Well, a list of locations is mostly all you need.

Python:
define locationNames = [ "location1", "location2", "location3", "location4" ]
default locationIndex = 0

screen minimap():

    add "path/to/background/{}.jpg".format( location )

    imagebutton:
        idle "prev room.png"
        xpos 0
        ypos 1000
        action [ If( locationIndex == 0, SetVariable( "locationIndex", len( rooms ) - 1), SetVariable( "locationIndex", locationIndex - 1 ), Return() ]

    imagebutton:
        idle "location 1.png"
        xpos 100
        ypos 1000
        action Return( "location1" )

    [...]
    imagebutton:
        idle "location n.png"
        xpos 300
        ypos 1000
        action Return( "locationn" )

    [...]
    imagebutton:
        idle "location x.png"
        xpos 500
        ypos 1000
        action Return( "locationx" )


    imagebutton:
        idle "next room.png"
        xpos 0
        ypos 1000
        action [ If( locationIndex == len( rooms ) - 1, SetVariable( "locationIndex", 0), SetVariable( "locationIndex", locationIndex + 1 ), Return() ]

label whatever:
    while game_running:

        call screen minimap

        if _return:
            $ locationIndex = locationNames.index( _return )
            jump expression _return
        else:
            jump expression locationNames[locationIndex]
You can of course change the list stored into "locationNames" if it need to be limited by a global location ; like by example "house" and "workplace", that obviously don't have link together.


I don't really need help with the logic of how to achieve the selector, [...]
Yet it's the whole logic (using Python equivalent and having two different screens) that were wrong...


PS. I'm guessing there should be a better way to read a minimap (or any kind of UI) input that doesn't require for the code to "stop" to get that info.
How can there be a better way to "read an input", than to wait for that input to exist ?
 
  • Heart
Reactions: The Lewd Gamer

anne O'nymous

I'm not grumpy, I'm just coded that way.
Modder
Donor
Respected User
Jun 10, 2017
10,959
16,193
[Sorry for the double post]

Code:
define room_a = {"bg_image": "bg cave",       "move_left" : False,     "move_right" : "room_b"}
define room_b = {"bg_image": "bg washington", "move_left" : "room_a",  "move_right" : "room_c"}
define room_c = {"bg_image": "bg whitehouse", "move_left" : "room_b",  "move_right" : False}
Like you don't need the "move_left" and "move_right" parts, it would be better expressed that way:
Code:
define roomNameToImage = { "room_a": "bg cave", "room_b": "bg washington", "room_c": "bg whitehouse" }

screen minimap():

    add "{}".format( roomNameToImage[location] )
 
Jun 21, 2020
67
86
BTW dude I just noticed you posted identical topics in both the "Programming and Development" and the "Dev help" forums.

Spam/cross posting is not permitted, careful or you will get a ban and that would suck. :sick:
Yesss! I know.... I realized this forum existed right after posting the previous one, and not knowing how to delete the original post I decided to leave it be and create another one in here
I feared hitting the report button on myself because I didn't know what it would do...

After reading through the Tutorial for Imagemaps / Screens myself, I realized that it still didn't really give enough information, so I spent the afternoon messing around and finally got a working demo (btw, Renpy official docs remain cursed. The info they contain is technically correct, but infuriatingly sparse and confusing compared to almost any other doco I work with in $DAYJOB)

Anyway, here is some code for an example "Minimap and Arrows navigation" implementation. It that works okay, but no guarantees it is great style. Anne'O'Nymous will probably hate it, lol.

Drop the code into the "Tutorial" game folder as it uses some assets for background - it overrides the main menu so launching the tute goes straight into my example.
Code:
# override the "Tutorial" game start label so it runs my code instead
define config.label_overrides = {
    "start": "nav_demo_start"
}


label nav_demo_start:
    scene bg cave
    "Hello, welcome to the nav demo"
    jump nav_hub

label nav_hub:
    call screen nav_map(True) # (using call) show nav_map screen and wait for input, param makes it interactable
    scene expression locations[location]["bg_image"]
    show screen nav_map(False)  # (using show) show nav_map screen but don't wait for input, param makes it non-interactable so players cannot change location mid dialogue : remove this line if you don't want the map showing during the dialogue
    call expression location
    jump nav_hub

label room_a:
    "This is ROOM_A dialogue!"
    "Fascinating..."
    return

label room_b:
    "ROOM_B also has dialogue!"
    "Marvellous..."
    return

label room_c:
    "In ROOM_C: Omae wa mou shindeiru!"
    "NANI!?"
    return


default location = "room_a"

define room_a = {"bg_image": "bg cave",       "move_left" : False,     "move_right" : "room_b"}
define room_b = {"bg_image": "bg washington", "move_left" : "room_a",  "move_right" : "room_c"}
define room_c = {"bg_image": "bg whitehouse", "move_left" : "room_b",  "move_right" : False}

define locations = {
    "room_a": room_a,
    "room_b": room_b,
    "room_c": room_c,
}


init python:

    def Move(direction):
        global location
        global locations
        if direction == "left":
            if locations[location]["move_left"] != False:
                location = locations[location]["move_left"]
        if direction == "right":
            if locations[location]["move_right"] != False:
                location = locations[location]["move_right"]

    def CanMove(direction):
        global location
        global locations
        if direction == "left":
            return locations[location]["move_left"] != False
        if direction == "right":
            return locations[location]["move_right"] != False


screen nav_map(can_interact = False):

    sensitive can_interact

    imagemap:
        auto "nav_map %s"
        hotspot (841, 34, 101, 72)  action [If(location != "room_a", [SetVariable("location", "room_a"), Return()],[NullAction()]) ] selected (location == "room_a")
        hotspot (977, 30, 103, 74)  action [If(location != "room_b", [SetVariable("location", "room_b"), Return()],[NullAction()]) ] selected (location == "room_b")
        hotspot (1113, 32, 103, 76) action [If(location != "room_c", [SetVariable("location", "room_c"), Return()],[NullAction()]) ] selected (location == "room_c")

  imagebutton:
        xpos 50
        yalign 0.5
        idle "arrow left"
        hover "arrow left hover"
        sensitive CanMove("left")
        action [Function(Move, "left"), Return()]

    imagebutton:
        xpos 1130
        yalign 0.5
        idle "arrow right"
        hover "arrow right hover"
        sensitive CanMove("right")
        action [Function(Move, "right"), Return()]
It also needs some images for the arrows and imagemap, so I'll attach those in an archive as well.
I'll try that out right away... thanks a lot! you went beyond by sharing some code! Its better this way since I actually want to learn (and not just copy/paste without reason like most do). Thanks!
 
Last edited:
Jun 21, 2020
67
86
Ok, back already... just checked your code osanaiko and wow, I know get the "loop" you talked about using "jump", which reminds me a lot to how the "goto" statement works in C-based coding languages. And now that I think of it, renpy (made with python) is technically C-based.

Anyways I get that this code is the "main loop" and you basically call the "ui screen" that waits for an input (be it the minimap or the arrows), then you show the image using scene expression and then you excecute some kind of "event" with the labels using call expression location.
Python:
label nav_hub:
    call screen nav_map(True)
    scene expression locations[location]["bg_image"]
    show screen nav_map(False)
    call expression location
    jump nav_hub
This, of course (and curious as I am), brings more questions:
  1. Does this mean that I will have to have every UI element that I want the user to interact with in the same screen? since I will only be able to call one from the nav_hub.
  2. Say that I have other interactible elements in the screen that aren't necessarily part of the UI (maybe elements found in the specific location) and the user should be able to click those AND the UI elements, should this method adapt to that scenario (maybe having another screen/hub for each sceneario that simultaneously calls the nav_screen somehow?).
  3. If I want to use keyboard input as well, but I want to be able to change focus between the UI and the room elements, can I do that somehow (like for example, AD keys change rooms if focused on selector, or highlights different elements if focused on rooms)
As you can see, I am making a rather complicated game, and that's because I want to challenge myself since I find it the best way to learn a new language (and coding platform in this case as well). And again... you and Anne'O'Nymous went ahead and coded a bit for me, and that's great because it helps me visualize what you are trying to teach me, but I also don't want to abuse/take all of your time, so even pointing to the right direction is of great help (i.e. sharing a useful/related post, etc). Thanks again to you and everyone who wants to help!

And again, I will be playing around with the code and see if I get to do what I picture in my head. I will post any progress. And a million thanks again. Can't thank you enough... :ROFLMAO:

PS. Yes I thought the same... either way my game will loop to the first room once the last room is reached, so no limits.
Like you don't need the "move_left" and "move_right" parts, it would be better expressed that way:
Code:
define roomNameToImage = { "room_a": "bg cave", "room_b": "bg washington", "room_c": "bg whitehouse" }

screen minimap():

    add "{}".format( roomNameToImage[location] )
 

osanaiko

Engaged Member
Modder
Jul 4, 2017
2,547
4,629
[Sorry for the double post]



Like you don't need the "move_left" and "move_right" parts, it would be better expressed that way:
Code:
define roomNameToImage = { "room_a": "bg cave", "room_b": "bg washington", "room_c": "bg whitehouse" }

screen minimap():

    add "{}".format( roomNameToImage[location] )
Yes, I considered this simpler solution where list adjacency controlled the navigation links.

However in my example, even though there are only 3 "rooms", I wanted to demonstrate to The Lewd Gamer that it was possible to have a more complex navigation graph using python methods to decide the navigable destinations. Imagining if there were 10 rooms with only some connections to each other, it is more complex than a simple before-in-list and after-in-list relation.
 
Jun 21, 2020
67
86
DONE! Room selector AND minimap working, there might be a better way to do this but this is what I've come up with following your suggestions:

Python:
default location = "location_1"
define location_list = [
    "location_1",
    "location_2",
    ...
]

label start:
    jump main_loop

    return

label main_loop:
    call screen ui_screen(True)
    show expression location_list[location_list.index(location)]
    jump main_loop

init python:
    class Room(object):
        def __init__(self, x, y, xsize, ysize, name):
            self.x = x
            self.y = y

            self.xsize = xsize
            self.ysize = ysize
           
            self.name = name

    def move_to(direction, current_location):
        curr_index = 0
        for room in rooms:
            if room.name == current_location:
                curr_index = rooms.index(room)

        if curr_index == 0 and direction == "left":
            return rooms[len(rooms) - 1].name
        elif direction == "left":
            return rooms[curr_index - 1].name
        elif curr_index == (len(rooms) - 1) and direction == "right":
            return rooms[0].name
        elif direction == "right":
            return rooms[curr_index + 1].name

    rooms = []

    rooms.append(Room(..., ..., ..., ..., ...))
    [...] # and the rest of the rooms
   

screen ui_screen(can_interact = False):
    sensitive can_interact
    zorder 15
    
    use minimap()
    use room_selector()


screen minimap():
    hbox:
        xalign 1.0
        yalign 0.0
        xsize 192*2
        ysize 108*2

        frame:
            xfill True
            style 'minimap_container' # this is just a background color for the container

            frame:
                xoffset 15
                yoffset 15
                style 'minimap_background' # this is just an image for the house layout

                for room in rooms:
                    button:
                        xsize room.xsize
                        ysize room.ysize
                        xpos room.x
                        ypos room.y
                               
                        background "#16b102f1"
                        hover_background "#24c010"

                        action [SetVariable("location", room.name), Return()]

                frame:
                    style 'minimap_linework' # this is just an image of the line work that goes on top of the rooms

screen room_selector():
    hbox:
        xalign 0.5
        yalign 0.0
        xsize 192*2
        ysize 108
               
        frame:
            yoffset 108/2
            xfill True
            style 'minimap_container'

            button:
                xsize 54
                ysize 108
                xpos 0
                yalign 0.5
                text "Left" size 13 xalign 0.5 yalign 0.5
                                               
                background "#16b102f1"
                hover_background "#24c010"

                action [SetVariable("location", move_to("left", location)), Return()]

            button:
                xsize 54
                ysize 108
                xalign 1.0
                yalign 0.5
                text "Right" size 13 xalign 0.5 yalign 0.5
                                                           
                background "#16b102f1"
                hover_background "#24c010"
                       
                action [SetVariable("location", move_to("right", location)), Return()]
Again, there might be better (and by better and mean both tidier, cleaner and more efficient) ways to achieve what I accomplished here, which is basically a list of rooms that display on a minimap as (in this case green) squares that you can click on, and then displays that room's image. I also implemented a selector (just two buttons that say left and right) that let you rotate the rooms by iterating the list in order.

My only "problem" right now is not understanding why a line like this "show expression location_list[location_list.index(location)]", just on top of the "jump main_loop" in the label start, won't show anything (black screen) except the screens (minimap and selector). Like so:
Python:
label start:
    show expression location_list[location_list.index(location)]
    jump main_loop

    return
Shouldn't it show the image (because location already has the default value)? What am I missing here.
 

osanaiko

Engaged Member
Modder
Jul 4, 2017
2,547
4,629
  1. Does this mean that I will have to have every UI element that I want the user to interact with in the same screen? since I will only be able to call one from the nav_hub.
  2. Say that I have other interactible elements in the screen that aren't necessarily part of the UI (maybe elements found in the specific location) and the user should be able to click those AND the UI elements, should this method adapt to that scenario (maybe having another screen/hub for each sceneario that simultaneously calls the nav_screen somehow?).
  3. If I want to use keyboard input as well, but I want to be able to change focus between the UI and the room elements, can I do that somehow (like for example, AD keys change rooms if focused on selector, or highlights different elements if focused on rooms)
1. yes and no.

it's possible to "include" other Screens under a top level screen. This way you can build up a more complicated UI while keeping
the implementation details separated.
If we think about game UI design, there's often informational elements, and action selectors. The information elements can be shown on the screen using a separate "show screen" call, and these will always be shown until you intentionally hide the containing screen again.
Action selection is only displayed when the game "state" is ready to take user input (i.e. not in a dialogue cut-scene). So you could certainly add all the other controls into the "nav screen", either directly or via the "include" functionality.

2. Yes, you could have a scene specific Screen that contained some imagebuttons for stuff like "open chest" etc. you would "show screen" this just before "call screen" to the nav screen. both screen's clickable targets would be active and the assigned actions would run when clicked. you'd need to manually control the "hide screen" for one or both of these screen layers depending on how you implement.
Note: all the stuff in renpy script is also available in python methods, i.e. renpy.hide_screen("nav_screen"). so once you get more into the implementation, you can potentially replace a lot of boilerplate Renpy script with some python methods and build up your screens and actions via a data driven approach.

3. keyboard input in screens is documented in the Tutorial. Basically you add a "key" statement that triggers an Action like the imagebuttons etc do.
 
Jun 21, 2020
67
86
2. Yes, you could have a scene specific Screen that contained some imagebuttons for stuff like "open chest" etc. you would "show screen" this just before "call screen" to the nav screen. both screen's clickable targets would be active and the assigned actions would run when clicked. you'd need to manually control the "hide screen" for one or both of these screen layers depending on how you implement.
OOOOOOh ok, so if I show a screen with buttons, and then call another, I can affect a global variable with any button, am I correct?

3. keyboard input in screens is documented in the Tutorial. Basically you add a "key" statement that triggers an Action like the imagebuttons etc do.
yes, I've seen it... I also have learned the use of the "sensitive" statement from your code, and that (together with keyboard input) will help me develop the focus tool I had in mind. THANKS AGAIN!
 

osanaiko

Engaged Member
Modder
Jul 4, 2017
2,547
4,629
Python:
default location = "location_1"
define location_list = [
    "location_1",
    "location_2",
    ...
]
....SNIP...
My only "problem" right now is not understanding why a line like this "show expression location_list[location_list.index(location)]", just on top of the "jump main_loop" in the label start, won't show anything (black screen) except the screens (minimap and selector). Like so:
Python:
label start:
    show expression location_list[location_list.index(location)]
    jump main_loop

    return
Shouldn't it show the image (because location already has the default value)? What am I missing here.
Given the definition of location and location_list, I'm not sure what location_list[location_list.index(location)] is supposed to achieve: location_list is a list-of-strings. So you're just getting the value of "location" back, right?
Was it supposed to be using the "room" list-of-dicts instead?
 
Jun 21, 2020
67
86
Given the definition of location and location_list, I'm not sure what location_list[location_list.index(location)] is supposed to achieve: location_list is a list-of-strings. So you're just getting the value of "location" back, right?
Was it supposed to be using the "room" list-of-dicts instead?
Well yes, it's just that it's not a dict yet, it's just a list :ROFLMAO: . That's why it looks weird, I could just use the variable and that's it, but I put it that way to make it easier to change later... but what I mean is:
If
"show expression location_list[location_list.index(location)]" (or "show expression location" for simplification if you like)
works perfectly in the loop, why doesn't it work on the label start? if location does have a default value set, shouldn't it return the image of that location_1? like it does in the loop right after the value is changed by the minimap.
 

osanaiko

Engaged Member
Modder
Jul 4, 2017
2,547
4,629
Well yes, it's just that it's not a dict yet, it's just a list :ROFLMAO: . That's why it looks weird, I could just use the variable and that's it, but I put it that way to make it easier to change later... but what I mean is:
If
"show expression location_list[location_list.index(location)]" (or "show expression location" for simplification if you like)
works perfectly in the loop, why doesn't it work on the label start? if location does have a default value set, shouldn't it return the image of that location_1? like it does in the loop right after the value is changed by the minimap.
Hmm. Not sure, it looks correct "on paper". I'd try to debug as follows:

1. add a `scene "location_1"` and see if it works. UPDATE: just noticed you are using "show" and not "scene" for the background image - you should check the docs to make sure you are clear on the difference

2. add a dialogue line (so you have a chance to use debug) and then use the debugger (shift-o) to see what the value of `location` and `location_list[location_list.index(location)]` are before the `jump mainloop`. (note that you need to have `config.developer = True` to enable the developer tools including the debugger)
 
Jun 21, 2020
67
86
1. add a `scene "location_1"` and see if it works. UPDATE: just noticed you are using "show" and not "scene" for the background image - you should check the docs to make sure you are clear on the difference
You are right, just checked the docs and "scene" is so much better for this.

2. add a dialogue line (so you have a chance to use debug) and then use the debugger (shift-o) to see what the value of `location` and `location_list[location_list.index(location)]` are before the `jump mainloop`. (note that you need to have `config.developer = True` to enable the developer tools including the debugger)
I figured where the problem is... and like you said, it looks good because it is... the problem has to do with a label I call before other lines (which I removed from the code sample since I thought would only bring confusion to any future readers). I still don't know what the problem is but I guess it has to do with that and will soon get to the solution.
I'll share how it looks so you can see:
Code:
default var = "foobar"

label start:
    call intro
    scene expression location
    # jump main_loop

    return

label intro:
    $ var = "bar" # I hid what I actually do since it's not too different from this (assigning variables which I don't even use yet)

label main_loop:
    call screen ui_screen(True)
    scene expression location
    jump main_loop
I'm guessing it has something to do with the "call" expression, I'll look into it and update.

EDIT: Solved it! I was missing the "return" at the end of the intro label... since it was a call it needed that to go back and show the first location. :FacePalm: Otherwise it would just move on to the label main_loop just below (which is why the screens where showing but the first location wasn't)
 
Last edited:
Jun 21, 2020
67
86
I hope that anybody trying to get a minimap/room selector to work in renpy can be helped by the code provided above.
Again, thanks for everything osanaiko and anne. And although this matter is resolved, be sure I will post again in the future asking for more help :ROFLMAO: , since I have great ambitions for this development, and I am new to renpy.
Again (and for the last time) thanks...