32blogby StudioMitsu

Screen Language en Ren'Py: Por Qué Tu UI No Se Actualiza

Aprende screen language de Ren'Py 8.5 desde cero. Cubre el paradigma declarativo, mecánica de interacciones, show vs call screen y control de UI basado en Actions.

8 min read
Contenido

Cuando intentas construir una pantalla de estado o un menú personalizado en Ren'Py, casi seguro pensarás: "Cambié la variable, pero la pantalla no se actualizó."

No es un bug. El screen language de Ren'Py está diseñado para ser declarativo. Si estás acostumbrado a la programación imperativa de Python, esto se siente contraintuitivo al principio.

Esta guía cubre los fundamentos del screen language en Ren'Py 8.5.2 y explica la mecánica detrás de "por qué no se actualiza."

Screen Language es declarativo

Python es imperativo. Escribes instrucciones paso a paso: "haz esto, luego haz aquello."

python
# Python (imperativo)
hp = 100
hp -= 30
print(hp)  # 70

El screen language de Ren'Py es declarativo. Describes cómo debería verse la pantalla.

renpy
screen stats_hud():
    frame:
        text "HP: [player_hp]"

Este text "HP: [player_hp]" no es un comando que dice "muestra HP ahora." Es una declaración: "la pantalla debería mostrar el valor de HP." Ren'Py lee esta declaración en el momento apropiado y construye la pantalla.

Esta naturaleza "declarativa" es la clave para entender el comportamiento de actualización de pantallas.

Sintaxis básica de screens

Definiendo y mostrando una screen

renpy
# Definir una screen
screen greeting():
    frame:
        xalign 0.5
        yalign 0.5

        vbox:
            text "Hello"
            textbutton "Close" action Hide("greeting")

label start:
    # Mostrar la screen
    show screen greeting

    "A window is visible on screen."

    # Ocultar la screen
    hide screen greeting

Define la estructura de UI con la declaración screen, luego muéstrala con show screen.

Displayables clave

Los bloques de construcción (displayables) que usas dentro de las screens.

DisplayablePropósitoEjemplo
textMostrar textotext "HP: 100"
addMostrar una imagenadd "icon.png"
textbuttonBotón de textotextbutton "OK" action Return()
imagebuttonBotón de imagenimagebutton idle "btn.png" action Return()
vboxContenedor verticalApila hijos verticalmente
hboxContenedor horizontalApila hijos horizontalmente
frameVentana enmarcadaÁrea de UI con fondo
barBarra/deslizadorbar value player_hp range 100

Para la lista completa, consulta Screens and Screen Language.

Condiciones y bucles

Puedes usar if y for dentro del screen language.

renpy
screen inventory():
    frame:
        vbox:
            text "Inventory"

            for item in inventory_list:
                textbutton "[item]" action NullAction()

            if len(inventory_list) == 0:
                text "No items"

Estos se parecen al if/for de Python, pero se comportan diferente. Los if/for de screen se re-evalúan cada vez que la screen se re-evalúa. No son flujo de control imperativo de una sola vez.

Por qué tu screen no se actualiza

Este es el concepto central. Mira este código:

renpy
default player_hp = 100

screen hp_display():
    text "HP: [player_hp]"

label start:
    show screen hp_display

    "HP is [player_hp]."

    $ player_hp -= 30

    "HP dropped to [player_hp]."

Este código funciona bien. Cuando player_hp cambia, la screen se actualiza.

Entonces, ¿por qué la gente dice "mi screen no se actualiza"? Aquí es cuando realmente sucede.

El caso: Sin interacción

renpy
label start:
    show screen hp_display

    $ player_hp -= 30
    # ← La screen NO se ha actualizado aquí

    $ player_hp -= 20
    # ← Todavía NO se ha actualizado

    "HP is [player_hp]."
    # ← La screen finalmente se actualiza en esta declaración say (interacción)

Las screens de Ren'Py se re-evalúan cuando ocurre una interacción. Una interacción es cualquier punto donde el motor espera input del jugador.

Causa interacciónNO causa interacción
Declaraciones say (say)$ variable = value
Opciones (menu)show image
pausehide image
call screenshow screen
with transitionDeclaraciones Python (líneas $)

No importa cuántas variables cambies con líneas $, la screen no se redibujará hasta la siguiente interacción.

La solución: Agrega una interacción

Para actualizar la screen después de cambiar variables, dispara una interacción con una declaración say o pause.

