Zum Hauptinhalt springen

Code-Frameworks

Schnellstart: dein erstes Code-Framework

Schreibe ein kleines Spiel mit einem Charakter, zwei Aktionen und einem Bogen. Prüfe es dann und erstelle einen Charakter in The Forge.

Bevor du anfängst

  • Du brauchst einen bezahlten Tarif, um ein Spiel zu erstellen.
  • Erstelle in The Forge ein privates Spiel. Wähle „EIGENES SPIEL ERSTELLEN“, dann „Manueller Editor“ und erstelle das Spiel.
  • Schreibe die Dateien in einem beliebigen Editor und füge sie dann in den Quellcode-Editor ein, oder tippe sie direkt dort.

Das Spiel

Lantern Keepers hat einen Charakter, den Keeper. Ein Keeper hat einen Namen, maximale TP und aktuelle TP. Zwei Aktionen ändern die TP: Schaden nehmen und Heilen. Heilen geht nie über das Maximum.

Schritt 1: das Manifest

Das Manifest nennt das Projekt und listet jedes Framework und jede Szenariodatei auf. Behalte die projectId, die der Editor für dein Spiel erstellt hat. Der Wert in diesem Beispiel ist nur ein Platzhalter.

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. Zeile 3 Behalte die projectId, die der Editor für dein Spiel erstellt hat. Diese hier ist ein Platzhalter.
  2. Zeilen 4-5 GAME ist ein vollständiges Spiel mit einem spielbaren Charakter. native-game-v1 ist das einzige Profil.
  3. Zeile 7 Das Framework, das ein Spieler zuerst erstellt.
  4. Zeilen 8-9 Jede Framework-Datei und jede Szenario-Datei steht hier.

Schritt 2: das Framework

Eine Framework-Datei deklariert, was der Spieler eingibt (Eingaben), was der Keeper tun kann (Aktionen) und welcher Frame ihn zeichnet. Der Konstruktor und jede Aktion verweisen auf eine Regeldatei.

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. Zeilen 2-5 Wer das Framework ist: seine id, sein Anzeigename, die Art von Element, die es baut, und die Regeldatei, die eines baut.
  2. Zeilen 6-9 Eingaben, die der Spieler ausfüllt. Jeder Schlüssel passt zu einem Parameter des Konstruktors.
  3. Zeilen 10-13 Aktionen. Jede verweist auf eine Regeldatei.
  4. Zeilen 14-16 Die Frames, die einen Keeper zeichnen. Genau einer ist der Standard.

Schritt 3: der Konstruktor

Der Konstruktor macht aus den Eingaben die Startwerte. Sein Rückgabetyp listet jedes gespeicherte Feld auf. Ein neuer Keeper startet mit vollen TP.

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. Zeilen 1-4 Die Eingaben und die Felder, die der Konstruktor zurückgibt.
  2. Zeile 5 Die Startwerte. Ein neuer Keeper startet mit vollen HP.

Schritt 4: die Aktionen

Eine Aktion nimmt zuerst das Element, dann ihre eigenen Eingaben. Sie ändert Werte nur über das Ledger. Schaden nehmen addiert einen negativen Betrag. Benutzt ein Spieler die Schaltfläche einer Aktion mit Eingaben wie amount, fragt Fabled sie in einem Dialog ab.

rules/hurt.ts
export function hurt(self: ElementRef, amount: number): void {
  ledger.add(self, "hp", 0 - amount);
}
  1. Zeile 2 ledger.add ändert hp um einen Betrag. Eine negative Zahl zu addieren zieht ab.

Heilen liest zwei Werte und setzt dann die neuen TP. Es tut nichts, wenn der Keeper schon volle TP hat. In diesem Fall wird nichts geschrieben.

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. Zeilen 2-3 Lies die aktuellen und die maximalen HP. element.var liefert einen Wert, den du mit as number umwandelst.
  2. Zeile 4 Tu nichts, wenn der Keeper schon volle HP hat, dann schreibt dieser Fall nichts.
  3. Zeile 5 Setze hp auf den kleineren Wert aus Maximum und geheiltem Wert.

Schritt 5: der Frame

Der Frame zeichnet den Bogen. Text zeigt Werte mit Bindungen wie {{element.attributes.name}}. Jede Schaltfläche nennt einen Aktions-Alias, den der Datenvertrag deklariert.

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. Zeilen 3-9 metadata benennt den Frame und sagt, zu welchem Framework er gehört.
  2. Zeilen 10-22 dataContract listet die Felder, die der Frame liest, und die Aktionen, die seine Schaltflächen aufrufen dürfen.
  3. Zeilen 23-34 layout ist der Baum, der gezeichnet wird. Text zeigt Felder mit doppelten geschweiften Klammern, und Schaltflächen nennen einen Aktions-Alias.

Schritt 6: die Szenarien

Ein Szenario erstellt einen Keeper und prüft seine Startwerte.

scenarios/new-keeper.json
{
  "id": "new-keeper",
  "framework": "keeper",
  "parameters": { "name": "Mira", "max_hp": 10 },
  "expectedValues": { "name": "Mira", "max_hp": 10, "hp": 10 }
}
  1. Zeile 4 Die Eingaben, mit denen der Keeper gebaut wird.
  2. Zeile 5 Jeder Wert, den der Konstruktor zurückgeben muss.

Dieses Szenario nimmt Schaden, heilt über das Maximum hinaus und heilt noch einmal bei vollen TP. Jeder Schritt listet die genauen Schreibvorgänge auf, die die Aktion machen muss. Der letzte Schritt listet keine auf.

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. Zeilen 7-11 Schritt eins: 6 Schaden nehmen. Die Aktion muss genau einen Schreibvorgang machen, ein ledger.add von -6.
  2. Zeilen 12-16 Schritt zwei: 9 heilen. Das Ergebnis wird auf das Maximum begrenzt, also wird hp auf 10 gesetzt.
  3. Zeile 17 Schritt drei: bei vollen HP noch einmal heilen. Die Schutzprüfung überspringt den Schreibvorgang, also werden keine Schreibvorgänge erwartet.

Bring es in The Forge

  • Öffne dein Spiel in The Forge.
  • Wähle im Schritt „Spielablauf“ die Option „Erweitert: Quellcode und Versionen“, um den Quelltext-Editor zu öffnen.
  • Wenn das Spiel noch kein Projekt hat, wähle „Selbst schreiben“, um mit bearbeitbaren Beispielen zu starten. Wähle nicht „Mit meinem Spiel beginnen“, das ist der KI-Weg. Ersetze die Beispieldateien durch die Dateien oben: Nutze „Datei hinzufügen“ für neue Pfade und „Datei entfernen“ für Dateien, die du nicht brauchst. Behalte die projectId, die der Editor ins Manifest geschrieben hat.
  • Wähle Prüfen und Vorschau. Behebe jedes aufgelistete Problem und prüfe dann erneut.
  • Wähle Projekt speichern.
  • Wähle Mein Spiel vorbereiten. Das erstellt private Frameworks, Frames und Inhalte aus deiner gespeicherten Version.
  • Wähle Einen Charakter in diesem Spiel erstellen und probiere Schaden nehmen und Heilen auf dem Bogen aus.

Mach es absichtlich kaputt

Ändere das Feld, das Heilen schreibt, von hp zu mana. Der Compiler stoppt und benennt das 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));
  }
}

Die Prüfung meldet:

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

Setze den Namen zurück auf hp und die Prüfung besteht. Regeln können nur Felder schreiben, die der Konstruktor zurückgibt.

Weiter