Code frameworks
Haz crecer Lantern hasta un juego de verdad
Añade una elección, un valor derivado, un Framework hijo y una lista al juego del inicio rápido, y prueba cada paso con un escenario.
Antes de empezar
Empieza con el proyecto que creaste en el inicio rápido. Cada paso nombra cada archivo que debes crear o cambiar. Un extracto muestra la parte que cambia, y puedes expandirlo para ver el archivo completo. Después de cada paso, elige Comprobar y previsualizar.
Cuatro pasos
- Añade una elección
Añade una entrada Calling con dos opciones.
schemas/keeper.json"calling": { "label": "Calling", "options": ["Guard", "Scout"], "defaultValue": "Guard" }- Línea 11 Una lista fija de opciones. El valor por defecto debe ser una de ellas.
El constructor recibe la entrada nueva y la devuelve como un campo guardado.
rules/keeper.tsexport function construct( name: string, max_hp: number, calling: string, ): { name: string; max_hp: number; hp: number; calling: string } { return { name: name, max_hp: max_hp, hp: max_hp, calling: calling }; }- Línea 4 La nueva entrada.
- Líneas 5-6 calling ahora es un campo guardado.
Los dos escenarios necesitan el valor nuevo, en parameters y en expectedValues. Empieza con new-keeper.json.
scenarios/new-keeper.json{ "id": "new-keeper", "framework": "keeper", "parameters": { "name": "Mira", "max_hp": 10, "calling": "Guard" }, "expectedValues": { "name": "Mira", "max_hp": 10, "hp": 10, "calling": "Guard" } }- Línea 4 La entrada que das.
- Línea 9 El valor guardado que esperas.
Luego haz los mismos dos cambios en heal.json.
scenarios/heal.json"parameters": { "name": "Mira", "max_hp": 10, "calling": "Guard" }, "expectedValues": { "name": "Mira", "max_hp": 10, "hp": 10, "calling": "Guard" },- Línea 4 La misma entrada.
- Línea 9 El mismo valor guardado.
Elige Comprobar y previsualizar. Debe pasar antes de seguir.
Ahora el proyecto tiene estos archivos. Expande cualquier extracto de arriba para ver su archivo completo.
manifest/project.json schemas/keeper.json rules/heal.ts rules/hurt.ts rules/keeper.ts layouts/keeper.json scenarios/heal.json scenarios/new-keeper.json - Deriva un valor
Una función del ledger deriva Guard a partir de calling. Decláralo en el esquema.
schemas/keeper.json"ledgerFunctions": [ { "id": "guard", "name": "Guard", "field": "guard", "source": "rules/guard.ts" } ]- Líneas 24-27 id y name identifican la función, field es el valor guardado que rellena y source es su archivo de reglas.
Crea rules/guard.ts.
rules/guard.tsfunction base_guard(calling: string): number { return calling === "Guard" ? 3 : 1; } export function guard(currentValue: number, self: ElementRef): number { const calling: string = element.var(self, "calling") as string; return base_guard(calling); }- Líneas 1-3 Una función auxiliar sin export.
- Líneas 5-7 La función del ledger lee calling y devuelve el valor derivado.
El constructor también devuelve un campo guard, con un valor inicial de 0. La función del ledger rellena el valor real.
rules/keeper.tsexport function construct( name: string, max_hp: number, calling: string, ): { name: string; max_hp: number; hp: number; calling: string; guard: number } { return { name: name, max_hp: max_hp, hp: max_hp, calling: calling, guard: 0 }; }- Líneas 5-6 guard es parte del valor de retorno y empieza en 0.
Un escenario comprueba la función. Añade guard a los valores esperados de new-keeper.json, y una línea de ledger que llame a la función.
scenarios/new-keeper.json{ "id": "new-keeper", "framework": "keeper", "parameters": { "name": "Mira", "max_hp": 10, "calling": "Guard" }, "expectedValues": { "name": "Mira", "max_hp": 10, "hp": 10, "calling": "Guard", "guard": 3 }, "ledger": [{ "function": "guard", "currentValue": 0, "expected": 3 }] }- Línea 10 El valor derivado, después de que se ejecutó la función del ledger.
- Línea 12 Llama a la función guard con el valor actual 0 y espera 3.
Añade el mismo valor esperado a heal.json.
scenarios/heal.json"parameters": { "name": "Mira", "max_hp": 10, "calling": "Guard" }, "expectedValues": { "name": "Mira", "max_hp": 10, "hp": 10, "calling": "Guard", "guard": 3 },- Línea 10 El valor derivado también se queda en 3 en este escenario.
Para mostrar Guard en la hoja, añádelo a requiredBindings y agrega un nodo de texto que lo use.
layouts/keeper.json{ "binding": "element.attributes.guard", "expectedType": "number" }- Línea 15 El enlace del campo nuevo.
layouts/keeper.json{ "kind": "text", "text": "Guard: {{element.attributes.guard}}" },- Línea 32 Un nodo de texto que muestra el valor.
Elige Comprobar y previsualizar. Debe pasar antes de seguir.
Ahora el proyecto tiene estos archivos. Expande cualquier extracto de arriba para ver su archivo completo.
manifest/project.json schemas/keeper.json rules/guard.ts rules/heal.ts rules/hurt.ts rules/keeper.ts layouts/keeper.json scenarios/heal.json scenarios/new-keeper.json - Añade un Framework hijo
Un Keeper lleva una Lamp. La Lamp es un segundo Framework con su propio esquema, constructor, Frame y escenario. Enumera su esquema y su escenario en el manifiesto.
manifest/project.json{ "formatVersion": "fabled.game-project/v1", "projectId": "game-your-game-id", "projectKind": "GAME", "compilerProfile": "native-game-v1", "name": "Lantern Keepers", "entryFramework": "keeper", "frameworks": ["schemas/keeper.json", "schemas/lamp.json"], "scenarios": [ "scenarios/new-keeper.json", "scenarios/heal.json", "scenarios/new-lamp.json" ] }- Línea 8 El esquema de la Lamp se suma al esquema del Keeper.
- Líneas 9-13 El escenario de la Lamp se suma a los escenarios del Keeper.
Crea el esquema de la Lamp.
schemas/lamp.json{ "id": "lamp", "name": "Lamp", "elementType": "item", "constructor": "rules/lamp.ts", "inputs": { "name": { "label": "Name" }, "oil": { "label": "Oil", "defaultValue": 5 } }, "frames": [{ "id": "card", "source": "layouts/lamp.json", "default": true }] }- Líneas 6-9 Las entradas del hijo. Un hijo también necesita un nombre.
- Línea 10 El hijo tiene su propio Frame.
Crea su constructor, su Frame y su escenario.
rules/lamp.tsexport function construct(name: string, oil: number): { name: string; oil: number } { return { name: name, oil: oil }; }- Línea 1 El constructor devuelve las entradas como valores guardados.
layouts/lamp.json{ "version": "1.0", "metadata": { "name": "Lamp card", "bindingMode": "bound", "frameworkRef": "lamp", "context": "BLADE", "template": "standard-blade" }, "dataContract": { "requiredBindings": [ { "binding": "element.attributes.name", "expectedType": "string" }, { "binding": "element.attributes.oil", "expectedType": "number" } ], "optionalBindings": [], "actions": [], "regions": [] }, "layout": { "kind": "container", "children": [ { "kind": "text", "text": "{{element.attributes.name}}" }, { "kind": "text", "text": "Oil: {{element.attributes.oil}}" } ] } }- Líneas 11-14 Cada campo que muestra el Frame se enumera como un enlace.
scenarios/new-lamp.json{ "id": "new-lamp", "framework": "lamp", "parameters": { "name": "Lantern", "oil": 5 }, "expectedValues": { "name": "Lantern", "oil": 5 } }- Línea 4 Crea una Lamp por sí sola y comprueba sus valores.
En el Keeper, una entrada oculta guarda la referencia al Framework y childFrameworks asigna la ranura al Framework.
schemas/keeper.json"lamp": { "label": "Lamp", "defaultValue": "lamp", "hidden": true }- Línea 14 Una entrada oculta nunca aparece en el asistente.
schemas/keeper.json"childFrameworks": { "lamp": "lamp" }- Línea 31 Asigna la ranura lamp al Framework con el id lamp.
El constructor devuelve las entradas de la Lamp junto a sus propios valores.
rules/keeper.tsexport function construct( name: string, max_hp: number, calling: string, lamp: FrameworkRef, ): { values: { name: string; max_hp: number; hp: number; calling: string; guard: number }; children: { lamp: { framework: FrameworkRef; params: { name: string; oil: number } } }; } { return { values: { name: name, max_hp: max_hp, hp: max_hp, calling: calling, guard: 0 }, children: { lamp: { framework: lamp, params: { name: "Lantern", oil: 5 } } }, }; }- Línea 5 lamp es un FrameworkRef. La plataforma convierte en uno el id del Framework de la entrada.
- Líneas 6-9 El tipo de retorno enumera los valores guardados y las ranuras hijas.
- Línea 12 La ranura hija: qué Framework crear y las entradas que se le dan.
Da a los escenarios del Keeper la entrada lamp, y espera la Lamp que crea el constructor.
scenarios/new-keeper.json{ "id": "new-keeper", "framework": "keeper", "parameters": { "name": "Mira", "max_hp": 10, "calling": "Guard", "lamp": "lamp" }, "expectedValues": { "name": "Mira", "max_hp": 10, "hp": 10, "calling": "Guard", "guard": 3 }, "ledger": [{ "function": "guard", "currentValue": 0, "expected": 3 }], "expectedChildren": { "lamp": { "name": "Lantern", "oil": 5 } } }- Línea 8 La entrada oculta.
- Línea 18 El hijo que crea el constructor, con sus valores.
scenarios/heal.json"parameters": { "name": "Mira", "max_hp": 10, "calling": "Guard", "lamp": "lamp" }, "expectedValues": { "name": "Mira", "max_hp": 10, "hp": 10, "calling": "Guard", "guard": 3 },- Línea 8 La entrada oculta en heal.json.
scenarios/heal.json"expectedChildren": { "lamp": { "name": "Lantern", "oil": 5 } }- Línea 30 El hijo esperado en heal.json.
Elige Comprobar y previsualizar. Debe pasar antes de seguir.
Ahora el proyecto tiene estos archivos. Expande cualquier extracto de arriba para ver su archivo completo.
manifest/project.json schemas/keeper.json schemas/lamp.json rules/guard.ts rules/heal.ts rules/hurt.ts rules/keeper.ts rules/lamp.ts layouts/keeper.json layouts/lamp.json scenarios/heal.json scenarios/new-keeper.json scenarios/new-lamp.json - Añade una lista y una acción rechazada
Una lista guarda contenido que un personaje gana al jugar. El Pack guarda Trinkets, un segundo Framework cuyos presets son el contenido. Enumera su esquema y sus dos escenarios en el manifiesto.
manifest/project.json{ "formatVersion": "fabled.game-project/v1", "projectId": "game-your-game-id", "projectKind": "GAME", "compilerProfile": "native-game-v1", "name": "Lantern Keepers", "entryFramework": "keeper", "frameworks": [ "schemas/keeper.json", "schemas/lamp.json", "schemas/trinket.json" ], "scenarios": [ "scenarios/new-keeper.json", "scenarios/heal.json", "scenarios/new-lamp.json", "scenarios/new-trinket.json", "scenarios/pack.json" ] }- Líneas 8-12 El esquema del Trinket se suma a los otros dos.
- Líneas 17-18 El escenario del Trinket y el escenario de pack.
Crea el esquema del Trinket. Su Frame necesita un id que ningún otro Frame del juego use.
schemas/trinket.json{ "id": "trinket", "name": "Trinket", "elementType": "item", "constructor": "rules/trinket.ts", "inputs": { "name": { "label": "Name" }, "weight": { "label": "Weight", "defaultValue": 1, "min": 0, "max": 5 } }, "frames": [ { "id": "trinket-card", "source": "layouts/trinket.json", "default": true } ], "content": [ { "id": "rope", "name": "Rope", "parameters": { "name": "Rope", "weight": 2 } }, { "id": "anvil", "name": "Anvil", "parameters": { "name": "Anvil", "weight": 5 } } ] }- Línea 8 Un peso con límites. La oferta lo lee.
- Líneas 10-16 El Trinket tiene su propio Frame.
- Líneas 17-28 Los presets que un jugador puede guardar. Sus ids son lo que elige un escenario.
Crea su constructor, su Frame y su escenario.
rules/trinket.tsexport function construct( name: string, weight: number, ): { name: string; weight: number } { return { name: name, weight: weight }; }- Línea 5 Devuelve los dos valores guardados.
layouts/trinket.json{ "version": "1.0", "metadata": { "name": "Trinket card", "bindingMode": "bound", "frameworkRef": "trinket", "context": "BLADE", "template": "standard-blade" }, "dataContract": { "requiredBindings": [ { "binding": "element.attributes.name", "expectedType": "string" }, { "binding": "element.attributes.weight", "expectedType": "number" } ], "optionalBindings": [], "actions": [], "regions": [] }, "layout": { "kind": "container", "children": [ { "kind": "text", "text": "{{element.attributes.name}}" }, { "kind": "text", "text": "Weight: {{element.attributes.weight}}" } ] } }- Líneas 22-23 Muestra el nombre y el peso.
scenarios/new-trinket.json{ "id": "new-trinket", "framework": "trinket", "parameters": { "name": "Rope", "weight": 2 }, "expectedValues": { "name": "Rope", "weight": 2 } }- Línea 4 Crea un Trinket por sí solo y comprueba sus valores.
Declara la acción y la lista en el esquema del Keeper.
schemas/keeper.json"actions": [ { "id": "hurt", "name": "Take damage", "source": "rules/hurt.ts" }, { "id": "heal", "name": "Heal", "source": "rules/heal.ts" }, { "id": "pack_trinket", "name": "Pack a trinket", "source": "rules/pack-trinket.ts" } ],- Líneas 19-23 La acción nueva.
schemas/keeper.json"lists": { "pack": { "label": "Pack", "of": "trinket", "max": 4 } }- Línea 37 La lista del Pack: su etiqueta, el Framework de contenido que guarda y su tamaño.
Crea el archivo de reglas de la acción.
rules/pack-trinket.tsexport function pack_trinket(self: ElementRef, trinket: ContentRef): void { list.add(self, "pack", trinket, offer.where({ weight: { lte: 3 } })); }- Línea 1 El trinket elegido llega como un ContentRef.
- Línea 2 La oferta decide qué se puede guardar. Solo los trinkets que pesan 3 o menos.
Una oferta puede rechazar, así que prueba los dos resultados. Crea scenarios/pack.json.
scenarios/pack.json{ "id": "pack-trinkets", "framework": "keeper", "parameters": { "name": "Mira", "max_hp": 10, "calling": "Guard", "lamp": "lamp" }, "expectedValues": { "name": "Mira", "max_hp": 10, "hp": 10, "calling": "Guard", "guard": 3 }, "expectedChildren": { "lamp": { "name": "Lantern", "oil": 5 } }, "actions": [ { "action": "pack_trinket", "parameters": {}, "choose": { "trinket": "rope" }, "expectedWrites": [{ "op": "list.add", "key": "pack", "value": "rope" }] }, { "action": "pack_trinket", "parameters": {}, "choose": { "trinket": "anvil" }, "expectedRefusal": "NOT_OFFERED", "expectedWrites": [] } ], "expectedLists": { "pack": ["rope"] } }- Líneas 19-24 Aceptado: la cuerda es lo bastante ligera, así que se añade.
- Líneas 25-31 Rechazado: el yunque pesa demasiado. El paso espera el rechazo NOT_OFFERED y ninguna escritura.
- Línea 33 Después del último paso, el Pack solo tiene la cuerda.
Un Frame que muestra una lista usa la version 2.0. Cambia la versión, declara la acción y una región en dataContract, y dibuja el botón y la región.
layouts/keeper.json"version": "2.0",- Línea 2 Los Frames con una lista usan la version 2.0.
layouts/keeper.json"actions": [ { "name": "hurt", "functionRef": "hurt" }, { "name": "heal", "functionRef": "heal" }, { "name": "pack_trinket", "functionRef": "pack_trinket" } ], "regions": [{ "name": "pack", "listBinding": "element.lists.pack" }]- Línea 21 El alias de la acción nueva.
- Línea 23 Declara la región de la lista.
layouts/keeper.json{ "kind": "button", "label": "Pack a trinket", "actionRef": "pack_trinket" }, { "kind": "region", "id": "pack_list", "listBinding": "{{element.lists.pack}}", "emptyText": "Your pack is empty." }- Líneas 37-39 El botón que ejecuta la acción.
- Línea 42 Una región dibuja una lista.
- Línea 44 La lista que se dibuja.
- Línea 45 Se muestra mientras la lista está vacía.
Elige Comprobar y previsualizar. Debe pasar antes de seguir.
Ahora el proyecto tiene estos archivos. Expande cualquier extracto de arriba para ver su archivo completo.
manifest/project.json schemas/keeper.json schemas/lamp.json schemas/trinket.json rules/guard.ts rules/heal.ts rules/hurt.ts rules/keeper.ts rules/lamp.ts rules/pack-trinket.ts rules/trinket.ts layouts/keeper.json layouts/lamp.json layouts/trinket.json scenarios/heal.json scenarios/new-keeper.json scenarios/new-lamp.json scenarios/new-trinket.json scenarios/pack.json
Lo que construiste
Tu Keeper ahora tiene una elección, un valor derivado, un Framework hijo, una lista con una acción rechazada y escenarios que prueban cada uno. Las recetas muestran cambios más pequeños por separado.

