Custom Mouse Cursors

Ren'Py has two systems for creating custom mouse cursors. The first takes advantage of the hardware mouse cursor, while the second uses Ren'Py to draw a displayable as the mouse cursor.

The hardware mouse cursor has the advantages:

  • It is very fast.

  • It is very low overhead, leaving Ren'Py time to do other things.

And the limitation:

  • Cursors are limited to small sizes. 32x32 is guaranteed, while 64x64 seems to work widely.

  • Cursors are limited to sequences of image files.

Using Ren'Py to draw a displayable as a cursor inverts these restrictions. While the cursors can be anything Ren'Py can draw, Ren'Py needs to do the drawing. When triple buffering is enabled on a computer, a lag can be present that isn't for the harware cursor.

Hardware Mouse Cursor

The hardware cursor is controlled by the config.mouse variable. This variable consists of a dictionary, that maps mouse names to a list of frames. Each frame is a 3-component tuple that contains an image file, and then X and Y offsets within that image.

For example:

define config.mouse = { }
define config.mouse['default'] = [ ( "gui/arrow.png", 0, 0) ]
define config.mouse['spin' ] = [
    ( "gui/spin0.png", 7, 7 ),
    ( "gui/spin1.png", 7, 7 ),
    ( "gui/spin2.png", 7, 7 ),
    ( "gui/spin3.png", 7, 7 ),
    ( "gui/spin4.png", 7, 7 ),
    ( "gui/spin5.png", 7, 7 ),
    ( "gui/spin6.png", 7, 7 ),
    ( "gui/spin7.png", 7, 7 ),
]

When an animation consists of multiple frames, the frames are played back at 20fps. Ren'Py will only change the cursor when the image or offsets change.

The following table lists the various states that the cursor can be in and the corresponding usage:

State

Usage

default

Used at all times unless another state is specified. It should always be present, as it is used when a more specific key is absent

say

Used when the player is on the "Say" screen.

with

Used during transitions.

menu

Used when the player is in a menu (for example, choice).

prompt

Used when the player is prompted for input.

imagemap

Used on an imagemap.

button

Used when the player is hovering over a button/imagebutton.

pause

Used during pause, renpy.pause()

mainmenu

Used in the main menu.

gamemenu

Used in the game menu.

Every key can have an optional pressed_ prefix, which indicates the cursor to use when the mouse is pressed. For instance, pressed_button is used when the user clicks on a button. To define a default pressed cursor style, use pressed_default key. It is used when no other pressed cursor is defined.

For example:

define config.mouse = { }
define config.mouse['default'] = [ ( "gui/arrow.png", 0, 0) ]
define config.mouse['pressed_default'] = [ ( "gui/arrow_pressed.png", 0, 0) ]
define config.mouse['button'] = [ ( "gui/arrow_button.png", 0, 0) ]
define config.mouse['pressed_button'] = [ ( "gui/arrow_button_pressed.png", 0, 0) ]
define config.mouse['menu'] = [ ( "gui/arrow_menu.png", 0, 0) ] # This cursor will be used when the player is in a menu
# Since there is no "pressed_menu" cursor, "pressed_default" cursor will be used instead

Displayable Mouse Cursor

A displayable cursor uses the config.mouse_displayable variable, and the MouseDisplayable displayable. As an example:

image mouse spin:
    "gui/spin0.png"
    rotate 0.0
    linear 1.0 rotate 360.0

    # Pause so image prediction can happen.
    pause 1.0

    repeat

define config.mouse_displayable = MouseDisplayable(
    "gui/arrow.png", 0, 0).add("spin", "mouse spin", 9.9, 9.9)
class MouseDisplayable(cursor, x, y)

A displayable that wraps a mouse cursor displayable, and causes it to move across the screen when the player moves their mouse.

cursor

A displayable that is used to draw the mouse.

x, y

The coordinates of the hotspot, relative to the upper left corner of the mouse, in virtual pixels.

add(name, cursor, x, y)

This adds a second cursor, that is used when the name mouse is displayed. This returns the MouseDisplayable, so that calls to this method can be chained.

Using Mouse Cursors

The usual way to use a mouse cursor is to provide the mouse property, giving the name of the cursor, to something that can be focused in a screen. (A button or bar.) For example:

screen test():
    textbutton "Mouse Test" action NullAction() mouse "spin"

It's also possible to use default_mouse to set the mouse cursor globally:

$ default_mouse = "spin"