Salta al contenuto principale

Framework in codice

Fai crescere Lantern fino a un vero gioco

Aggiungi una scelta, un valore derivato, un Framework figlio e un elenco al gioco dell'avvio rapido, e dimostra ogni passo con uno scenario.

Prima di iniziare

Parti dal progetto che hai costruito nell'avvio rapido. Ogni passo nomina ogni file da creare o modificare. Un estratto mostra la parte che cambia, e puoi espanderlo per vedere tutto il file. Dopo ogni passo, scegli Verifica e anteprima.

Quattro passi

  1. Aggiungi una scelta

    Aggiungi un input Calling con due opzioni.

    schemas/keeper.json
        "calling": {
          "label": "Calling",
          "options": ["Guard", "Scout"],
          "defaultValue": "Guard"
        }
    1. Riga 11 Un elenco fisso di scelte. Il valore predefinito deve essere una di esse.

    Il costruttore riceve il nuovo input e lo restituisce come campo salvato.

    rules/keeper.ts
    export 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 };
    }
    1. Riga 4 Il nuovo input.
    2. Righe 5-6 calling ora è un campo salvato.

    Entrambi gli scenari hanno bisogno del nuovo valore, in parameters e in expectedValues. Parti da 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"
      }
    }
    1. Riga 4 L'input che dai.
    2. Riga 9 Il valore salvato che ti aspetti.

    Poi fai le stesse due modifiche in heal.json.

    scenarios/heal.json
      "parameters": { "name": "Mira", "max_hp": 10, "calling": "Guard" },
      "expectedValues": {
        "name": "Mira",
        "max_hp": 10,
        "hp": 10,
        "calling": "Guard"
      },
    1. Riga 4 Lo stesso input.
    2. Riga 9 Lo stesso valore salvato.

    Scegli Verifica e anteprima. Deve passare prima di andare avanti.

    Ora il progetto contiene questi file. Espandi un estratto qui sopra per vedere tutto il suo file.

    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
  2. Ricava un valore

    Una funzione del registro ricava Guard dal calling. Dichiarala nello schema.

    schemas/keeper.json
      "ledgerFunctions": [
        {
          "id": "guard",
          "name": "Guard",
          "field": "guard",
          "source": "rules/guard.ts"
        }
      ]
    1. Righe 24-27 id e name identificano la funzione, field è il valore salvato che riempie e source è il suo file di regole.

    Crea rules/guard.ts.

    rules/guard.ts
    function 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);
    }
    1. Righe 1-3 Una funzione di supporto senza export.
    2. Righe 5-7 La funzione del registro legge il calling e restituisce il valore derivato.

    Il costruttore restituisce anche un campo guard, con valore iniziale 0. La funzione del registro inserisce il valore vero.

    rules/keeper.ts
    export 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 };
    }
    1. Righe 5-6 guard fa parte del valore restituito, e parte da 0.

    Uno scenario controlla la funzione. Aggiungi guard ai valori attesi di new-keeper.json e una riga del registro che chiama la funzione.

    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 }]
    }
    1. Riga 10 Il valore derivato, dopo l'esecuzione della funzione del registro.
    2. Riga 12 Chiama la funzione guard con il valore attuale 0 e si aspetta 3.

    Aggiungi lo stesso valore atteso 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
      },
    1. Riga 10 Anche in questo scenario il valore derivato resta 3.

    Per mostrare Guard sulla scheda, elencalo in requiredBindings e aggiungi un nodo di testo che lo usa.

    layouts/keeper.json
          { "binding": "element.attributes.guard", "expectedType": "number" }
    1. Riga 15 Il collegamento per il nuovo campo.
    layouts/keeper.json
          { "kind": "text", "text": "Guard: {{element.attributes.guard}}" },
    1. Riga 32 Un nodo di testo che mostra il valore.

    Scegli Verifica e anteprima. Deve passare prima di andare avanti.

    Ora il progetto contiene questi file. Espandi un estratto qui sopra per vedere tutto il suo file.

    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
  3. Aggiungi un Framework figlio

    Un Keeper porta con sé una Lamp. La Lamp è un secondo Framework, con il suo schema, il suo costruttore, il suo Frame e il suo scenario. Elenca il suo schema e il suo scenario nel manifest.

    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"
      ]
    }
    1. Riga 8 Lo schema della Lamp si aggiunge a quello del Keeper.
    2. Righe 9-13 Lo scenario della Lamp si aggiunge a quelli del Keeper.

    Crea lo schema della 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 }]
    }
    1. Righe 6-9 Gli input del figlio. Anche un figlio ha bisogno di un nome.
    2. Riga 10 Il figlio ha il suo Frame.

    Crea il suo costruttore, il suo Frame e il suo scenario.

    rules/lamp.ts
    export function construct(name: string, oil: number): { name: string; oil: number } {
      return { name: name, oil: oil };
    }
    1. Riga 1 Il costruttore restituisce gli input come valori salvati.
    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}}" }
        ]
      }
    }
    1. Righe 11-14 Ogni campo che il Frame mostra è elencato come collegamento.
    scenarios/new-lamp.json
    {
      "id": "new-lamp",
      "framework": "lamp",
      "parameters": { "name": "Lantern", "oil": 5 },
      "expectedValues": { "name": "Lantern", "oil": 5 }
    }
    1. Riga 4 Costruisce una Lamp da sola e ne controlla i valori.

    Nel Keeper, un input nascosto contiene il riferimento al Framework e childFrameworks associa lo slot al Framework.

    schemas/keeper.json
        "lamp": { "label": "Lamp", "defaultValue": "lamp", "hidden": true }
    1. Riga 14 Un input nascosto non compare mai nella procedura guidata.
    schemas/keeper.json
      "childFrameworks": { "lamp": "lamp" }
    1. Riga 31 Associa lo slot lamp al Framework con id lamp.

    Il costruttore restituisce gli input della Lamp accanto ai propri valori.

    rules/keeper.ts
    export 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 } } },
      };
    }
    1. Riga 5 lamp è un FrameworkRef. La piattaforma trasforma in uno di questi l'id del Framework preso dall'input.
    2. Righe 6-9 Il tipo di ritorno elenca i valori salvati e gli slot figli.
    3. Riga 12 Lo slot figlio: quale Framework costruire e gli input da dargli.

    Dai agli scenari del Keeper l'input lamp e aspettati la Lamp che il costruttore costruisce.

    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 } }
    }
    1. Riga 8 L'input nascosto.
    2. Riga 18 Il figlio che il costruttore costruisce, con i suoi valori.
    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
      },
    1. Riga 8 L'input nascosto in heal.json.
    scenarios/heal.json
      "expectedChildren": { "lamp": { "name": "Lantern", "oil": 5 } }
    1. Riga 30 Il figlio atteso in heal.json.

    Scegli Verifica e anteprima. Deve passare prima di andare avanti.

    Ora il progetto contiene questi file. Espandi un estratto qui sopra per vedere tutto il suo file.

    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
  4. Aggiungi un elenco e un'azione rifiutata

    Un elenco contiene i contenuti che un personaggio ottiene giocando. Il Pack contiene Trinket, un secondo Framework i cui preset sono i contenuti. Elenca il suo schema e i suoi due scenari nel manifest.

    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"
      ]
    }
    1. Righe 8-12 Lo schema del Trinket si aggiunge agli altri due.
    2. Righe 17-18 Lo scenario del Trinket e lo scenario pack.

    Crea lo schema del Trinket. Il suo Frame ha bisogno di un id che nessun altro Frame del gioco usa.

    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 }
        }
      ]
    }
    1. Riga 8 Un peso con dei limiti. L'offerta lo legge.
    2. Righe 10-16 Il Trinket ha il suo Frame.
    3. Righe 17-28 I preset che un giocatore può mettere nello zaino. I loro id sono ciò che uno scenario sceglie.

    Crea il suo costruttore, il suo Frame e il suo scenario.

    rules/trinket.ts
    export function construct(
      name: string,
      weight: number,
    ): { name: string; weight: number } {
      return { name: name, weight: weight };
    }
    1. Riga 5 Restituisce i due valori salvati.
    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}}" }
        ]
      }
    }
    1. Righe 22-23 Mostra il nome e il peso.
    scenarios/new-trinket.json
    {
      "id": "new-trinket",
      "framework": "trinket",
      "parameters": { "name": "Rope", "weight": 2 },
      "expectedValues": { "name": "Rope", "weight": 2 }
    }
    1. Riga 4 Costruisce un Trinket da solo e ne controlla i valori.

    Dichiara l'azione e l'elenco nello schema 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"
        }
      ],
    1. Righe 19-23 La nuova azione.
    schemas/keeper.json
      "lists": { "pack": { "label": "Pack", "of": "trinket", "max": 4 } }
    1. Riga 37 L'elenco Pack: la sua etichetta, il Framework di contenuti che contiene e la sua dimensione.

    Crea il file di regole dell'azione.

    rules/pack-trinket.ts
    export function pack_trinket(self: ElementRef, trinket: ContentRef): void {
      list.add(self, "pack", trinket, offer.where({ weight: { lte: 3 } }));
    }
    1. Riga 1 Il trinket scelto arriva come ContentRef.
    2. Riga 2 L'offerta decide cosa si può mettere nello zaino. Solo i trinket che pesano 3 o meno.

    Un'offerta può rifiutare, quindi testa entrambi i risultati. 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"] }
    }
    1. Righe 19-24 Accettato: la rope è abbastanza leggera, quindi viene aggiunta.
    2. Righe 25-31 Rifiutato: la anvil è troppo pesante. Il passo si aspetta il rifiuto NOT_OFFERED e nessuna scrittura.
    3. Riga 33 Dopo l'ultimo passo il Pack contiene solo la rope.

    Un Frame che mostra un elenco usa version 2.0. Cambia la versione, dichiara l'azione e una regione nel dataContract, e disegna il pulsante e la regione.

    layouts/keeper.json
      "version": "2.0",
    1. Riga 2 I Frame con un elenco usano 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" }]
    1. Riga 21 L'alias per la nuova azione.
    2. Riga 23 Dichiara la regione dell'elenco.
    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."
          }
    1. Righe 37-39 Il pulsante che esegue l'azione.
    2. Riga 42 Una regione disegna un elenco.
    3. Riga 44 L'elenco da disegnare.
    4. Riga 45 Mostrato mentre l'elenco è vuoto.

    Scegli Verifica e anteprima. Deve passare prima di andare avanti.

    Ora il progetto contiene questi file. Espandi un estratto qui sopra per vedere tutto il suo file.

    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

Cosa hai costruito

Il tuo Keeper ora ha una scelta, un valore derivato, un Framework figlio, un elenco con un'azione rifiutata e scenari che dimostrano ognuno di questi elementi. Le ricette mostrano modifiche più piccole, una alla volta.