Salto de escenas y cambio de entradas mediante UI
Pulsas un botón → saltas a otra apertura escrita de antemano. Escribes en un cuadro de texto → cambias lo que una entrada le dice a la IA. Esta receta enseña las dos cosas, primero sin código y luego con código de interfaz.
Parte 1: Cambiar de apertura con un botón
Lo que vas a construir
Un mundo con varias aperturas escritas de antemano. El jugador ve primero la apertura «principal», con botones para pulsar. Cuando pulsa uno, el primer mensaje del chat cambia al momento a otra de las aperturas: la IA no genera nada, sale el texto que escribiste tú.
Cómo funciona
Una carta puede tener varias aperturas. Cuando un jugador empieza una sesión nueva, todas se guardan como swipes del primer mensaje (izquierda/derecha para cambiar). Cada apertura lleva además sus propios valores iniciales para las variables.
api.switchGreeting(index) salta directamente a la apertura con ese número; el paso de botón Cambiar de inicio de la interfaz del jugador hace lo mismo sin código.
El jugador pulsa "Entrar en la cueva oscura"
→ el botón cambia a la apertura n.º 2 (el índice empieza en 0, así que 1 = la segunda)
→ las variables vuelven a los valores iniciales de esa apertura
→ El jugador ve la apertura de la cueva oscura que escribisteSin código
- Escribe las aperturas (Paso 1, más abajo)
- En la Interfaz del jugador, pulsa Plantillas y añade Elegir inicio: una tarjeta por cada apertura, y la historia empieza en la que toque el jugador. O pon tú los botones en una página y dale a cada uno un paso Cambiar de inicio
- Para darle a cada ruta su propio lore, sigue los Pasos 2 y 3 de más abajo; tampoco necesitan código
Si al elegir la ruta tiene que pasar algo más (música, un aviso), añade un paso Activar un comportamiento después del paso Cambiar de inicio. Al cambiar, las variables vuelven a los valores iniciales de esa apertura, así que cualquier cambio hecho antes se pierde.
Paso a paso
Paso 1: Escribe las aperturas
En el lienzo, haz clic en la fila de la apertura y escribe la primera. Para añadir otra, pulsa el + de arriba a la derecha del bloque de apertura.
La primera apertura (la principal, que presenta la elección de ruta):
Es lo primero que ve el jugador al abrir la sesión: describe la escena y llévalo hacia una elección:
*Te despiertas en un bosque misterioso. La niebla de la mañana se arremolina entre árboles centenarios.*
Ante ti se separan dos caminos:
**A la izquierda**: un sendero estrecho que se mete en la oscuridad. Aire frío y ecos lejanos.
**A la derecha**: un camino salpicado de sol, con flores silvestres y canto de pájaros.
¿Por cuál vas a ir?La apertura es texto fijo que escribiste tú, no lo genera la IA, así que controlas cada palabra que ve el jugador.
La segunda apertura (la cueva oscura):
*Tomas el camino de la izquierda. Las copas de los árboles se cierran sobre ti y se tragan la luz. En pocos minutos, el sendero se estrecha hasta quedar en una grieta en una pared de roca: la entrada de una cueva.*
*Sale un aire frío que huele a piedra húmeda y a algo metálico. Al fondo parpadea una luz tenue, entre azul y verde: hongos luminosos pegados a las paredes.*
*Respiras hondo y entras. A tu espalda, el último resquicio de luz del día se reduce a una línea pálida y luego desaparece.*
Estás solo en la oscuridad.Este texto solo aparece después de que el jugador pulse «Entrar en la cueva oscura». Antes, el jugador ve la primera apertura.
La tercera apertura (la pradera soleada):
*Eliges el camino de la derecha. Los árboles se van separando y el sol cálido entra a raudales entre las ramas. En pocos minutos, el bosque se abre a una pradera inmensa que llega hasta el horizonte.*
*Flores silvestres de todos los colores se mecen con la brisa. A lo lejos brilla un arroyo. Muy cerca, un pájaro canta una melodía que no habías oído nunca.*
*Notas cómo se te suelta la tensión de los hombros. Sea lo que sea este lugar, aquí te sientes a salvo.*
Bienvenido a la Pradera Siempreflor.El orden de las aperturas es el índice
El orden de las aperturas es el index de switchGreeting(), contando desde 0: la primera apertura es el índice 0 (la que sale por defecto), la segunda es el índice 1 y la tercera, el índice 2. El paso Cambiar de inicio de la interfaz del jugador las enumera como Inicio 1, 2 y 3.
Paso 2: Crea una variable de ruta con un valor por apertura
Una variable apunta en qué ruta está el jugador. Sirve para dos cosas:
- Ocultar los botones cuando ya se ha elegido ruta (el código comprueba si sigue siendo
"none") - Enviar el lore de cada ruta (el Paso 3 hace que el lore dependa de ella)
En el lienzo, pulsa + Variable en la fila Puedes añadir:
| Campo | Qué poner | Por qué |
|---|---|---|
| Nombre de la variable | current_route | El código y las condiciones encuentran la variable por este nombre |
| Tipo | Texto | El valor es texto ("none", "dark", "light") |
| Valor inicial | none | Significa «todavía sin elegir» |
| Acceso de la IA (en Qué hace la IA con ella) | Solo lectura para la IA | Solo la cambian las aperturas; la IA puede verla pero no cambiarla |
Ahora dale a cada ruta su propio valor inicial: arrastra la segunda apertura sobre current_route y ponla en dark, y luego arrastra la tercera apertura y ponla en light. Cuando el jugador cambia a una apertura, current_route toma el valor de esa apertura.
Paso 3: (Opcional) Dale a cada ruta su propio lore
Si quieres que, después de elegir la ruta, las respuestas de la IA usen una ambientación distinta, haz este paso. Si solo quieres cambiar el texto de la apertura, sáltatelo.
Añade dos entradas de lore y haz que cada una sea Lore condicional: arrastra current_route sobre la entrada, haz clic en la línea y pon la condición (current_route es dark para la entrada de la cueva, light para la de la pradera). El lore condicional solo se envía mientras se cumple su condición.
Entrada de lore de la cueva oscura (condición: current_route es dark):
[Ambientación: la Cueva de las Fauces Sombrías]
El jugador está explorando la Cueva de las Fauces Sombrías. Datos clave:
- Antiguas ruinas enanas, abandonadas hace siglos
- Hongos luminosos dan una luz tenue, entre azul y verde
- Criaturas extrañas acechan en los túneles más profundos
- La temperatura baja cuanto más te adentras
Mantén un ambiente tenso de terror y supervivencia. Describe sonidos que resuenan, sombras que parpadean, agua que gotea y el peso opresivo de la roca sobre la cabeza.Entrada de lore de la pradera soleada (condición: current_route es light): describe la ambientación y el ambiente de la pradera.
Antes de que el jugador elija, current_route es none, así que no se envía ninguna de las dos.
Si una ruta tiene mucho lore, variables e IA propias, mejor métela en un escenario y arrastra la apertura sobre el escenario: así ese escenario solo existe en las partidas que empiezan por esa apertura.
Paso 4: Añade los botones de ruta en el código de interfaz
Para poner los botones dentro de la primera burbuja del chat, escríbelos en el código de interfaz. Abre Paneles → Código de frontend → index.tsx y sustituye su contenido por:
export default function MyWorld() {
const api = useYumina();
const hasChosen = api.variables.current_route !== "none";
return (
<Chat renderBubble={(msg) => (
<div>
{/* Render the message text normally */}
<div
style={{ color: "#e2e8f0", lineHeight: 1.7 }}
dangerouslySetInnerHTML={{ __html: msg.contentHtml }}
/>
{/* Route selection buttons */}
{/* msg.messageIndex === 0 means only show on the first message */}
{/* !hasChosen means hide once a choice has been made */}
{msg.messageIndex === 0 && !hasChosen && (
<div style={{
display: "flex",
gap: "12px",
marginTop: "16px",
}}>
<button
onClick={() => api.switchGreeting(1)} // Switch to the 2nd opening
style={{
flex: 1,
padding: "16px",
background: "linear-gradient(135deg, #1e1b4b, #312e81)",
border: "1px solid #4338ca",
borderRadius: "12px",
color: "#c7d2fe",
fontSize: "15px",
fontWeight: "bold",
cursor: "pointer",
}}
>
Enter the Dark Cave
</button>
<button
onClick={() => api.switchGreeting(2)} // Switch to the 3rd opening
style={{
flex: 1,
padding: "16px",
background: "linear-gradient(135deg, #365314, #4d7c0f)",
border: "1px solid #65a30d",
borderRadius: "12px",
color: "#ecfccb",
fontSize: "15px",
fontWeight: "bold",
cursor: "pointer",
}}
>
Walk to the Sunlit Meadow
</button>
</div>
)}
</div>
)} />
);
}Explicación línea a línea:
<Chat renderBubble={...} />: usa la interfaz de chat por defecto de la plataforma (el cuadro de texto, los swipes y los puntos de guardado ya vienen incluidos); tú solo te encargas de cómo se dibujan las burbujasconst api = useYumina(): te da la API de Yumina, con la que puedes leer y escribir variables, activar comportamientos y cambiar de aperturaapi.variables.current_route: lee el valor actual de la variable de rutahasChosen: si no es"none", el jugador ya ha elegidomsg.contentHtml: el HTML ya preparado que pasa renderBubble (con el Markdown ya procesado)msg.messageIndex === 0: muestra los botones solo en el primer mensaje (no en todos)api.switchGreeting(1): cambia el primer mensaje al índice 1 (la segunda apertura). Las variables vuelven a los valores iniciales de esa apertura, así quecurrent_routepasa a"dark", los botones desaparecen y empieza a enviarse el lore de la cueva
Hacer más cosas al pulsar. Si el botón también tiene que ejecutar un comportamiento (poner música, mostrar un aviso, cambiar otros valores), cambia primero de apertura y ejecuta el comportamiento cuando el cambio haya terminado:
onClick={async () => {
await api.switchGreeting(1); // resets variables to opening 2's values
await api.executeActionAndWait("choose-dark"); // a behavior triggered by The player presses a button → Button "choose-dark"
}}Llamar a setVariable o a un comportamiento antes del cambio no sirve: el cambio vuelve a poner los valores iniciales de esa apertura.
¿No quieres escribir el código tú?
Pulsa Asistente IA arriba a la derecha del lienzo y describe los botones que quieres. Te escribe el código.
Paso 5: Guarda y prueba
- Pulsa Guardar en la barra superior
- Pulsa Jugar para empezar una prueba
- Verás la apertura principal con dos botones debajo
- Pulsa «Enter the Dark Cave»: el primer mensaje pasa a ser tu apertura de la cueva y los botones desaparecen
- Envíale unos cuantos mensajes a la IA: si hiciste el Paso 3, las respuestas de la IA siguen el lore de la cueva
- Para probar la otra ruta, empieza una prueba nueva y pulsa el otro botón
Solución de problemas:
| Síntoma | Causa probable | Solución |
|---|---|---|
| No se ven los botones | El código de interfaz no está guardado o tiene un error de sintaxis | Paneles → Código de frontend debería mostrar OK, no Error |
| El botón se pulsa pero la apertura no cambia | No hay suficientes aperturas | Comprueba que la carta tenga 3 aperturas |
| La apertura cambia pero los botones se quedan | La apertura no tiene valor inicial para current_route | Arrastra esa apertura sobre current_route y ponle su valor |
| El lore no cambia | La condición de la entrada no coincide | Haz clic en la línea entre current_route y la entrada y revisa el valor (dark / light) |
| Un valor puesto al pulsar desaparece | Se puso antes del cambio | Espera primero a switchGreeting y cambia los valores después |
Parte 2: Lo que escribe el jugador cambia el contenido de una entrada
Lo que vas a construir
Un cuadro de texto donde el jugador escribe algo (una regla propia, el nombre de un personaje, una indicación para la historia). El texto va a una variable, y una entrada de lore incluye esa variable, así que cambia lo que la IA lee a continuación.
Cómo funciona
Las entradas de lore admiten macros. Escribe {{nombre_de_variable}} en el contenido de una entrada y, cada vez que el motor monta el prompt para la IA, sustituye el marcador por el valor actual de la variable.
Por ejemplo:
- Escribes en una entrada:
Regla especial: {{custom_rule}} - La variable
custom_rulevale"Se permite toda la magia" - En el prompt que recibe la IA, esa línea queda así:
Regla especial: Se permite toda la magia
La sustitución se hace cada vez que se monta el prompt, es decir, cuando el jugador envía su siguiente mensaje y la IA va a responder.
El orden completo:
1. El contenido de la entrada dice: "Regla especial: {{custom_rule}}"
2. Valor actual de la variable custom_rule = "Se permite toda la magia"
3. El jugador envía un mensaje → el motor monta el prompt → sustituye {{custom_rule}} por el valor de la variable
→ la IA recibe "Regla especial: Se permite toda la magia" → la IA responde en consecuencia
4. El jugador escribe "La magia está prohibida" en el cuadro de texto y pulsa "Apply"
5. La variable custom_rule pasa a valer "La magia está prohibida"
→ el valor de la variable se actualiza al momento
6. La IA todavía no lo ve: el prompt no se ha vuelto a montar.
7. El jugador envía otro mensaje → el motor vuelve a montar el prompt → esta vez con el valor nuevo
→ la IA recibe "Regla especial: La magia está prohibida" → la IA empieza a cumplir la regla nuevaLa variable cambia al momento; la IA ve el cambio en el siguiente mensaje.
Sin código
En la Interfaz del jugador, añade una parte Campo (Texto corto o Párrafo) y pon Guardar la respuesta en en custom_rule. La variable se actualiza mientras el jugador escribe. Una parte Texto puede mostrar la regla actual con Insertar el valor de una variable…. Luego haz los Pasos 1 y 2 de abajo; el Paso 3 solo hace falta para un cuadro de texto dentro del chat.
Paso a paso
Paso 1: Crea una variable de texto
Esta variable guarda lo que escribe el jugador. En el lienzo, pulsa + Variable en la fila Puedes añadir:
| Campo | Qué poner | Por qué |
|---|---|---|
| Nombre de la variable | custom_rule | La macro {{custom_rule}} de la entrada busca este nombre |
| Tipo | Texto | El contenido es lo que escriba el jugador |
| Valor inicial | (déjalo vacío, o pon uno por defecto como Se permite toda la magia) | Vacío = una sesión nueva no tiene regla; con texto = una regla de partida |
| Acceso de la IA (en Qué hace la IA con ella) | Solo lectura para la IA | La pone el jugador; la IA la lee pero no puede cambiarla |
Paso 2: Usa la macro en una entrada
Crea una entrada que use {{custom_rule}} como marcador. En el bloque Personaje y mundo del lienzo (se envía en cada turno), pulsa el + de arriba a la derecha del bloque:
| Campo | Qué poner | Por qué |
|---|---|---|
| Nombre | Reglas del mundo | Para que tú la reconozcas |
| Cuándo lo ve la IA | Personaje y mundo (en cada turno) | La regla vale para todas las respuestas |
Contenido:
[Reglas del mundo]
La siguiente regla está en vigor en este mundo y debe respetarse siempre:
{{custom_rule}}Cada vez que el motor monta el prompt, busca
{{...}}en las entradas. Si el nombre de dentro de las llaves coincide con una variable, lo sustituye por el valor actual de la variable.Si la variable está vacía, la línea queda vacía: la IA ve «La siguiente regla está en vigor...» sin nada detrás. Si el valor es «La magia está prohibida», la IA ve «La siguiente regla está en vigor... La magia está prohibida».
Paso 3: Añade un cuadro de texto en el código de interfaz
Esto pone un cuadro de texto debajo del último mensaje del chat, escrito dentro de renderBubble (solo debajo del último mensaje, para que haya un cuadro y no uno por mensaje).
En tu index.tsx, añade lo siguiente. Si ya tienes el código de la Parte 1, añádelo dentro del JSX que devuelve renderBubble, debajo del texto del mensaje:
// Near the top of MyWorld() (outside <Chat>), add these
const api = useYumina(); // If you already have it, don't duplicate
const msgs = api.messages || [];
const [ruleInput, setRuleInput] = React.useState("");
const currentRule = String(api.variables.custom_rule || "");
// Inside renderBubble, add a check
const isLastMsg = msg.messageIndex === msgs.length - 1; // whether this is the last message
// In the JSX renderBubble returns, below the message text
{isLastMsg && (
<div style={{
marginTop: "12px",
padding: "12px",
background: "rgba(30,41,59,0.5)",
borderRadius: "8px",
border: "1px solid #334155",
}}>
<div style={{ fontSize: "12px", color: "#94a3b8", marginBottom: "6px" }}>
World Rule: {currentRule || "(not set)"}
</div>
<div style={{ display: "flex", gap: "8px" }}>
<input
type="text"
value={ruleInput}
onChange={(e) => setRuleInput(e.target.value)}
placeholder="Type a new rule..."
style={{
flex: 1, padding: "6px 10px", background: "#1e293b",
border: "1px solid #475569", borderRadius: "6px",
color: "#e2e8f0", fontSize: "13px", outline: "none",
}}
onKeyDown={(e) => {
if (e.key === "Enter" && ruleInput.trim()) {
api.setVariable("custom_rule", ruleInput.trim());
setRuleInput("");
}
}}
/>
<button
onClick={() => {
if (ruleInput.trim()) {
api.setVariable("custom_rule", ruleInput.trim());
setRuleInput("");
}
}}
style={{
padding: "6px 14px", background: "#4338ca", borderRadius: "6px",
color: "#e0e7ff", fontSize: "13px", fontWeight: "600",
cursor: "pointer", border: "none",
}}
>
Apply
</button>
</div>
</div>
)}Explicación línea a línea:
isLastMsg: muestra el cuadro solo en el último mensaje; si no, saldría uno en cada mensajecurrentRule: lee el valor actual de la variable y lo muestra encima del cuadro, para que el jugador vea la regla vigenteruleInput: estado de React con lo que se está escribiendoonKeyDown: pulsar Enter también lo envía, no solo pulsar el botónapi.setVariable("custom_rule", ...): escribe en la variable el texto del jugador. En la siguiente respuesta de la IA, el{{custom_rule}}de la entrada se sustituye por este textosetRuleInput(""): vacía el cuadro después de enviar
¿Por qué dentro de renderBubble?
Devolver <Chat /> desde index.tsx te da el chat de la plataforma. Para meter elementos interactivos (botones, cuadros de texto) en el chat hay dos formas: 1) ponerlos dentro de <Chat renderBubble={...} />, como aquí, para que se dibujen junto a las burbujas de los mensajes; 2) poner <Chat /> y tu propio componente uno al lado del otro con una disposición flex (para barras laterales). Para una interfaz a pantalla completa sin chat (por ejemplo, una novela visual), prescinde de <Chat /> y escribe tu propia disposición, usando <MessageList /> + <MessageInput /> si te hacen falta.
Paso 4: Guarda y prueba
- Guarda la carta y empieza una prueba con Jugar
- Debajo del último mensaje verás «World Rule: (not set)» y un cuadro de texto
- Escribe «La magia está prohibida» y pulsa «Apply» (o Enter)
- El texto de encima del cuadro cambia a «World Rule: La magia está prohibida»: la variable se ha actualizado
- Ahora envía un mensaje (por ejemplo, «Intento lanzar una bola de fuego»): es en este momento cuando el motor monta el prompt y sustituye
{{custom_rule}}por «La magia está prohibida» - La respuesta de la IA debería reflejar la regla (por ejemplo, «Alzas la mano para lanzar el hechizo, pero notas tu maná bloqueado por una fuerza invisible»)
- Vuelve a cambiar la regla (por ejemplo, a «Solo se permite la magia de fuego») y envía otro mensaje: la IA se adapta
Combinar los dos patrones
Puedes combinar el cambio de apertura y la modificación de entradas. Un ejemplo concreto:
Creación de personaje + apertura de la historia:
- La primera apertura (índice 0) no es la historia: es una pantalla de creación de personaje con campos para el nombre, la clase y el trasfondo
- El jugador la rellena → los campos escriben en variables → las entradas con las macros
{{player_name}},{{player_class}}y{{player_backstory}}recogen los valores - El jugador pulsa «Start Adventure» →
switchGreeting(1)salta a la apertura de verdad de la historia. Al cambiar, las variables vuelven a los valores iniciales de esa apertura, así que guarda las respuestas después del cambio (espera aswitchGreetingy luego usasetVariableopatchVariables) - Desde la primera respuesta de la IA, la IA ya conoce el nombre, la clase y el trasfondo del personaje del jugador
Las plantillas de inicio de la interfaz del jugador (Escribir tu nombre, Ficha de personaje, Elegir origen, Repartir atributos) montan este tipo de pantalla sin código, antes de que empiece la historia.
Referencia rápida
| Lo que quieres | Cómo hacerlo |
|---|---|
| Saltar a una apertura escrita de antemano | Interfaz del jugador: paso Cambiar de inicio o plantilla Elegir inicio. Código: await api.switchGreeting(index) (empieza en 0, en el orden de las aperturas) |
| Valores distintos según la apertura | Arrastra la apertura sobre la variable en el lienzo y ponle su valor inicial |
| Que lo que escribe el jugador cambie cómo se comporta la IA | Variable de texto + {{nombre_de_variable}} en una entrada + un Campo (o setVariable() desde el código) |
| Mostrar botones solo en el primer mensaje | Dentro de <Chat renderBubble>, comprueba msg.messageIndex === 0 |
| Ocultar los botones después de elegir | Guarda la elección en una variable y comprueba hasChosen en el TSX |
| Cambiar el lore después de elegir ruta | Lore condicional sobre la variable de ruta, o un escenario ligado a la apertura |
| Poner un sonido o mostrar un aviso al cambiar | Un comportamiento con Reproducir música / Reproducir efecto de sonido / Mostrar notificación, activado después del cambio |
Pruébalo tú: un mundo de demostración para importar
Descarga este archivo JSON e impórtalo:
Cómo importarlo:
- Pulsa Crear a la izquierda y elige Proyecto en blanco
- Abre Paneles → Ajustes de la carta y luego Importar archivo → Elegir archivo
- Selecciona el archivo
.jsonque descargaste. Al importarlo se sustituye todo lo que hay en esa carta - Pulsa Guardar y luego Jugar para probarlo
Qué incluye:
- 3 aperturas (la principal + la cueva oscura + la pradera soleada)
- 2 variables (
current_routepara seguir la ruta ycustom_rulepara la regla que puede editar el jugador) - 2 comportamientos que se disparan con El jugador pulsa un botón (
choose-dark/choose-light), que ponen la ruta y activan y desactivan el lore de cada ruta - Un renderizador de mensajes (código de interfaz que dibuja cada mensaje) con los botones de ruta y el editor de la regla
- Una entrada de lore que usa la macro
{{custom_rule}}
Los botones de la demo ponen la ruta, ejecutan el comportamiento y después cambian de apertura, todo con un solo clic. El cambio devuelve las variables a los valores iniciales de la apertura, así que puede que la ruta no se quede guardada; los Pasos 2 a 4 de arriba evitan esto.