renpy
label start:
    show screen hp_display

    $ player_hp -= 30
    pause 0.5
    # ← pause dispara una interacción, la screen se actualiza

    $ player_hp -= 20
    "HP dropped to [player_hp]."
    # ← la declaración say también es una interacción, la screen se actualiza aquí también

Cuando cambias variables a través de botones dentro de una screen, los Actions (cubiertos abajo) automáticamente reinician la interacción, así que no necesitas disparar una manualmente.

Cuidado con la predicción de screens

Ren'Py predice las screens antes de mostrarlas. Esta optimización permite transiciones suaves, pero significa que el código Python dentro de las screens puede ejecutarse en momentos inesperados.

renpy
screen dangerous():
    python:
        # ¡Esto se ejecuta durante la predicción también!
        store.gold -= 100

    text "Gold: [gold]"

El bloque python: dentro de una screen se ejecuta durante la predicción, antes de que la screen se muestre realmente. Nunca escribas efectos secundarios (cambios de variables, operaciones de archivo) en bloques python: de screens. Usa Actions para cambios de variables en su lugar.

show screen vs call screen

Dos formas de mostrar una screen, con comportamiento muy diferente.

show screen: El label sigue ejecutándose

renpy
label start:
    show screen hp_display

    "HP display is visible during this dialogue."
    "Still visible here."

    hide screen hp_display

    "HP display is gone."

show screen muestra la screen pero no detiene la ejecución del label. Úsalo para HUDs siempre visibles (barras de estado, minimapas, etc.).

call screen: Espera input del jugador

renpy
screen choice_menu():
    vbox:
        xalign 0.5
        yalign 0.5

        textbutton "Fight" action Return("fight")
        textbutton "Flee" action Return("flee")

label start:
    call screen choice_menu

    # El resultado se almacena en _return
    if _return == "fight":
        "You chose to fight!"
    else:
        "You ran away!"

call screen muestra la screen y detiene la ejecución del label hasta que se llama a Return(). El valor de retorno va a _return. Úsalo para menús y diálogos temporales.

Controlando screens con Actions

Lo que pasas al action de un botón no es una llamada a función Python — es un objeto Action.

Un error común

renpy
init python:
    def do_heal():
        store.player_hp += 20

screen heal_button():
    # Incorrecto — llama a do_heal() inmediatamente durante la evaluación de la screen
    # textbutton "Heal" action do_heal()

    # Correcto — lo envuelve en Function()
    textbutton "Heal" action Function(do_heal)

Escribir do_heal() llama a la función inmediatamente durante la evaluación de la screen, pasando su valor de retorno (None) a action. Escribir Function(do_heal) hace que se ejecute cuando se hace clic en el botón.

Actions incorporados clave

ActionComportamiento
SetVariable("name", value)Cambiar una variable global
SetScreenVariable("name", value)Cambiar una variable local de screen
ToggleVariable("name")Alternar una variable booleana
Function(callable)Ejecutar cualquier función
Show("screen_name")Mostrar otra screen
Hide("screen_name")Ocultar una screen
Return(value)Devolver un valor a call screen
NullAction()No hacer nada (hace que un botón sea clickeable)

Los Actions incorporados reinician automáticamente la interacción después de la ejecución. Cambia HP con SetVariable, y cualquier screen que muestre HP se actualiza automáticamente. Por eso raramente necesitas renpy.restart_interaction() directamente.

Para la lista completa, consulta Screen Actions.

Variables locales de screen

Las variables usadas solo dentro de una screen se declaran con default.

renpy
screen counter():
    default count = 0

    vbox:
        text "Count: [count]"
        textbutton "+1" action SetScreenVariable("count", count + 1)
        textbutton "Reset" action SetScreenVariable("count", 0)

default se inicializa una vez cuando la screen se muestra por primera vez. Asignar variables en un bloque python: dentro de una screen las reinicia en cada re-evaluación.

Conclusión

Lo que cubrimos:

  • Paradigma declarativo: Las screens describen lo que debería mostrarse, no instrucciones paso a paso
  • Sintaxis básica: Define con screen, construye con displayables text/textbutton/vbox/frame
  • Interacciones: Las screens se re-evalúan en las interacciones. Las líneas $ solas no disparan actualizaciones de screen
  • show vs call: show screen es no bloqueante (para HUDs), call screen es bloqueante (para menús)
  • Actions: Pasa objetos SetVariable/Function/Return a los botones. Automáticamente disparan actualizaciones de screen

Para lo básico de Ren'Py, consulta la guía de inicio. Para visualización de imágenes, consulta la guía básica de imágenes.

Recursos oficiales: