Point and Click#

Introduction#

Point and click interface, apart from standard usage in Ren’Py, can be applied to:

  • Movement

    • Navigate between locations.

    • Step in various directions (e.g., when having arrow buttons).

  • Interaction with objects and NPCs

    • Find hidden objects in a location (to progress, to get bonus content or extra points)…

    • Manage character’s inventory and containers. Equip and unequip items.

    • Pick up and drop items; trade them.

    • Interact with NPCs, choose dialog options.

  • Use custom graphical interface

    • Activate items, spells, skills…

    • Use HUD and other UI elements — e.g., rotate and zoom mini-map, place beacons etc.

    • Open additional menus, information screens and the like.

For some of those functionalities, Drag and Drop can be useful too.

Point and click interface normally uses some background and some sensitive spots that react to being hovered and/or clicked.

In Ren’Py a sensitive spot can be implemented as

Clicking graphical objects#

Let’s suppose we want to show a room and let the player find clues there. We create a screen that would contain the background and the sensitive spots. There are two main ways to make that screen:

  1. Show a background, then place there a number of separate buttons.

  2. Use imagemap that contains all sensitive spots in one picture.

Imagemaps are especially convenient when there are many clicable items and they are parts of the whole picture.

Using a graphical editor like GIMP, we can extract the clickable spots to a separate image (hotspots) and also create a highligted version of that image (hovered hotspots).

Example:

  1. The image of the room ­— “ground” (courtesy of):

_images/ground_half.webp

2. The image of hotspots (“idle”) is mostly transparent, only with cut out hotspot images:

_images/idle_half.webp
  1. The image of hovered hotspots (“hovered”) is just a bit brighter:

_images/hover_half.webp

These images can be used as imagemap thusly:

# Prepare pictures for imagemap, each full screen size (1920x1080)
# images/room_ground.webp
# images/room_hover.webp
# images/room_idle.webp

# Create screen with imagemap using those pictures
screen investigate_room():

    imagemap auto "room_%s":

        hotspot (611, 0, 911, 558) action Function(
                renpy.notify, "A strange TV set!")

        hotspot (194, 12, 223, 250) action Function(
                renpy.notify, "A strange lamp!")

        hotspot (788, 594, 229, 195) action Function(
                renpy.notify, "A strange toy!")

label start:
    call screen investigate_room
    return

style notify_frame:
    pos (600, 500)

style notify_text:
    size 48

As you see, the code is very simple: we set three hotspots, retrieving their area coordinates from the graphical editor. Ren’Py notifications are actions for those hotspots, because it’s just an example and we don’t need anything more fancy. And it works: hovering a hotspot we see its highlighted item, and clicking it we run its action:


Hotspots are not always suitable though. Their sensitive areas are defined in rectangles, and they can be set to be sensitive only in non-transparent parts. That allows to have hospots of arbitrary shapes. Sometimes however a non-transparent part can belong to more than one rectangle. Suppose that we want both the table and the AC here to be clickable:

_images/hotspots_intersect.webp

Their rectangles intersect, and even if we cut them out of the background, there are still opaque parts that belong to both “table rectangle” and “AC rectangle”:

_images/hotspots_intersect2.webp

In cases like this, we need to use buttons instead of hotspots. Each button uses its own image, rather than sharing the same image:

_images/imagebuttons.webp

As buttons have separate images, it doesn’t matter if their rectangles intersect and catch non-transparent parts of each other.

(We just need to give them property focus_mask True.)

Now let’s create a screen with buttons instead of imagemap:

# Create screen with buttons, using "room_ground.webp" as background
# and button images:
# "ac_idle", "ac_hover", "table_idle", "table_hover".

screen investigate_room2():

    add "room_ground"

    imagebutton auto "table_%s":
        area (745, 767, 1175, 313)
        focus_mask True
        action Function(
                renpy.notify, "A strange table!")

    imagebutton auto "ac_%s":
        area (1465, 294, 452, 678)
        focus_mask True
        action Function(
                renpy.notify, "A strange AC!")

label start:
    call screen investigate_room2

Aaaand… it works:

To conclude, let’s join both methods. As we can put other elements into imagemap (not only hotspots and hotbars), we can place buttons right there. (Though that’s not necessary. Still we can see here how similar hotspots and imagebuttons are):

screen investigate_room():

    imagemap auto "room_%s":

        hotspot (611, 0, 911, 558) action Function(
                renpy.notify, "A strange TV set!")

        hotspot (194, 12, 223, 250) action Function(
                renpy.notify, "A strange lamp!")

        hotspot (788, 594, 229, 195) action Function(
                renpy.notify, "A strange toy!")

        imagebutton auto "table_%s":
            area (745, 767, 1175, 313)
            focus_mask True
            action Function(
                    renpy.notify, "A strange table!")

        imagebutton auto "ac_%s":
            area (1465, 294, 452, 678)
            focus_mask True
            action Function(
                    renpy.notify, "A strange AC!")

label start:
    call screen investigate_room
    return

style notify_frame:
    pos (600, 500)

style notify_text:
    size 48

The result:


Now, what if we need to dynamically add or remove items at the scene?

Adding and removing items#

Screens in Ren’Py can have if statements, for conditional execution of their blocks. Thus we can show and hide buttons or hotspots depending on conditions we set, e.g. some variables.

So clicking a button we can remove that item from screen, and we can add items when some conditions are met. For example, imagine that you were sent to gather 5 mushrooms. Clicking each one you increase the counter and remove the mushroom from the scene. After you gathered 4, the fifth one appears.

The code is rather simple:

# Set areas for 4 mushrooms
default shrooms = [
    ( 335, 437, 128, 128),
    ( 330, 912, 128, 128),
    (1076, 912, 128, 128),
    (1437, 690, 128, 128),
    ]

# Count how many we have
default got = 0

init python:
    def got_one(area=None):
        """ When a mushroom is clicked """
        # Increase the counter
        store.got += 1
        if store.shrooms:
            # Remove the shroom from list,
            # so it won't be shown on screen
            store.shrooms.remove(area)
        if not store.shrooms:
            # If all were gathered, quit the screen
            return True

screen mushrooms(extra=None):
    text "Mushrooms: [got]" color "#333" size 80
    for m in shrooms:
        # For every mushroom in the list, show it as imagebutton
        imagebutton idle "mushroom":
            area m
            action Function(got_one, m)

    if extra:
        # The 5th shroom doesn't show until I say so
        imagebutton idle "mushroom":
            at floating
            action got_one

label start:
    scene new_quest
    "Bring me 5 mushrooms!"
    scene platformer
    call screen mushrooms
    "{i}(me){/i} Oh NO... Where's the fifth mushroom?"
    call screen mushrooms(True)
    show expression Text("VICTORY!", color="#876", size=300) at titles
    pause
    return

transform titles:
    alpha 0.
    align (.5, 1.)
    linear 3. align (.5, .5) alpha 1.

transform floating:
    anchor (0, .5)
    pos (1920, 0.5)
    ease 1. xpos 1860
    ease 1. xpos 1920
    ease 12. xpos -130
    repeat

# Credits:
# Mushroom https://www.iconarchive.com/icons/iconicon/veggies/readme.rtf
# Guru http://www.afterglow.ie/

The same way we can interact with any items on screen:

  • Show the item conditionally if necessary.

  • Make item properties conditional (putting them in if block).

  • Running custom functions when we hover and/or click the item.

For details, see e.g. properties action, alternate, hovered, unhovered of Button.