[Ren'Py] Image maps - create an interactive main screen

APD13

Captain X
Donor
Game Developer
Feb 2, 2018
254
702
Hi mates,

How can I create a main screen where the players can interact with different elements to activate the content. This type of feature has been used in many games, an the most known is MILF City. Basically you have a screen with different image maps. For example, a house where the user can navigate between the rooms and interact with elements and unlock scenes in the process.

I just need something to start from.

Thank you in advance
 
Last edited:

mgomez0077

Well-Known Member
Game Developer
Oct 12, 2017
850
1,175
Imagemaps I think it has become a bit old, although you can use it, but it has its limitations, I recommend you an imagebuttons...

And as for how to start, you can try this:

Code:
screen house_navigation:
    add "nav/house_corridor.webp"  ## this is the background
    imagebutton auto "nav/door1_%s.webp" xpos 100 ypos 140 focus_mask True action Hide("house_navigation), Jump("Room1") #Door button to jump to "Room1" label
    imagebutton auto "nav/door2_%s.webp" xpos 700 ypos 140 focus_mask True action Hide("house_navigation), Jump("Room2") #Door button to jump to "Room2" label
For this example you need 5 images, "house_corridor" for the background, and two images for every imagebutton.
In the examples, images for imagebuttons are inside a folder called "nav" inside the "images" folder; and you need one for idle state, called in this case "door1_idle.webp" (use your preferred format, just keep in main that you need transparency, so use webp or png, not jpg), and another image for the same imagebutton for the hover state, called in this case "door1_hover.webp".
The remaining two images for the other imagebutton would be "door2_idle.webp" and "door2_hover.webp".

For images in "hover" state the best thing is to create some kind of highlight with some image editor, so that the player when passing over notices that he/she can click there.

And to use this code in the game, here's an example:
Code:
label start:
    "This is the corridor upstairs, and you can visit the rooms..."
    "Try it"

    show screen house_navigation

label Room1:
    "This is the room of..."
    "It's pretty, huh? Now let's go back to the corridor"
    show screen house_navigation   

label Room2:
    "This is the room of..."
    "It's pretty, huh? Now let's go back to the corridor"
    show screen house_navigation
 
  • Like
  • Love
Reactions: zger and APD13

Rich

Well-Known Member
Modder
Game Developer
Jun 25, 2017
753
2,256
To add a few tidbits to what @mgomez0077 wrote:
  1. If you're going to display your "pick what to do next" screen via "show screen" and have it "Jump" to the appropriate spot to handle what you clicked on, you'll want to make sure that the screen is set to be modal. Otherwise, Ren'py will show your screen, but then move right on to the next statement in the code, and clicks on the screen will also be interpreted as "move to the next bit of dialog." Not what you'll want, in all probability.
  2. The alternate approach is to use "call screen" instead, which implicitly makes the screen modal. The particular paradigm I strongly prefer is to "call screen" and have anything you click on Return() a value. Then, right after the "call screen", I put the appropriate "and what do I do next" logic.
I tend to use #2 if I'm working on a "sandbox" type game, because I basically set up a "game loop" that "call"s out to various bits and pieces of logic, where each "call" returns back to the game loop with "what to do next." "What to do next" might be "call the dialog at this label" or "change the player to be at this location" (which then might either return "display the navigation map for the current location" or "call this label because there's an event that happens when you enter that location")

Doing this requires you to set up some "structure" (locations, actions, etc.) for your game, but once you get the basic plumbing in place, it's very straightforward to add more locations, events, etc., etc., etc. It turns out that there usually aren't all that many "what to do next" options in the game loop if you set up the proper abstractions. Otherwise, as your game grows you're probably going to find yourself repeating a lot of code over and over again, and possibly missing a few places when an update is required. Which leads to "the event triggers if you come from here, but not if you come from there" type bugs.

Just my $0.02.
 

APD13

Captain X
Donor
Game Developer
Feb 2, 2018
254
702
@mgomez0077 and @Rich, thank you a lot for stepping out and help me. As @Rich gave the example, I want to tend for a sandbox game that will generate actions regarding the progress of the player. Can I ask if you have an example or a source I can get the info from? Never tried this before and I am in the dark as a blind man.
 

mgomez0077

Well-Known Member
Game Developer
Oct 12, 2017
850
1,175
Maybe I'm wrong because I tend to use instead of , but I think my example code adapted to the call command would be like this, using :
Code:
screen house_navigation:
    add "nav/house_corridor.webp"  ## this is the background
    imagebutton auto "nav/door1_%s.webp" xpos 100 ypos 140 focus_mask True action Call("Room1") #Door button to jump to "Room1" label
    imagebutton auto "nav/door2_%s.webp" xpos 700 ypos 140 focus_mask True action Call("Room2") #Door button to jump to "Room2" label
Code:
label start:
    scene houser_corridor   #I forgot to put this in my previous example... this will show the image of the corridor :-p
    "This is the corridor upstairs, and you can visit the rooms..."
    "Try it"

    call screen house_navigation

label Room1:
    "This is the room of..."
    "It's pretty, huh? Now let's go back to the corridor"
    return   #It will return you to the previous call; if you don't come from a call command it will return you to the main menu.

label Room2:
    "This is the room of..."
    "It's pretty, huh? Now let's go back to the corridor"
    return
 
  • Love
Reactions: APD13

APD13

Captain X
Donor
Game Developer
Feb 2, 2018
254
702
Maybe I'm wrong because I tend to use instead of , but I think my example code adapted to the call command would be like this, using :
Code:
screen house_navigation:
    add "nav/house_corridor.webp"  ## this is the background
    imagebutton auto "nav/door1_%s.webp" xpos 100 ypos 140 focus_mask True action Call("Room1") #Door button to jump to "Room1" label
    imagebutton auto "nav/door2_%s.webp" xpos 700 ypos 140 focus_mask True action Call("Room2") #Door button to jump to "Room2" label
Code:
label start:
    scene houser_corridor   #I forgot to put this in my previous example... this will show the image of the corridor :-p
    "This is the corridor upstairs, and you can visit the rooms..."
    "Try it"

    call screen house_navigation

label Room1:
    "This is the room of..."
    "It's pretty, huh? Now let's go back to the corridor"
    return   #It will return you to the previous call; if you don't come from a call command it will return you to the main menu.

label Room2:
    "This is the room of..."
    "It's pretty, huh? Now let's go back to the corridor"
    return

Thank you! You have been of great help. I will give it a try
 
  • Like
Reactions: mgomez0077

anne O'nymous

Well-Known Member
Modder
Respected User
Jun 10, 2017
2,091
2,313
Maybe I'm wrong because I tend to use instead of , but I think my example code adapted to the call command would be like this, using :
Code:
    [...] action Call("Room1") #Door button to jump to "Room1" label
I know it's confusing, but called screens and called labels are two different mechanisms that absolutely don't react in the same way. So you don't need to call the labels from a called screen. You can still jump to labels like in your initial example, and you don't need to return from the labels you jumped to from the screen.

The problem of something like show screen navigation is double. There's the problem pointed by @Rich , and there's also the fact that the player can quit the actual flow at anytime.
Take this by example :
Code:
label blabla:
    show screen navigation
    mc "Hello sis."
    menu:
        "compliment her":
            mc "you look really good today."
            sis "thanks."
            $ sis_like += 1
        "Say nothing":
            pass
Like the player can quit the actual flow at anytime, he can by example do it right after the mc said that she look good. So, he'll have made the right choice, but he will not have the point for it, because he never reached this part.
Obviously, the fix for this particular example is to give the point before everything else, but it's still a flaw in the design to let the player quit in the middle of the action.

If the problem is that the navigation bar isn't always visible and it somehow break the design of the User Interface, you can use something like this :

[Note, I use textbutton for the example, because it simplify it. But obviously the exact same works with imagemap and imagebutton.]
Python:
# Flag to control the display of the navigation bar in the UI.
default disableNav = False

# The navigation bar when active.
screen myNav():
    # Ensure that it will always be on top of the UI.
    zorder 1
    # Add the navigation bar.
    use myNavButtons( True )

# The User Interface.
screen myUI():
    # Whatever is needed in the UI.
    vbox:
        hbox:
            text "Monday"
            null width 5
            text "noon"

    # Add the navigation bar, unless it's disabled.
    if disableNav is False:
        use myNavButtons()

# The navigation bar, inactive by default.
screen myNavButtons( active=False):

    vbox:
        yalign 0.5
        textbutton "Go here":
            # Add the action only if the bar is active.
            if active is True:
                action Jump( "here" )

        textbutton "Go there":
            if active is True:
                action Jump( "there" )
            #  An alternative is to have this when the bar 
            # is not active. But as you see the button still
            # react, what can be confusing.
            else:
                action NullAction()

label start:
    # The UI is always to show.
    show screen myUI
    "welcome to  my example."
    # But the navigation bar is active only now.
    call screen myNav

label here:

    "I'm here."
    "The navigation bar is still visible."
    "But it don't works."
    "Now it will works again."
    # Active the navigation bar again.
    call screen myNav
    "back here."

label there:
    "I'm there."
    #  Temporarily remove the navigation bar, and it only,
    # the User Interface is still visible.
    $ disableNav = True
    "But for this scene we need to get ride of the navigation bar."
    "Ok, now we can have it back."
    # Ok, the navigation bar can appear again.
    $ disableNav = False
    "END"
The "myUI" screen will always be visible and include the navigation bar by default. But the button are effectively active only when you call the "myNav" screen.
Plus, like the navigation bar is inside it's own screen, added by , you don't have to define the buttons twice, which will limit the risk of bugs.
 

APD13

Captain X
Donor
Game Developer
Feb 2, 2018
254
702
I know it's confusing, but called screens and called labels are two different mechanisms that absolutely don't react in the same way. So you don't need to call the labels from a called screen. You can still jump to labels like in your initial example, and you don't need to return from the labels you jumped to from the screen.

The problem of something like show screen navigation is double. There's the problem pointed by @Rich , and there's also the fact that the player can quit the actual flow at anytime.
Take this by example :
Code:
label blabla:
    show screen navigation
    mc "Hello sis."
    menu:
        "compliment her":
            mc "you look really good today."
            sis "thanks."
            $ sis_like += 1
        "Say nothing":
            pass
Like the player can quit the actual flow at anytime, he can by example do it right after the mc said that she look good. So, he'll have made the right choice, but he will not have the point for it, because he never reached this part.
Obviously, the fix for this particular example is to give the point before everything else, but it's still a flaw in the design to let the player quit in the middle of the action.

If the problem is that the navigation bar isn't always visible and it somehow break the design of the User Interface, you can use something like this :

[Note, I use textbutton for the example, because it simplify it. But obviously the exact same works with imagemap and imagebutton.]
Python:
# Flag to control the display of the navigation bar in the UI.
default disableNav = False

# The navigation bar when active.
screen myNav():
    # Ensure that it will always be on top of the UI.
    zorder 1
    # Add the navigation bar.
    use myNavButtons( True )

# The User Interface.
screen myUI():
    # Whatever is needed in the UI.
    vbox:
        hbox:
            text "Monday"
            null width 5
            text "noon"

    # Add the navigation bar, unless it's disabled.
    if disableNav is False:
        use myNavButtons()

# The navigation bar, inactive by default.
screen myNavButtons( active=False):

    vbox:
        yalign 0.5
        textbutton "Go here":
            # Add the action only if the bar is active.
            if active is True:
                action Jump( "here" )

        textbutton "Go there":
            if active is True:
                action Jump( "there" )
            #  An alternative is to have this when the bar
            # is not active. But as you see the button still
            # react, what can be confusing.
            else:
                action NullAction()

label start:
    # The UI is always to show.
    show screen myUI
    "welcome to  my example."
    # But the navigation bar is active only now.
    call screen myNav

label here:

    "I'm here."
    "The navigation bar is still visible."
    "But it don't works."
    "Now it will works again."
    # Active the navigation bar again.
    call screen myNav
    "back here."

label there:
    "I'm there."
    #  Temporarily remove the navigation bar, and it only,
    # the User Interface is still visible.
    $ disableNav = True
    "But for this scene we need to get ride of the navigation bar."
    "Ok, now we can have it back."
    # Ok, the navigation bar can appear again.
    $ disableNav = False
    "END"
The "myUI" screen will always be visible and include the navigation bar by default. But the button are effectively active only when you call the "myNav" screen.
Plus, like the navigation bar is inside it's own screen, added by , you don't have to define the buttons twice, which will limit the risk of bugs.
Hi mates,

Sorry for the late reply, I am doing a run to see if I can get this through and I will return with feedback. Thank you for the help guys