Timers y vigías#
Algunos de los mejores momentos de una aventura gráfica pasan con retraso: el jugador toca un timbre, se va a pasear, y diez segundos después alguien en el piso de arriba murmura "¡el timbre!" y baja a abrir, esté donde esté el jugador para entonces. Ignitor construye esto con dos piezas pequeñas, timers y vigías, y esta guía te enseña a cablearlas en un ciclo que funciona siempre, no solo la primera vez.
Las tres piezas, en palabras llanas#
- Un timer es un temporizador de cocina. Le das cuerda con un nombre y unos segundos
(
STARTTIMER:doorbell_timer|10), te alejas, y suena solo. No pasa nada cuando suena: se queda ahí, sonado, hasta que algo lo revise. - Un vigía es una trampa de alambre. Vigila una condición en cada frame ("¿ya
sonó
doorbell_timer?") y en el momento en que la condición es verdadera dispara sus efectos, una sola vez. Después de disparar queda desarmado: no volverá a disparar hasta que algo lo re-arme (o hasta que su condición deje de cumplirse, si lo pones a disparar cada vez). - Un behaviour es una rutina que un personaje ejecuta por su cuenta (camina aquí, abre aquello, pasea por este pasillo) sin que el jugador intervenga.
Encadena las tres y tienes la escena del timbre: el click arranca el timer, el timer activa la trampa, el vigía lanza la reacción.
El ciclo, dibujado#
Este es el montaje real de nuestro demo tributo a Maniac Mansion: Weird Ed, arriba en su cuarto, reaccionando al timbre de la puerta principal:
El ejemplo, pieza por pieza#
Tres editores, una pieza cada uno:
1. El hotspot del timbre (Editor de Salas): la reacción use toca un campaneo y le da
cuerda al timer. Eso es todo lo que el hotspot sabe; no tiene idea de que Ed existe.
SOUND:assets/audio/sfx/doorbell_chime.ogg
STARTTIMER:doorbell_timer|10
2. El vigía (Editor de Puzzles, pestaña 🌐 Vigías Globales): global, porque lo más probable es que el jugador ya se haya ido a otra sala cuando el timer suene, y un vigía global escucha en todas partes.
id: weird_ed_reacts_to_doorbell
when: timerExpired · doorbell_timer
do: CLEARTIMER:doorbell_timer
CUTSCENE:weird_ed_hears_doorbell
3. La cinemática (Editor de Cinemáticas, bucket 🌐 Global): Ed reacciona, camina hasta su puerta y devuelve el control. Ahí entra su behaviour: una rutina que lo lleva escaleras abajo, sala por sala, hasta abrir la puerta principal. El paso final es el que hace todo repetible:
SWITCHCHAR:weird_ed
SAY:weird_ed|"¡Ah! ¡El timbre!"
WAIT:1
SAY:weird_ed|"¡A lo mejor es mi paquete!"
WALKNPCTO:weird_ed|1348|358
SWITCHCHAR:{_prevChar}
BEHAVIOUR:weird_ed|weird_ed_notices_doorbell
CLEARWATCHER:weird_ed_reacts_to_doorbell
Toca el timbre otra vez cuando Ed ya volvió a su cuarto, y la misma mecha de diez segundos arde hacia la misma reacción. Cada vuelta del ciclo se reinicia sola.
Las dos reglas de oro#
Las dos nos mordieron construyendo el demo, así que son sabiduría ganada a pulso:
Un timer expirado se queda "expirado" hasta que lo limpies. Al sonar, el timer no desaparece: se queda en su estado de sonado, y una condición
timerExpiredsigue respondiendo sí para siempre. Haz queCLEARTIMERsea el primer efecto del vigía que lo escucha, o la condición de ese vigía queda verdadera permanentemente.
Un vigía no puede re-armarse desde sus propios efectos. El motor marca al vigía como disparado después de correr su lista de efectos, así que un
CLEARWATCHERapuntándose a sí mismo dentro de su propiododes-marca algo que todavía no está marcado: un no-op, y el vigía queda muerto. Pon elCLEARWATCHERal final de la cinemática (o cadena de efectos) que el vigía lanzó. Efecto secundario bonito: mientras la reacción sigue corriendo, el vigía sigue desarmado, así que machacar el timbre no puede apilar cinco Eds.
Un vigía que dispara cada vez#
Disparar una sola vez es lo de fábrica, y para un momento de la historia es lo que quieres. Cuando un vigía tiene que reaccionar cada vez que su condición se cumple (una barra que se muestra siempre que el cursor llega al borde, una lámpara que se prende siempre que el jugador pisa una zona), pon Dispara en Cada vez que se cumple en su tarjeta del Editor de Puzzles.
Igual dispara una vez cuando su condición se vuelve verdadera, y después se queda quieto
mientras siga cumpliéndose, así que nunca se repite en cada frame. Lo que cambia es el
re-armado: en cuanto la condición deja de cumplirse, el vigía se re-arma solo, y la siguiente
vez que se cumpla vuelve a disparar. Sin CLEARWATCHER.
Dos cosas a tener en cuenta:
- Se re-arma mirando la condición, no lo que lanzó. Si el vigía arranca una cinemática y
la condición se vuelve a cumplir mientras todavía corre, dispara otra vez. Por eso el timbre
de arriba se queda en Una vez con su
CLEARWATCHERal final de la cinemática: Ed tiene que terminar antes de que el timbre vuelva a contar. - Un vigía con
timerExpiredsigue necesitando suCLEARTIMER. Un timer expirado deja la condición en verdadero (regla uno), así que el vigía nunca la vería volverse falsa y nunca se re-armaría.
Un timer como puerta de cooldown#
Un timer no es solo la mecha de un vigía; también puedes preguntar si sigue corriendo y usar eso como puerta. Dos condiciones leen el mismo timer desde extremos opuestos:
timerExpired: verdadera solo después de que el timer sonó (y se queda verdadera hasta que lo limpies conCLEARTIMER, según la primera regla de oro de arriba).timerRunning: verdadera mientras el timer sigue contando, y falsa en el instante en que se acaba, o antes de que siquiera arranque.
Eso convierte un cooldown en una sola línea. Arranca un timer como ventana de "enfriamiento",
luego protege la acción con timerRunning:
- Mientras
timerRunning→ no hagas nada (o di "todavía no"). La ventana sigue abierta. - De lo contrario → haz la cosa, y arranca el timer de nuevo. Eso reabre la ventana y bloquea una repetición hasta que se consuma.
Sin flag compañero, sin vigía que se re-arme: el timer mismo es la memoria de "hecho hace
poco". Usa timerExpired cuando quieras un disparo diferido de una sola vez (el timbre); usa
timerRunning cuando quieras suprimir algo durante una ventana: un botón que no se puede
machacar, un NPC que no repite una línea por un rato.
Un vigía que sigue al cursor#
Hay interfaz que solo aparece cuando el jugador va a buscarla: una barra de verbos que se muestra cuando el cursor toca el borde de abajo, un panel que baja desde arriba, una flecha de salida en un costado. Dos condiciones leen dónde está el cursor en este momento:
cursorAtEdge: verdadera mientras el cursor está en un borde de la pantalla (arriba, abajo, izquierda o derecha), dentro de una franja de pocos píxeles: 4, salvo que le pongas otro grosor. Pasarse del borde también cuenta, así que las barras negras alrededor de la imagen, cuando la ventana no tiene la forma de tu juego, cuentan como ese borde.cursorInRect: verdadera mientras el cursor está dentro de un rectángulo: la x y la y de su esquina superior izquierda, y después su ancho y su alto. Son píxeles de pantalla, no del cuarto, así que la cámara nunca mueve el rectángulo.
El mouse y el stick de un control mueven el mismo cursor, así que las dos condiciones funcionan con cualquiera. Una pantalla táctil no tiene un cursor apoyado en ningún lado, así que ahí las dos quedan en falso: dale a quien juega con el dedo otra forma de llegar, como un botón.
"Mostrarla en el borde y esconderla cuando el cursor se va" lleva dos vigías, los dos puestos a disparar cada vez:
id: bar_show
when: cursorAtEdge · bottom
dispara: Cada vez que se cumple
do: SHOWGUI
id: bar_hide
when: NOT cursorAtEdge · bottom · 60
dispara: Cada vez que se cumple
do: HIDEGUI
Dos detalles hacen que funcione:
- Cada vigía se re-arma solo.
bar_showse re-arma en cuanto el cursor se va del borde, ybar_hideen cuanto vuelve a entrar a la franja, así que el par sigue funcionando todas las veces que el jugador vaya y venga. - La franja para esconder es más gruesa que la de mostrar.
bar_hideespera a que el cursor salga de los 60 píxeles de abajo, no solo del borde de 4, así que la barra no se mueve mientras el jugador pasa el cursor por encima. Pon ahí la altura de tu barra.
Si armaste esta receta antes de que existiera Dispara, cada vigía re-armaba al otro con un
CLEARWATCHER al final de sus efectos. Esa versión sigue funcionando tal como está.
Al arrancar el juego el cursor todavía no está en el borde, así que bar_hide dispara de
inmediato y la barra empieza escondida, que es lo que quieres. SHOWGUI y HIDEGUI muestran y
esconden todos los GUIs en pantalla a la vez.
Para la barra principal hay una opción integrada que además la desliza y la sostiene mientras hay un ítem en la mano: Barra que se asoma al borde en el Editor de GUIs. Usa la receta con vigías cuando lo que tiene que aparecer es otra cosa: un panel, una flecha de salida, una pista.
Cuando algo no dispara#
- "Mi vigía solo funciona una vez." Disparó y se desarmó solo. Así viene de fábrica.
Si tiene que disparar cada vez que su condición se cumple, pon Dispara en Cada vez
que se cumple. Si tiene que esperar a que termine su reacción, déjalo en Una vez y
agrega un
CLEARWATCHER:<id>al final de la reacción que lanza (ver regla dos). - "Mi reacción se repite cada frame." El vigía se está re-armando mientras su
condición sigue siendo verdadera: casi siempre un
timerExpiredsinCLEARTIMER. Limpia el timer en los efectos del propio vigía (ver regla uno). - "El segundo timbrazo no hace nada." El timer sí está arrancando de nuevo
(
STARTTIMERsiempre lo rebobina); el que sigue desarmado es el vigía. Revisa que el pasoCLEARWATCHERde verdad corra: debe estar en un camino que la reacción siempre alcance, como el último paso de la cinemática. - "El timer no suena nunca." Revisa que su
STARTTIMERlleve de verdad una duración. Segundos arranca vacío y nada te obliga a rellenarlo, así que es fácil dejar un timer con nombre y sin número. UnSTARTTIMERsin duración se ignora: no arranca ningún timer, así quetimerExpirednunca se vuelve cierto y nada de lo que cuelga de él corre.
A dónde ir después#
- Editor de Puzzles: donde se editan vigías y vigías globales, con el catálogo completo de condiciones.
- Editor de Cinemáticas: la edición de las listas de pasos que lanzan reacciones como esta.
- Effect tokens: la tabla de referencia de
STARTTIMER,CLEARTIMER,CLEARWATCHER,BEHAVIOURy todo lo demás que ofrece el widget de efectos.