Przejdź do głównej treści

Frameworki kodowe

Rozwiń Lantern w prawdziwą grę

Dodaj do gry z szybkiego startu wybór, wartość pochodną, podrzędny framework i listę, a każdy krok potwierdź scenariuszem.

Zanim zaczniesz

Zacznij od projektu zbudowanego w szybkim starcie. Każdy krok wymienia każdy plik do utworzenia lub zmiany. Fragment pokazuje część, która się zmienia, a możesz go rozwinąć, aby zobaczyć cały plik. Po każdym kroku wybierz Sprawdź i wyświetl.

Cztery kroki

  1. Dodaj wybór

    Dodaj dane wejściowe Calling z dwiema opcjami.

    schemas/keeper.json
        "calling": {
          "label": "Calling",
          "options": ["Guard", "Scout"],
          "defaultValue": "Guard"
        }
    1. Wiersz 11 Stała lista wyborów. Wartość domyślna musi być jednym z nich.

    Konstruktor odbiera nowe dane wejściowe i zwraca je jako zapisane pole.

    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. Wiersz 4 Nowe dane wejściowe.
    2. Wiersze 5-6 calling jest teraz zapisanym polem.

    Oba scenariusze potrzebują nowej wartości, w parameters i w expectedValues. Zacznij od 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. Wiersz 4 Dane wejściowe, które podajesz.
    2. Wiersz 9 Zapisana wartość, której oczekujesz.

    Potem wprowadź te same dwie zmiany w heal.json.

    scenarios/heal.json
      "parameters": { "name": "Mira", "max_hp": 10, "calling": "Guard" },
      "expectedValues": {
        "name": "Mira",
        "max_hp": 10,
        "hp": 10,
        "calling": "Guard"
      },
    1. Wiersz 4 Te same dane wejściowe.
    2. Wiersz 9 Ta sama zapisana wartość.

    Wybierz Sprawdź i wyświetl. Sprawdzenie powinno przejść, zanim ruszysz dalej.

    Projekt zawiera teraz te pliki. Rozwiń dowolny fragment powyżej, aby zobaczyć cały plik.

    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. Wylicz wartość

    Funkcja ledgera wylicza Guard z calling. Zadeklaruj ją w schemacie.

    schemas/keeper.json
      "ledgerFunctions": [
        {
          "id": "guard",
          "name": "Guard",
          "field": "guard",
          "source": "rules/guard.ts"
        }
      ]
    1. Wiersze 24-27 id i name wskazują funkcję, field to zapisana wartość, którą wypełnia, a source to jej plik reguł.

    Utwórz 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. Wiersze 1-3 Funkcja pomocnicza bez export.
    2. Wiersze 5-7 Funkcja ledgera odczytuje calling i zwraca wyliczoną wartość.

    Konstruktor zwraca też pole guard z wartością początkową 0. Funkcja ledgera wpisuje prawdziwą wartość.

    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. Wiersze 5-6 guard jest częścią zwracanej wartości i zaczyna od 0.

    Scenariusz sprawdza tę funkcję. Dodaj guard do oczekiwanych wartości w new-keeper.json oraz linię ledgera, która wywołuje funkcję.

    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. Wiersz 10 Wyliczona wartość, po uruchomieniu funkcji ledgera.
    2. Wiersz 12 Wywołuje funkcję guard z bieżącą wartością 0 i oczekuje 3.

    Dodaj tę samą oczekiwaną wartość do 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. Wiersz 10 Wyliczona wartość w tym scenariuszu też zostaje 3.

    Aby pokazać Guard na karcie, wpisz go w requiredBindings i dodaj węzeł tekstowy, który go używa.

    layouts/keeper.json
          { "binding": "element.attributes.guard", "expectedType": "number" }
    1. Wiersz 15 Powiązanie dla nowego pola.
    layouts/keeper.json
          { "kind": "text", "text": "Guard: {{element.attributes.guard}}" },
    1. Wiersz 32 Węzeł tekstowy, który pokazuje wartość.

    Wybierz Sprawdź i wyświetl. Sprawdzenie powinno przejść, zanim ruszysz dalej.

    Projekt zawiera teraz te pliki. Rozwiń dowolny fragment powyżej, aby zobaczyć cały plik.

    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. Dodaj podrzędny framework

    Keeper nosi Lamp. Lamp to drugi framework z własnym schematem, konstruktorem, ramką i scenariuszem. Wpisz jego schemat i scenariusz w manifeście.

    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. Wiersz 8 Schemat Lamp dołącza do schematu Keeper.
    2. Wiersze 9-13 Scenariusz Lamp dołącza do scenariuszy Keepera.

    Utwórz schemat 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. Wiersze 6-9 Dane wejściowe elementu podrzędnego. On też potrzebuje name.
    2. Wiersz 10 Element podrzędny ma własną ramkę.

    Utwórz jego konstruktor, ramkę i scenariusz.

    rules/lamp.ts
    export function construct(name: string, oil: number): { name: string; oil: number } {
      return { name: name, oil: oil };
    }
    1. Wiersz 1 Konstruktor zwraca dane wejściowe jako zapisane wartości.
    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. Wiersze 11-14 Każde pole, które pokazuje ramka, jest wymienione jako powiązanie.
    scenarios/new-lamp.json
    {
      "id": "new-lamp",
      "framework": "lamp",
      "parameters": { "name": "Lantern", "oil": 5 },
      "expectedValues": { "name": "Lantern", "oil": 5 }
    }
    1. Wiersz 4 Buduje samodzielnie Lamp i sprawdza jego wartości.

    W Keeperze ukryte dane wejściowe przechowują odwołanie do frameworka, a childFrameworks przypisuje miejsce do frameworka.

    schemas/keeper.json
        "lamp": { "label": "Lamp", "defaultValue": "lamp", "hidden": true }
    1. Wiersz 14 Ukryte dane wejściowe nigdy nie pojawiają się w kreatorze.
    schemas/keeper.json
      "childFrameworks": { "lamp": "lamp" }
    1. Wiersz 31 Przypisuje miejsce lamp do frameworka o id lamp.

    Konstruktor zwraca dane wejściowe Lamp obok własnych wartości.

    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. Wiersz 5 lamp to FrameworkRef. Platforma zamienia na niego id frameworka z danych wejściowych.
    2. Wiersze 6-9 Typ zwracany wymienia zapisane wartości i miejsca podrzędne.
    3. Wiersz 12 Miejsce podrzędne: który framework zbudować i jakie dane wejściowe mu dać.

    Dodaj do scenariuszy Keepera dane wejściowe lamp i oczekuj Lamp, który buduje konstruktor.

    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. Wiersz 8 Ukryte dane wejściowe.
    2. Wiersz 18 Element podrzędny, który buduje konstruktor, razem z jego wartościami.
    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. Wiersz 8 Ukryte dane wejściowe w heal.json.
    scenarios/heal.json
      "expectedChildren": { "lamp": { "name": "Lantern", "oil": 5 } }
    1. Wiersz 30 Oczekiwany element podrzędny w heal.json.

    Wybierz Sprawdź i wyświetl. Sprawdzenie powinno przejść, zanim ruszysz dalej.

    Projekt zawiera teraz te pliki. Rozwiń dowolny fragment powyżej, aby zobaczyć cały plik.

    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. Dodaj listę i odrzuconą akcję

    Lista przechowuje treści, które postać zdobywa w grze. Pack przechowuje Trinket, czyli drugi framework, którego presety są treścią. Wpisz jego schemat i dwa scenariusze w manifeście.

    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. Wiersze 8-12 Schemat Trinket dołącza do pozostałych dwóch.
    2. Wiersze 17-18 Scenariusz Trinket i scenariusz pack.

    Utwórz schemat Trinket. Jego ramka potrzebuje id, którego nie używa żadna inna ramka w grze.

    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. Wiersz 8 Waga z ograniczeniami. Oferta ją odczytuje.
    2. Wiersze 10-16 Trinket ma własną ramkę.
    3. Wiersze 17-28 Presety, które gracz może spakować. Scenariusz wybiera je po id.

    Utwórz jego konstruktor, ramkę i scenariusz.

    rules/trinket.ts
    export function construct(
      name: string,
      weight: number,
    ): { name: string; weight: number } {
      return { name: name, weight: weight };
    }
    1. Wiersz 5 Zwraca dwie zapisane wartości.
    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. Wiersze 22-23 Pokazuje nazwę i wagę.
    scenarios/new-trinket.json
    {
      "id": "new-trinket",
      "framework": "trinket",
      "parameters": { "name": "Rope", "weight": 2 },
      "expectedValues": { "name": "Rope", "weight": 2 }
    }
    1. Wiersz 4 Buduje samodzielnie Trinket i sprawdza jego wartości.

    Zadeklaruj akcję i listę w schemacie 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. Wiersze 19-23 Nowa akcja.
    schemas/keeper.json
      "lists": { "pack": { "label": "Pack", "of": "trinket", "max": 4 } }
    1. Wiersz 37 Lista Pack: jej etykieta, framework treści, który przechowuje, i jej rozmiar.

    Utwórz plik reguł akcji.

    rules/pack-trinket.ts
    export function pack_trinket(self: ElementRef, trinket: ContentRef): void {
      list.add(self, "pack", trinket, offer.where({ weight: { lte: 3 } }));
    }
    1. Wiersz 1 Wybrany trinket przychodzi jako ContentRef.
    2. Wiersz 2 Oferta decyduje, co można spakować. Tylko trinkety ważące 3 lub mniej.

    Oferta może odmówić, więc przetestuj oba wyniki. Utwórz 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. Wiersze 19-24 Przyjęty: lina jest dość lekka, więc zostaje dodana.
    2. Wiersze 25-31 Odrzucony: kowadło jest za ciężkie. Krok oczekuje odmowy NOT_OFFERED i braku zapisów.
    3. Wiersz 33 Po ostatnim kroku Pack zawiera tylko linę.

    Ramka, która pokazuje listę, używa version 2.0. Zmień wersję, zadeklaruj akcję i region w dataContract oraz narysuj przycisk i region.

    layouts/keeper.json
      "version": "2.0",
    1. Wiersz 2 Ramki z listą używają 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. Wiersz 21 Alias nowej akcji.
    2. Wiersz 23 Deklaruje region listy.
    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. Wiersze 37-39 Przycisk, który uruchamia akcję.
    2. Wiersz 42 Region rysuje listę.
    3. Wiersz 44 Lista do narysowania.
    4. Wiersz 45 Pokazywane, gdy lista jest pusta.

    Wybierz Sprawdź i wyświetl. Sprawdzenie powinno przejść, zanim ruszysz dalej.

    Projekt zawiera teraz te pliki. Rozwiń dowolny fragment powyżej, aby zobaczyć cały plik.

    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

Co zbudowałeś

Twój Keeper ma teraz wybór, wartość pochodną, podrzędny framework, listę z odrzuconą akcją i scenariusze, które potwierdzają każdą z tych rzeczy. Przepisy pokazują mniejsze zmiany osobno.