Przejdź do głównej treści

Frameworki kodowe

Szybki start: Twój pierwszy framework kodowy

Napisz małą grę z jedną postacią, dwiema akcjami i kartą. Potem sprawdź ją i utwórz postać w The Forge.

Zanim zaczniesz

  • Aby utworzyć grę, potrzebujesz płatnego planu.
  • Utwórz prywatną grę w The Forge. Wybierz „STWÓRZ WŁASNĄ GRĘ”, potem „Edytor ręczny” i utwórz grę.
  • Napisz pliki w dowolnym edytorze, a potem wklej je do edytora kodu źródłowego albo wpisz je od razu tam.

Gra

Lantern Keepers ma jedną postać, Keepera. Keeper ma imię, maksymalne HP i bieżące HP. Dwie akcje zmieniają HP: Take damage i Heal. Heal nigdy nie podnosi HP ponad maksimum.

Krok 1: manifest

Manifest podaje nazwę projektu i wymienia każdy Framework oraz każdy plik scenariusza. Zachowaj projectId, który edytor utworzył dla Twojej gry. Wartość w tym przykładzie to tylko wzór.

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"],
  "scenarios": ["scenarios/new-keeper.json", "scenarios/heal.json"]
}
  1. Wiersz 3 Zachowaj projectId, który edytor utworzył dla Twojej gry. Ten jest tylko wzorem.
  2. Wiersze 4-5 GAME to pełna gra z postacią, którą można grać. native-game-v1 to jedyny profil.
  3. Wiersz 7 Framework, który gracz tworzy jako pierwszy.
  4. Wiersze 8-9 Tutaj wymieniony jest każdy plik frameworka i każdy plik scenariusza.

Krok 2: framework

Plik frameworka deklaruje, co wpisuje gracz (dane wejściowe), co potrafi Keeper (akcje) i która ramka go rysuje. Konstruktor i każda akcja wskazują plik z regułami.

schemas/keeper.json
{
  "id": "keeper",
  "name": "Keeper",
  "elementType": "character",
  "constructor": "rules/keeper.ts",
  "inputs": {
    "name": { "label": "Name" },
    "max_hp": { "label": "Maximum HP", "defaultValue": 10 }
  },
  "actions": [
    { "id": "hurt", "name": "Take damage", "source": "rules/hurt.ts" },
    { "id": "heal", "name": "Heal", "source": "rules/heal.ts" }
  ],
  "frames": [
    { "id": "sheet", "source": "layouts/keeper.json", "default": true }
  ]
}
  1. Wiersze 2-5 Kim jest framework: jego id, nazwa wyświetlana, rodzaj elementu, który buduje, i plik reguł, który go buduje.
  2. Wiersze 6-9 Dane wejściowe, które wypełnia gracz. Każdy klucz odpowiada parametrowi konstruktora.
  3. Wiersze 10-13 Akcje. Każda wskazuje plik reguł.
  4. Wiersze 14-16 Ramki, które rysują Keepera. Dokładnie jedna jest domyślna.

Krok 3: konstruktor

Konstruktor zamienia dane wejściowe na wartości początkowe. Jego typ zwracany wymienia każde zapisywane pole. Nowy Keeper zaczyna z pełnym HP.

rules/keeper.ts
export function construct(
  name: string,
  max_hp: number,
): { name: string; max_hp: number; hp: number } {
  return { name: name, max_hp: max_hp, hp: max_hp };
}
  1. Wiersze 1-4 Dane wejściowe i pola, które zwraca konstruktor.
  2. Wiersz 5 Wartości początkowe. Nowy Keeper zaczyna z pełnym HP.

Krok 4: akcje

Akcja przyjmuje najpierw element, potem własne dane wejściowe. Zmienia wartości tylko przez ledger. Take damage dodaje wartość ujemną. Gdy gracz użyje przycisku akcji z danymi wejściowymi, takimi jak amount, Fabled prosi o nie w oknie dialogowym.

rules/hurt.ts
export function hurt(self: ElementRef, amount: number): void {
  ledger.add(self, "hp", 0 - amount);
}
  1. Wiersz 2 ledger.add zmienia hp o podaną wartość. Dodanie liczby ujemnej odejmuje.

Heal odczytuje dwie wartości, a potem ustawia nowe HP. Nic nie robi, gdy Keeper ma już pełne HP, więc w tym przypadku nic nie zapisuje.

rules/heal.ts
export function heal(self: ElementRef, amount: number): void {
  const hp: number = element.var(self, "hp") as number;
  const max_hp: number = element.var(self, "max_hp") as number;
  if (hp < max_hp) {
    ledger.set(self, "hp", math.min(max_hp, hp + amount));
  }
}
  1. Wiersze 2-3 Odczytaj bieżące i maksymalne HP. element.var daje wartość, którą rzutujesz przez as number.
  2. Wiersz 4 Nie rób nic, gdy Keeper ma już pełne HP, więc w tym przypadku nic się nie zapisuje.
  3. Wiersz 5 Ustaw hp na mniejszą z dwóch wartości: maksimum albo wyleczone HP.

Krok 5: ramka

Ramka rysuje kartę. Tekst pokazuje wartości za pomocą powiązań w stylu {{element.attributes.name}}. Każdy przycisk wskazuje alias akcji zadeklarowany w kontrakcie danych.

