KigyoDev
8 supporters
Ren'Py Tutorial: Automatic Location/Mini ...

Ren'Py Tutorial: Automatic Location/Minimap System

Jun 05, 2022

Since resuming my work on Artificial Selection, I've been refactoring lots of old code, and came up with a location system that updates an in-game map completely by itself! It works for both minimaps and the game menu - all you need is a map image (and optionally a "you are here" indicator):

>> Download Framework <<

Tutorial

So let's learn how to use it! The core of this system revolves around a simple "current_location" variable. For context, my old way of updating the location was:

    scene hallway with dissolve

    $ current_location = "hallway"

This worked, but it was very easy to overlook instances where the location should be changed. The cool thing about the new location system is that manually setting "current_location" won't be necessary. The location will change automatically with every scene statement, which is fool-proof and saves a ton of tedious work!

Step 1: Setting up Background Images

There are two things you need to do (aside from changing the default map background). The first step is to adjust your background images. Unless you diligently followed the engine default, there is a good chance that they all have different image tags, like "hallway", or "park". But to benefit from automatic location updates, we need the same image tag for all backgrounds. For all your backgrounds that you want a location for, add "bg" in front of the image name:

image bg storage = "images/backgrounds/storage.png"

image bg lounge = "images/backgrounds/books.png"

image bg tv = "images/backgrounds/tv.png"

Note: You will also have to adjust all your scene statements in accordance with this change. For example, "scene lounge" should be changed to "scene bg lounge", and so on.

Step 2: Setting up Locations

The second thing you have to do is defining the different locations in your game, like this:

define locations = {

    "clothes" : {"name":_("Clothing Store"), "area":None},


    "saroom" : {"name":_("Saeva's Room"), "area":1, "pos":(400,500)},

    "hallway" : {"name":_("Hallway"), "area":1, "pos":(240,490)},


    "storage" : {"name":_("Storage"), "area":2, "pos":(500,160)},

    "lounge" : {"name":_("Lounge"), "area":2, "pos":(460,680)},

    "tv" : {"name":_("TV"), "area":2, "pos":(340,680)},

    }

There's already a lot going on here, so let's go through these things in order:

The first strings ("clothes", "hallway", "storage") should match the names of your background images. This allows the location system to know that when you use "scene bg hallway", the player's current location is the hallway, and then access the associated information.

Note: One exception for this is "saroom", Saeva's room. Since all character's rooms use the same background image ("bg bedroom") the location cannot be automatically derived from the image name. In these instances, we do have to set "current_location" manually. I explain this in more detail in "Component 1".

Locations - name

Next is the location's name. This is optional, but it allows you to show players a human-readable string of where they currently are - on a screen, on a notification popup, on a save file, or anywhere else! Whenever you want to access the current location's name, use:

locations[current_location]["name"]

Locations - area

Area allows you to group locations together. Artificial Selection is set in a building with multiple floors, so not all locations share the same floor plan, and this variable allows me to distinguish between them. What this means for you: If locations use the same map background image, give them the same area number. (Though I assume many games will only need to set "area" to 1.)

You can also set area to None if a location should be hidden from your map. In my example, the clothing store only appears in the prologue of the game and is unrelated to where the main action takes place. Therefore, the map is hidden during the prologue.

Locations - pos

Setting up pos is important to let the map screen know where to put the "you are here" marker. It should consist of a tuple (x,y) determining the xoffset and yoffset from the upper left corner of the map image.

The easiest way to do it is to open up your map image in an editing program, and drawing a rectangle from the upper left corner of the map to the upper left corner of where the location indicator should go. The size of this rectangle corresponds to the marker's position.

This is an example for Saeva's room, with "pos": (400,500).

(Optional) Step 3: Configuration Variables

The most recent update made the map screen a lot easier to customize. You might not even want to change most of them, but I'll go through all of the variables in detail here.

Image-related variables

define map_image_dimension = (1100, 900)

define map_image_path = "map/"

define map_image_type = ".png"

The framework comes with a placeholder image, which you can (and should!) replace. These variables exist to tell the map screen how big your map image is, where it is located, and what type of image it is. If your map images are in a different folder, change the [map_image_path] accordingly.

One important restriction is that the file names have to be "1.png", "2.png", etc. and match the "area" that is assigned to your locations. (If you do not use PNG images, change [map_image_type] as needed.)

Note: If you don't want to use the "area view" buttons, the number restriction can be avoided. However, the file names should still match the areas defined in your locations variable. For example, if an image is called "basement.png", then the area of those locations should be "basement".

Default variables

default current_area = None

default current_location = "hidden"

default area_limit = 1

The first two variables should be set to the first area and location of your game, respectively.

