Aller au contenu principal

Frameworks de code

Démarrage rapide : votre premier framework de code

Écrivez un petit jeu avec un personnage, deux actions et une fiche. Puis vérifiez-le et créez un personnage dans The Forge.

Avant de commencer

  • Vous avez besoin d'un forfait payant pour créer un Jeu.
  • Créez un jeu privé dans The Forge. Choisissez « CRÉER VOTRE PROPRE JEU », puis « Éditeur manuel », et créez le jeu.
  • Écrivez les fichiers dans n'importe quel éditeur, puis collez-les dans l'éditeur de code source, ou saisissez-les directement dedans.

Le jeu

Lantern Keepers a un seul personnage, le Keeper. Un Keeper a un nom, des PV maximum et des PV actuels. Deux actions changent les PV : Subir des dégâts et Soigner. Soigner ne dépasse jamais le maximum.

Étape 1 : le manifeste

Le manifeste nomme le projet et liste chaque Framework et chaque fichier de scénario. Gardez le projectId que l'éditeur a créé pour votre jeu. La valeur de cet exemple n'est qu'un espace réservé.

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. Ligne 3 Gardez le projectId créé par l'éditeur pour votre jeu. Celui-ci est un espace réservé.
  2. Lignes 4-5 GAME est un jeu complet avec un personnage jouable. native-game-v1 est le seul profil.
  3. Ligne 7 Le Framework qu'un joueur crée en premier.
  4. Lignes 8-9 Chaque fichier de Framework et chaque fichier de scénario est listé ici.

Étape 2 : le Framework

Un fichier de Framework déclare ce que le joueur saisit (les entrées), ce que le Keeper peut faire (les actions) et quel Cadre le dessine. Le constructeur et chaque action pointent vers un fichier de règles.

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. Lignes 2-5 Qui est le Framework : son id, son nom affiché, le type d'Élément qu'il construit et le fichier de règles qui en construit un.
  2. Lignes 6-9 Les entrées que le joueur remplit. Chaque clé correspond à un paramètre du constructeur.
  3. Lignes 10-13 Les actions. Chacune pointe vers un fichier de règles.
  4. Lignes 14-16 Les Cadres qui dessinent un Keeper. Un seul est le Cadre par défaut.

Étape 3 : le constructeur

Le constructeur transforme les entrées en valeurs de départ. Son type de retour liste chaque champ stocké. Un nouveau Keeper commence avec tous ses PV.

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. Lignes 1-4 Les entrées, et les champs que le constructeur retourne.
  2. Ligne 5 Les valeurs de départ. Un nouveau Keeper commence avec tous ses PV.

Étape 4 : les actions

Une action reçoit d'abord l'Élément, puis ses propres entrées. Elle ne modifie les valeurs que par le registre. Subir des dégâts ajoute un montant négatif. Quand un joueur utilise le bouton d'une action qui a des entrées, comme amount, Fabled les demande dans une boîte de dialogue.

rules/hurt.ts
export function hurt(self: ElementRef, amount: number): void {
  ledger.add(self, "hp", 0 - amount);
}
  1. Ligne 2 ledger.add change hp d'un montant. Ajouter un nombre négatif soustrait.

Soigner lit deux valeurs, puis définit les nouveaux PV. Elle ne fait rien quand le Keeper a déjà tous ses PV, donc ce cas n'écrit rien.

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. Lignes 2-3 Lisez les PV actuels et maximum. element.var donne une valeur que vous convertissez avec as number.
  2. Ligne 4 Ne faites rien quand le Keeper a déjà tous ses PV, donc ce cas n'écrit rien.
  3. Ligne 5 Définissez hp sur le plus petit entre le maximum et la valeur soignée.

Étape 5 : le Cadre

Le Cadre dessine la fiche. Le texte affiche des valeurs avec des liaisons de la forme {{element.attributes.name}}. Chaque bouton nomme un alias d'action que le contrat de données déclare.

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. Lignes 3-9 metadata nomme le Cadre et dit à quel Framework il appartient.
  2. Lignes 10-22 dataContract liste les champs que le Cadre lit et les actions que ses boutons peuvent appeler.
  3. Lignes 23-34 layout est l'arbre qui est dessiné. Le texte affiche des champs entre doubles accolades, et les boutons nomment un alias d'action.

Étape 6 : les scénarios

Un scénario construit un Keeper et vérifie ses valeurs de départ.

scenarios/new-keeper.json
{
  "id": "new-keeper",
  "framework": "keeper",
  "parameters": { "name": "Mira", "max_hp": 10 },
  "expectedValues": { "name": "Mira", "max_hp": 10, "hp": 10 }
}
  1. Ligne 4 Les entrées avec lesquelles construire le Keeper.
  2. Ligne 5 Chaque valeur que le constructeur doit retourner.

Ce scénario subit des dégâts, soigne au-delà du maximum, puis soigne à nouveau avec tous les PV. Chaque étape liste les écritures exactes que l'action doit faire. La dernière étape n'en liste aucune.

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. Lignes 7-11 Étape un : subir 6 dégâts. L'action doit faire exactement une écriture, un ledger.add de -6.
  2. Lignes 12-16 Étape deux : soigner 9. Le résultat est plafonné au maximum, donc hp est défini à 10.
  3. Ligne 17 Étape trois : soigner à nouveau avec tous les PV. La garde saute l'écriture, donc aucune écriture n'est attendue.

Mettez-le dans The Forge

  • Ouvrez votre jeu dans The Forge.
  • Dans l'étape « Gameplay », choisissez « Avancé : code source et versions » pour ouvrir l'éditeur de source.
  • Si le jeu n'a pas encore de projet, choisissez « L’écrire moi-même » pour partir d'exemples modifiables. Ne choisissez pas « Commencer à créer mon jeu », qui est la voie de l'IA. Remplacez les fichiers d'exemple par les fichiers ci-dessus : utilisez « Ajouter un fichier » pour les nouveaux chemins et « Supprimer le fichier » pour les fichiers dont vous n'avez pas besoin. Gardez le projectId que l'éditeur a placé dans le manifeste.
  • Choisissez Vérifier et prévisualiser. Corrigez chaque problème listé, puis vérifiez à nouveau.
  • Choisissez Enregistrer le projet.
  • Choisissez Préparer mon jeu. Cela crée des Frameworks, des Cadres et du contenu privés à partir de votre version enregistrée.
  • Choisissez Créer un personnage dans ce jeu et essayez Subir des dégâts et Soigner sur la fiche.

Cassez-le exprès

Changez le champ que Soigner écrit, de hp à mana. Le compilateur s'arrête et nomme le problème.

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));
  }
}

La vérification signale :

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

Remettez le nom hp et la vérification passe. Les règles ne peuvent écrire que les champs que le constructeur renvoie.

Suite