Ways to Have User Navigate Rooms

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.
Message
Author
User avatar
chris3spice
Regular
Posts: 44
Joined: Wed Nov 20, 2013 12:39 am
Projects: BKA24
Location: Kansas
Contact:

Ways to Have User Navigate Rooms

#1 Post by chris3spice »

I'm working on a new side project and it involved a character being able to freely run through a mansion.I'm unsure how to do and just looking for some guidance on my thoughts so far for navigation.

Notes: I want to have each room have spots that "Photos" can be taken of, so the user would click on the spot and then it would show the photo. I would also like to have items shown in the rooms that can be picked up as well that would display a message and then be archived so the user could re-read the message later. I wasn't sure if these would affect my thoughts below or not so I just wanted to let people know.

Navigation Issue:
Thought 1:
There will be 4 buttons (for each direction) that will lead to the connecting rooms. There will be not be a button if a room is not next to it.
My issue is keeping track of all the individual rooms, the story has about 20 rooms and I would like to expand upon those in the future so this idea seems a bit cumbersome to me.

Thought 2:
Image map each room and have a "back" button. I have the same issues as above

Thought 3:
World Map, they click on an adjoining room to enter it.
My issue with this is zooming in on the part of the map where the player is currently at and making it so they can only click on the adjoining rooms.

User avatar
xela
Lemma-Class Veteran
Posts: 2481
Joined: Sun Sep 18, 2011 10:13 am
Contact:

Re: Ways to Have User Navigate Rooms

#2 Post by xela »