layouts/keeper.json
{
  "version": "1.0",
  "metadata": {
    "name": "Keeper sheet",
    "bindingMode": "bound",
    "frameworkRef": "keeper",
    "context": "BLADE",
    "template": "standard-blade"
  },
  "dataContract": {
    "requiredBindings": [
      { "binding": "element.attributes.name", "expectedType": "string" },
      { "binding": "element.attributes.hp", "expectedType": "number" },
      { "binding": "element.attributes.max_hp", "expectedType": "number" }
    ],
    "optionalBindings": [],
    "actions": [
      { "name": "hurt", "functionRef": "hurt" },
      { "name": "heal", "functionRef": "heal" }
    ],
    "regions": []
  },
  "layout": {
    "kind": "container",
    "children": [
      { "kind": "text", "text": "{{element.attributes.name}}" },
      {
        "kind": "text",
        "text": "HP: {{element.attributes.hp}} / {{element.attributes.max_hp}}"
      },
      { "kind": "button", "label": "Take damage", "actionRef": "hurt" },
      { "kind": "button", "label": "Heal", "actionRef": "heal" }
    ]
  }
}
  1. Wiersze 3-9 metadata nazywa ramkę i mówi, do którego frameworka należy.
  2. Wiersze 10-22 dataContract wymienia pola, które ramka czyta, i akcje, które mogą wywołać jej przyciski.
  3. Wiersze 23-34 layout to drzewo, które jest rysowane. Tekst pokazuje pola w podwójnych nawiasach klamrowych, a przyciski wskazują alias akcji.

Krok 6: scenariusze

Scenariusz buduje Keepera i sprawdza jego wartości początkowe.

scenarios/new-keeper.json
{
  "id": "new-keeper",
  "framework": "keeper",
  "parameters": { "name": "Mira", "max_hp": 10 },
  "expectedValues": { "name": "Mira", "max_hp": 10, "hp": 10 }
}
  1. Wiersz 4 Dane wejściowe, z których powstaje Keeper.
  2. Wiersz 5 Każda wartość, którą musi zwrócić konstruktor.

Ten scenariusz zadaje obrażenia, leczy ponad maksimum i leczy jeszcze raz przy pełnym HP. Każdy krok wymienia dokładne zapisy, które akcja musi wykonać. Ostatni krok nie wymienia żadnych.

scenarios/heal.json
{
  "id": "heal-after-damage",
  "framework": "keeper",
  "parameters": { "name": "Mira", "max_hp": 10 },
  "expectedValues": { "name": "Mira", "max_hp": 10, "hp": 10 },
  "actions": [
    {
      "action": "hurt",
      "parameters": { "amount": 6 },
      "expectedWrites": [{ "op": "ledger.add", "key": "hp", "value": -6 }]
    },
    {
      "action": "heal",
      "parameters": { "amount": 9 },
      "expectedWrites": [{ "op": "ledger.set", "key": "hp", "value": 10 }]
    },
    { "action": "heal", "parameters": { "amount": 3 }, "expectedWrites": [] }
  ]
}
  1. Wiersze 7-11 Krok pierwszy: otrzymaj 6 obrażeń. Akcja musi wykonać dokładnie jeden zapis, ledger.add z wartością -6.
  2. Wiersze 12-16 Krok drugi: ulecz 9. Wynik jest ograniczony do maksimum, więc hp jest ustawione na 10.
  3. Wiersz 17 Krok trzeci: ulecz jeszcze raz przy pełnym HP. Warunek pomija zapis, więc nie oczekujemy żadnych zapisów.

Umieść to w The Forge

  • Otwórz swoją grę w The Forge.
  • W kroku „Rozgrywka” wybierz „Zaawansowane: kod źródłowy i wersje”, aby otworzyć edytor źródeł.
  • Jeśli gra nie ma jeszcze projektu, wybierz „Napiszę samodzielnie”, aby zacząć od przykładów do edycji. Nie wybieraj „Zacznij tworzyć moją grę”, bo to droga przez AI. Zastąp pliki przykładowe plikami z góry: użyj „Dodaj plik” dla nowych ścieżek i „Usuń plik” dla plików, których nie potrzebujesz. Zachowaj projectId, który edytor wpisał do manifestu.
  • Wybierz Sprawdź i wyświetl. Popraw każdy wymieniony problem, a potem sprawdź ponownie.
  • Wybierz Zapisz projekt.
  • Wybierz Przygotuj moją grę. To tworzy prywatne frameworki, ramki i treści z zapisanej wersji.
  • Wybierz Stwórz postać w tej grze i wypróbuj Take damage i Heal na karcie.

Zepsuj to celowo

Zmień pole, które zapisuje Heal, z hp na mana. Kompilator zatrzyma się i nazwie problem.

export function heal(self: ElementRef, amount: number): void {
  const hp: number = element.var(self, "hp") as number;
  const max_hp: number = element.var(self, "max_hp") as number;
  if (hp < max_hp) {
    ledger.set(self, "mana", math.min(max_hp, hp + amount));
  }
}

Sprawdzenie zgłasza:

Action "keeper.heal" calls ledger.set(self, "mana", ...), and "mana" is not declared on this schema. Declared scalar fields: hp, max_hp, name.

Przywróć nazwę hp, a sprawdzenie przejdzie. Reguły mogą zapisywać tylko pola, które zwraca konstruktor.

Dalej