On the other hand, [area_limit] specifies which areas are already "unlocked" on the map screen. If you want the map to progressively unlock new areas as they are encountered, this should stay 1.
Otherwise, if you want everything to be unlocked from the beginning, set the value to the highest area number in the game (if you have 5 areas, set it to 5).

UI variables

define game_resolution = (1920, 1080)

define minimap_position = (1.0, 0.0)

define minimap_offset = 10

These variables handle the positioning of the minimap.

[game_resolution] should, of course, match the default resolution of your game.

[minimap_position] specifies the alignment of the minimap. The default (1.0, 0.0) is the upper right corner of the screen. If you wanted the minimap to be in the lower left corner, for example, simply change this to (0.0, 1.0).

Finally, [minimap_offset] specifies how far away the minimap should be from the edge of the screen.

Location indicator variable

define location_indicator_image = None

The default (None) simply displays an "x", but you can also change this variable to an image name (for example, "images/map_indicator.png").

Preference variables

default preferences.show_minimap = True

default preferences.minimap_opacity = 0.5

default preferences.minimap_size = 0.3

default preferences.minimap_zoom = 2

These variables can be adjusted by you, but can also be added to the preferences screen to give players more freedom.

[preferences.show_minimap] determines whether the minimap is shown at all.

[preferences.minimap_opacity] specifies how translucent the minimap is. (0.0 would be invisible, 1.0 fully opaque.)

[preferences.minimap_size] lets you decide how big the minimap should be. (0.1 is very tiny, 1.0 is the original image size.)

Finally, [preferences.minimap_zoom] determines how much the minimap is zoomed/cropped. (A value of 2 shrinks the map by half, a value of 3 is only a third of the full map, and so on. If you want to disable this feature, you can set the value to 0.)

Setup complete!

With that, you're done, and the map screen should be fully functional. Hooray!

The rest of this tutorial only explains the different components in case you are curious or unsure how to customize things.

Component 1: The Location Function

def location(name=None):

loc = None

if 'bg' in renpy.getshowingtags():

loc = renpy.get_attributes('bg')[0]

if name in locations:

current_location = name

elif loc in locations:

current_location = loc

if locations[current_location]["area"]:

current_area = locations[current_location]["area"]

if locations[current_location]["area"] > area_limit:

area_limit = locations[current_location]["area"]

So, what's happening in here? The name parameter is only used when we update the location manually. This has to be done when multiple locations share the same background image:

scene bg bedroom with dissolve

$location("saroom")

(In this example, "bedroom" by itself would be too ambiguous, because all characters' bedrooms look the same. We unfortunately have to specify whose room it is by hand.)

The most important part of the function is:

        if 'bg' in renpy.get_showing_tags():

            loc = renpy.get_attributes('bg')[0]

        if name in locations:

            current_location = name

        elif loc in locations:

            current_location = loc

It first checks if a background is currently shown, and if yes, sets our placeholder variable loc to said background image. If we have a valid location name, the location is updated.

Last but not least:

        if locations[current_location]["area"]:

            current_area = locations[current_location]["area"]

            if locations[current_location]["area"] > area_limit:

                area_limit = locations[current_location]["area"]

This makes sure that we can access new areas whenever we come across a new one.

Location Function - Behavior

I tried to make this function as error-proof as possible, which results in the following default behaviors:

scene bg kitchen #current_location = "kitchen"

scene black #current_location = "kitchen"

scene bg lounge #current_location = "lounge"

scene bg undefined_location #current_location = "lounge"

In particular, background images that don't have an associated location won't break or hide the map screen - it will stay on the last valid location. If you do want to hide the map screen, you can add:

define locations = {

    "hidden" : {"name":_("???"), "area":None},

}

scene bg kitchen #current_location = "kitchen"

scene black #current_location = "kitchen"

$location("hidden") #current_location = "hidden"

Because "hidden" has area None, it will hide the map.

Component 2: The Status Screen (Game Menu)

screen status():

    tag menu

    layer 'game_menu'

    on "show" action SetVariable("current_area", locations[current_location]["area"])

    use game_menu(("Status")):

text _("Current Location: ") + locations[current_location]["name"]

        use map

To actually view our map in-game, we need to turn it into a "game_menu" and also add it to the navigation! Be sure to add a button with action ShowMenu('status') in there.

Component 3: The Minimap

By "minimap", I mean this thing in the upper right corner:

Fun fact: You can click on the minimap to get to the game menu!

Tutorial End

...And that's it! Writing this tutorial took wayyyyy longer than I expected, but I hope it helps!

Let me know below if there is anything that's still unclear or if you run into any difficulties!

Enjoy this post?

Buy KigyoDev a brain cell

More from KigyoDev