I am allergic to imagemaps :(

If you want this all to be automatic (without coding every room with tedious, separate logic), I'd suggest something along these lines:

There is a WorldMap class that handles movement, stores last label through a callback (back button, you can have back button leading all the way back to the start this way if you like), conditions the buttons and movement,

Room/Tile class (or a dict, class is prolly more convenient to use) that stores the coordinates, name of the label, corresponding image map and anything else you require.

A dict or a list|lists to create the actual map.

Zooming in/out the map you do with a simple transform with a map inside of a viewport (maybe it can be done without viewport (with crop), I cannot tell what's best here right off the bat) + bar to adjust the zoom factor. You can adjust location without too much fuss using coordinates vs pos on the map.

Crop for the photoshoot? Can't say I've tried that, but I see no reason for it not to work.

There are other good approaches, but this is how I'd do it. It's not the easiest concept and will require some work, but I doubt that it's more than a couple hundred lines of code. Everything I've suggested except for the photo thing I've either done in my game or seen working really well in others so it's all sound advice (in my opinion anyway).
Like what we're doing? Support us at:
Image

User avatar
chris3spice
Regular
Posts: 44
Joined: Wed Nov 20, 2013 12:39 am
Projects: BKA24
Location: Kansas
Contact:

Re: Ways to Have User Navigate Rooms

#3 Post by chris3spice »

I'll will have to do some searching, because I have no idea how to do that, I've seen the dungeon crawler but I'm needing each room to have its own separate background and not needing to create views like that system does. I'll still have complicated room logic for items and stuff that the player needs to find so I'll have to figure out how to incorporate all that as well, but I'll worry about that later.

User avatar
xela
Lemma-Class Veteran
Posts: 2481
Joined: Sun Sep 18, 2011 10:13 am
Contact:

Re: Ways to Have User Navigate Rooms

#4 Post by xela »

I have almost the exact code in my game, let me see if I can isolate it.

*You'll have to wait a bit, it has too much unneeded data since it is not based on loading labels. I'll also add the automated logger for the back button.
Last edited by xela on Sun Aug 10, 2014 7:27 pm, edited 1 time in total.
Like what we're doing? Support us at:
Image

User avatar
chris3spice
Regular
Posts: 44
Joined: Wed Nov 20, 2013 12:39 am
Projects: BKA24
Location: Kansas
Contact:

Re: Ways to Have User Navigate Rooms

#5 Post by chris3spice »

xela wrote:I have almost the exact code in my game, let me see if I can isolate it.
Thanks! That would be a big help.

User avatar
xela
Lemma-Class Veteran
Posts: 2481
Joined: Sun Sep 18, 2011 10:13 am
Contact:

Re: Ways to Have User Navigate Rooms

#6 Post by xela »

Ghm, if every room is a label, there is no need for the room class at all... labels can hold all the data.
Like what we're doing? Support us at:
Image

User avatar
xela
Lemma-Class Veteran
Posts: 2481
Joined: Sun Sep 18, 2011 10:13 am
Contact:

Re: Ways to Have User Navigate Rooms

#7 Post by xela »

This is completely self-sustaining, you can just throw it in any fresh Ren'Py project. There was no need for label callbacks and this should do what you require. You'll obviously have to add better map graphics/background/imagemaps and etc. yourself. This is more like a skeleton for logic and movement inside of your building. I went with a class for rooms as well, it obviously doesn't have to be a class but it can make thing much easier for you (like keeping track of backgrounds, secret rooms, imagemaps or other logic.)

If something is not self-evident, feel free to ask questions.

Code: Select all

init python:
    
    class Room(object):
        def __init__(self, label, color):
            self.label = label
            self.color = color
    
    class World(object):
        def __init__(self):
            # map assests
            self.dimensions = 20, 10
            self.map = [[blocked
                               for y in xrange(self.dimensions[1])]
                               for x in xrange(self.dimensions[0])]
            
            self.player_tile = [3, 3]
            self.map[3][3] = test1
            self.map[3][4] = test2
            self.map[3][5] = test3
            self.map[4][4] = test4
            
            self.last_coords = None
            
        def screen_loop(self):
            while True:
                result = ui.interact()
                
                if result[0] == "go_back":
                    self.player_tile = self.last_coords*1
                    self.last_coords = None
                    renpy.jump(self.pt.label)
                    
                
                if result[0] == "move":
                    self.last_coords = self.player_tile*1
                    if result[1] == "up":
                        self.player_tile[1] -= 1
                    elif result[1] == "down":
                        self.player_tile[1] += 1
                    elif result[1] == "left":
                        self.player_tile[0] -= 1
                    elif result[1] == "right":
                        self.player_tile[0] += 1
                    renpy.jump(self.pt.label)
                    
        @property
        def pt(self):
            return self.map[self.player_tile[0]][self.player_tile[1]]
            
        def button_state(self, direction):
            if direction == "up":
                if self.player_tile[1] - 1 >=  0 and self.map[self.player_tile[0]][self.player_tile[1] - 1] != blocked:
                    return True
            elif direction == "down":
                if self.player_tile[1] + 1 <= self.dimensions[1] - 1 and self.map[self.player_tile[0]][self.player_tile[1] + 1] != blocked:
                    return True
            elif direction == "left":
                if self.player_tile[0] - 1 >=  0 and self.map[self.player_tile[0] - 1][self.player_tile[1]] != blocked:
                    return True
            elif direction == "right":
                if self.player_tile[0] + 1 <= self.dimensions[0] - 1 and self.map[self.player_tile[0] + 1][self.player_tile[1]] != blocked:
                    return True
            return False        
            
label start:
    python:            
        blocked = Room("", "#f0f8ff")
        test1 = Room("test1", "#00ffff")
        test2 = Room("test2", "#00ffff")
        test3 = Room("test3", "#00ffff")
        test4 = Room("test4", "#faebd7")
        map = World()
    "Starting the game!"
    jump test1
    
label test1:
    "Label 1 reporting in!"
    "You can put backgrounds or show other screens from here!"
    "Put any logic inside of the loop"
    show screen my_screen
    $ map.screen_loop()
    
label test2:
    "Just entered label 2!"

    $ map.screen_loop()    
    
label test3:
    "Just entered label 3!"
    
    $ map.screen_loop()
    
label test4:
    "I am the room to the right that you can barely see on the map!"
    
    $ map.screen_loop()
    
screen my_screen:
    # Tile Map ---------------------------------------------->
    frame:
        align (1.0, 1.0)
        xysize (200, 200)
        xfill True
        yfill True
        hbox:
            align (0.5, 0.5)
            box_wrap True
            xysize(162, 82)
            for y in xrange(map.dimensions[1]):
                    for x in xrange(map.dimensions[0]):
                        if map.pt == map.map[x][y]:
                            frame:
                                xysize (8, 8)
                                xfill True
                                yfill True
                                background "#4b0082"
                        else:        
                            frame:
                                xysize (8, 8)
                                xfill True
                                yfill True
                                background map.map[x][y].color


    text("Player Coords: %d, %d"% (map.player_tile[0], map.player_tile[1])) align (0.5, 0.0)
    
    # Directional Buttons ---------------------------------------------->
    vbox:
        align (0.5, 0.9)
        spacing 10
        hbox:
            xalign 0.5
            textbutton "Up":
                action If(map.button_state("up"), true=Return(['move', 'up']))
        hbox:
            xalign 0.5
            spacing 5
            textbutton "Left":
                action If(map.button_state("left"), true=Return(['move', 'left']))
            textbutton "Down":
                action If(map.button_state("down"), true=Return(['move', 'down']))
            textbutton "Right":
                action If(map.button_state("right"), true=Return(['move', 'right']))

    # Action Buttons ---------------------------------------------->
    textbutton "Back":
        align (0.01, 0.5)
        action If(map.last_coords, true=Return(["go_back"]))
                    
Like what we're doing? Support us at:
Image

User avatar
chris3spice
Regular
Posts: 44
Joined: Wed Nov 20, 2013 12:39 am
Projects: BKA24
Location: Kansas
Contact:

Re: Ways to Have User Navigate Rooms

#8 Post by chris3spice »

That works perfectly, I believe I understand most of what is going on so I don't think I'll have a problem integrating my other stuff into it!

User avatar
xela
Lemma-Class Veteran
Posts: 2481
Joined: Sun Sep 18, 2011 10:13 am
Contact:

Re: Ways to Have User Navigate Rooms

#9 Post by xela »

Have fun with it :) There aren't any good examples of moving around logic on the forums that could be decipherable to anyone who cannot code this themselves in couple of hours. Best one is the pseudo 3D dungeon crawler you referenced I believe.
chris3spice wrote:I'll still have complicated room logic for items and stuff that the player needs to find so I'll have to figure out how to incorporate all that as well, but I'll worry about that later.
The while loop with returns is incredibly powerful when handling complex logic. If the one in the class gets to big, copy it's contents directly to labels where you require a lot of logic instead o calling the method. You can code practically anything with a setup like this (saying this from experience). It also removes almost all "cannot start a new interaction while in the same context" issues since it executes stuff after ui.interact returns the result.
Like what we're doing? Support us at:
Image

Reem13
Newbie
Posts: 5
Joined: Sun Nov 05, 2017 2:52 pm
Contact:

Re: Ways to Have User Navigate Rooms

#10 Post by Reem13 »

Hello !
I made the following mini game using xela's wonderful code (THANK YOU xela !!!). It begins with a choice menu with 3 options. One will jump the player to the right place/room ("test4"), the other two will dump the player somewhere a little far from the goal.

QUESTION 1: how do I use the [$ map.screen_loop()] ? Can they be used to direct the player to OR prevent the player from entering specific labels or rooms/places ?

QUESTION 2 [SOLVED, just hide screen my_screen]: is there a way to "hide" some or all of the navigation buttons if the player reaches a certain place/room or fulfills a condition?

QUESTION 3: Is there a way to hide the little pixel map?

Here is the code (without the screens/buttons/navigation system bits):

Code: Select all

label start:
    scene bg sunnysky with dissolve
    python:            
        blocked = Room("", "#C9DCED")
        test1 = Room("test1", "#89E0E0")
        test2 = Room("test2", "#89E0E0")
        test3 = Room("test3", "#89E0E0")
        test4 = Room("test4", "#63B8B8")
        map = World()
    "Starting the game!"

    menu:
     "What do I need for making this potion..."

     "Dragon Tulip, Azur Sand and .... I don't remember!":
        jump test2

     "Dragon Tulip, Blue sycamore sap, and Azur sand...Right ?":
        jump test4

     "I'm not sure, but I think I can start with looking for Blue sycamore sap.":
        jump test3
    $ map.screen_loop()

label test1:
    scene bg square1 with fade
    "I really need to start looking for that sap."

    $ map.screen_loop()

label test2:
    scene bg square2 with dissolve
    show screen my_screen
    "I'm still in the village..."

    $ map.screen_loop()    

label test3:
    scene bg village with dissolve
    show screen my_screen
    "Just a little farther !"
    
    $ map.screen_loop()

label test4:
    scene bg forestentrance with dissolve

    "Now I can start looking for the sap."
    jump the_end
    $ map.screen_loop()

label the_end:
    scene bg sunnysky with dissolve
"The end."
return
I also attached the mini game itself (7zip format).
I'm still new to Ren'py (and coding in general, I'm more of a writer/artist kind of person). So, I'm sorry if some of my questions seem stupid.
Attachments
Into the Woods.7z
Art and sprites used are not mine. Bgs from Shijimi (?), sprites from Poepii Elzee.
(4.61 MiB) Downloaded 131 times
Last edited by Reem13 on Thu Nov 09, 2017 6:50 am, edited 2 times in total.

Reem13
Newbie
Posts: 5
Joined: Sun Nov 05, 2017 2:52 pm
Contact:

Re: Ways to Have User Navigate Rooms

#11 Post by Reem13 »

I found how to hide the direction controls !
Silly me, it was quite simple ^-^'
just replace show "show screen my_screen" with ....... hide .

example below:

Code: Select all

label start:
    scene bg sunnysky with dissolve
    python:            
        blocked = Room("", "#C9DCED")
        test1 = Room("test1", "#89E0E0")
        test2 = Room("test2", "#89E0E0")
        test3 = Room("test3", "#89E0E0")
        test4 = Room("test4", "#63B8B8")
        map = World()
    "Starting the game!"

    menu:
     "What do I need for making this potion..."

     "Dragon Tulip, Azur Sand and .... I don't remember!":
        jump test2

     "Dragon Tulip, Blue sycamore sap, and Azur sand...Right ?":
        jump test4

     "I'm not sure, but I think I can start with looking for Blue sycamore sap.":
        jump test3
    $ map.screen_loop()

label test1:
    scene bg square1 with fade
    "I really need to start looking for that sap."

    $ map.screen_loop()

label test2:
    scene bg square2 with dissolve
    show screen my_screen
    "I'm still in the village..."

    $ map.screen_loop()    

label test3:
    scene bg village with dissolve
    show screen my_screen
    "Just a little farther !"
    
    $ map.screen_loop()

label test4:
    scene bg forestentrance with dissolve
    hide screen my_screen
    "Now I can start looking for the sap."

    jump the_end

    $ map.screen_loop()

label the_end:
    scene bg sunnysky with dissolve
"The end."
return
This way, the player will stop seeing the direction control buttons when he reaches the room/place "test4".
This was so simple I'm embarrassed...

But I'm still trying to figure out how to hide the little map. I would love to be able to confuse the player a bit...

Reem13
Newbie
Posts: 5
Joined: Sun Nov 05, 2017 2:52 pm
Contact:

Re: Ways to Have User Navigate Rooms

#12 Post by Reem13 »

It's me again.

Mini-game description: I incorporated the bit of code posted in the topic mentioned above (Ways to Have User Navigate Rooms) into a mini-game I use to learn Ren'py.
I divided the game into 2 "quests" (both in 1 script.rpy). The first uses the original map (rooms "test1" to "test4"). The second uses rooms I added (rooms "test5" to "test8").
At the beginning of each Quest, I "dump" the player using a menu with 3 choices that jumps them to a label. They are supposed to err around until they reach the destination (the last tile of each part of the map).
I'm still playing around with the code. I tried several things but I still encounter the same problems:

!First problem!:
When the player starts using the navigation system, they are dumped at the player coordinates corresponding to the first room/location (self.player_tile). But then are "re-dumped" to the same coordinates that correspond to the first room ("test1"). Thus if I insert a piece of dialogue for example, it is played twice in a row.
Is there any way to avoid this from happening?

!Second problem!:
When I start the second quest, the navigation system dumps the player at the very first coordinates ("test1"/self.player_tile).
Is there a way to replace the first self.player_tile with another ?
or maybe restrict the tiles used to the original map (rooms "test1" to "test4") or the added rooms (rooms "test5" to "test8").

Here is a preview of the script (full game attached below):

Code: Select all

# Game starts here
# Quest 1    

label start:
    scene bg sunnysky with dissolve
    python:            
        blocked = Room("blocked", "#C9DCED")
        test1 = Room("test1", "#89E0E0")
        test2 = Room("test2", "#89E0E0")
        test3 = Room("test3", "#89E0E0")
        test4 = Room("test4", "#63B8B8")
        test5 = Room("test5", "#89E0E0")
        test6 = Room("test6", "#89E0E0")
        test7 = Room("test7", "#89E0E0")
        test8 = Room("test8", "#63B8B8")
        map = World()
    "Starting the first quest!"

    menu:
     "What do I need for making this potion..."

     "Dragon Tulip, Azur Sand and .... I don't remember!":
        jump test2

     "Dragon Tulip, Blue sycamore sap, and Azur sand...Right ?":
        jump test4

     "I'm not sure, but I think I can start with looking for Blue sycamore sap.":
        jump test3
    $ map.screen_loop()

label test1:
    scene bg square1 with fade
    "I really need to start looking for that sap."

    $ map.screen_loop()

label test2:
    scene bg square2 with dissolve
    show screen my_screen
    "I'm still in the village..."

    $ map.screen_loop()    

label test3:
    scene bg village with dissolve
    show screen my_screen
    "Just a little farther !"
    
    $ map.screen_loop()

label test4:
    scene bg forestentrance with dissolve
    hide screen my_screen
    
    "Now I can start looking for the sap."

    jump the_end

    $ map.screen_loop()

label the_end:
    scene bg sunnysky with dissolve

    "It's really hot today."

# Quest 2

label wood:
    show bg forestentrance with dissolve
    "What a beautiful view!"
    "Starting the second quest!"

label start2:
    python:            
        blocked = Room("blocked", "#C9DCED")
        test1 = Room("test1", "#89E0E0")
        test = Room("test2", "#89E0E0")
        test3 = Room("test3", "#89E0E0")
        test4 = Room("test4", "#63B8B8")
        test5 = Room("test5", "#89E0E0")
        test6 = Room("test6", "#89E0E0")
        test7 = Room("test7", "#89E0E0")
        test8 = Room("test8", "#63B8B8")
        map = World()
    
    show bg forestentrance with dissolve

    "But I really need to start lookign for that sap..."
    menu:
     "Where could the Sap tree be..."

     "It could be near the clearing. I'll start there.":
        jump test7

     "Maybe if I just follow this road, I'll end up finding it.":
        jump test6

     "The big clearing at the core of the forest could be a good place to start...":
        jump test8
    $ map.screen_loop()


label test5:
    show bg forestentrance with dissolve
    show screen my_screen

    show WoodFairy talking at center_right with mid_dissolve
    $ randfruit = renpy.random.choice(['an apple', 'some grapes', 'a dragon tail',])
    w "Would you like [randfruit]?"
    "Thanks! Bye!"
    hide WoodFairy talking with mid_dissolve
    "What a nice Fairy!"
    $ map.screen_loop()

label test6:
    show bg forestroad with dissolve
    show screen my_screen

    show Bear talking at center_right with mid_dissolve
    b "What are you doing here! Go away!"
    "Sorry!"
    hide Bear talking with mid_dissolve
    "Really wasn't expecting this..."
    $ map.screen_loop()

label test7:
    show bg forestclearing with dissolve
    show screen my_screen
    
    show Amadryad madtalking at center_right with mid_dissolve
    m "Would you like to keep me company?"
    "Euhh, no thank you!"
    hide Amadryad madtalking with mid_dissolve
    "This forest is scarier than I expected."

    $ map.screen_loop()

label test8:
    show bg saptree with dissolve
    hide screen my_screen

    "Oh! I was right! I found the sap tree!"
    jump the_end2
    $ map.screen_loop()

label the_end2:
    show bg sunnysky with dissolve
    "I got the sap I needed for the potion. Now I need to find the other ingredients."
    "The end."
return
For the navigation system, I used the Xela's script with the following coordinates/settings:

Code: Select all

    class World(object):
        def __init__(self):
            # map assests
            self.dimensions = 20, 10
            self.map = [[blocked
                               for y in xrange(self.dimensions[1])]
                               for x in xrange(self.dimensions[0])]
            
            self.player_tile = [3,3]
            self.map[3][3] = test1
            self.map[3][4] = test2
            self.map[3][5] = test3
            self.map[4][4] = test4
            self.map[5][4] = test5
            self.map[6][4] = test6
            self.map[6][5] = test7
            self.map[7][5] = test8
            self.last_coords = None
I really hope someone answers instead of just downloading my attachement and leaving like the previous post :(
Please help a poor noob :(
Attachments
Into the Woods-v2.7z
This is the second version of the mini-game. Disclaimer: I added some pictures/sprites that are not open-source/free. Therefore, please do not re-distribute this mini-game. Art and sprites used are not mine. Bgs from Shijimi (?), sprites from Poepii Elzee. Added Bgs & sprites are from Eldarya.fr. No copyright infringement intended. Sprites and Bgs used for educational purposes only.
(8.08 MiB) Downloaded 106 times

User avatar
xela
Lemma-Class Veteran
Posts: 2481
Joined: Sun Sep 18, 2011 10:13 am
Contact:

Re: Ways to Have User Navigate Rooms

#13 Post by xela »

If you jump to a label manually (without using navigation system):

Code: Select all

     "Dragon Tulip, Azur Sand and .... I don't remember!":
        jump test2
You should put the player in that tile:

Code: Select all

     "Dragon Tulip, Azur Sand and .... I don't remember!":
     	map.last_coords = None # Will prevent player from going back
        map.player_tile = [3, 4]
        jump test2
If you decide to use this in actual game, screen_loop is likely to work better as a label. My guess is that it'll be more save/load friendly.
Like what we're doing? Support us at:
Image

Reem13
Newbie
Posts: 5
Joined: Sun Nov 05, 2017 2:52 pm
Contact:

Re: Ways to Have User Navigate Rooms

#14 Post by Reem13 »

Thank you sooooo much! Will try this as soon as I can!
Sorry to bother you again, but how do I exactly use the screen loop?
I tried inserting player coordinates or label names but it didn't work, saying it only accepts 1 argument ....
Thank you again for your reply ❤❤❤

User avatar
xela
Lemma-Class Veteran
Posts: 2481
Joined: Sun Sep 18, 2011 10:13 am
Contact:

Re: Ways to Have User Navigate Rooms

#15 Post by xela »

Screen loop just waits for user input, you use it as it is in the example whenever you wish to give a chance to the player to make a move.
Like what we're doing? Support us at:
Image

Post Reply

Who is online

Users browsing this forum: No registered users