Фреймворки на коде
Вырастим Lantern до настоящей игры
Добавьте в игру из быстрого старта выбор, производное значение, дочерний Framework и список, и проверьте каждый шаг сценарием.
Перед началом
Начните с проекта, который вы собрали в быстром старте. Каждый шаг называет все файлы, которые нужно создать или изменить. Отрывок показывает часть, которая меняется, а целый файл можно раскрыть. После каждого шага выберите «Проверить и показать».
Четыре шага
- Добавьте выбор
Добавьте вход Calling с двумя вариантами.
schemas/keeper.json"calling": { "label": "Calling", "options": ["Guard", "Scout"], "defaultValue": "Guard" }- Строка 11 Фиксированный список вариантов. Значение по умолчанию должно быть одним из них.
Конструктор получает новый вход и возвращает его как хранимое поле.
rules/keeper.tsexport 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 }; }- Строка 4 Новый вход.
- Строки 5-6 calling теперь хранимое поле.
Обоим сценариям нужно новое значение, в parameters и в expectedValues. Начните с 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" } }- Строка 4 Вход, который вы передаёте.
- Строка 9 Хранимое значение, которое вы ожидаете.
Затем сделайте те же два изменения в heal.json.
scenarios/heal.json"parameters": { "name": "Mira", "max_hp": 10, "calling": "Guard" }, "expectedValues": { "name": "Mira", "max_hp": 10, "hp": 10, "calling": "Guard" },- Строка 4 Тот же вход.
- Строка 9 То же хранимое значение.
Выберите «Проверить и показать». Проверка должна пройти, прежде чем вы перейдёте дальше.
Теперь в проекте эти файлы. Раскройте любой отрывок выше, чтобы увидеть целый файл.
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 - Выведите значение
Функция журнала выводит Guard из calling. Объявите её в схеме.
schemas/keeper.json"ledgerFunctions": [ { "id": "guard", "name": "Guard", "field": "guard", "source": "rules/guard.ts" } ]- Строки 24-27 id и name определяют функцию, field это хранимое значение, которое она заполняет, а source это файл с её правилами.
Создайте rules/guard.ts.
rules/guard.tsfunction 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-3 Вспомогательная функция без export.
- Строки 5-7 Функция журнала читает calling и возвращает выведенное значение.
Конструктор также возвращает поле guard с начальным значением 0. Функция журнала записывает настоящее значение.
rules/keeper.tsexport 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 }; }- Строки 5-6 guard входит в результат и начинается с 0.
Сценарий проверяет функцию. Добавьте guard в ожидаемые значения new-keeper.json и строку ledger, которая вызывает функцию.
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 }] }- Строка 10 Выведенное значение после работы функции журнала.
- Строка 12 Вызывает функцию guard с текущим значением 0 и ожидает 3.
Добавьте то же ожидаемое значение в 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 },- Строка 10 В этом сценарии выведенное значение тоже остаётся 3.
Чтобы показать Guard на листе, перечислите его в requiredBindings и добавьте текстовый узел, который его использует.
layouts/keeper.json{ "binding": "element.attributes.guard", "expectedType": "number" }- Строка 15 Привязка для нового поля.
layouts/keeper.json{ "kind": "text", "text": "Guard: {{element.attributes.guard}}" },- Строка 32 Текстовый узел, который показывает значение.
Выберите «Проверить и показать». Проверка должна пройти, прежде чем вы перейдёте дальше.
Теперь в проекте эти файлы. Раскройте любой отрывок выше, чтобы увидеть целый файл.
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 - Добавьте дочерний Framework
Keeper носит Lamp. Lamp это второй Framework со своей схемой, конструктором, Frame и сценарием. Перечислите его схему и его сценарий в манифесте.
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" ] }- Строка 8 Схема Lamp добавляется к схеме Keeper.
- Строки 9-13 Сценарий Lamp добавляется к сценариям Keeper.
Создайте схему 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 }] }- Строки 6-9 Входы дочернего Framework. Дочернему тоже нужно имя.
- Строка 10 У дочернего Framework свой Frame.
Создайте его конструктор, его Frame и его сценарий.
rules/lamp.tsexport function construct(name: string, oil: number): { name: string; oil: number } { return { name: name, oil: oil }; }- Строка 1 Конструктор возвращает входы как хранимые значения.
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}}" } ] } }- Строки 11-14 Каждое поле, которое показывает Frame, перечислено как привязка.
scenarios/new-lamp.json{ "id": "new-lamp", "framework": "lamp", "parameters": { "name": "Lantern", "oil": 5 }, "expectedValues": { "name": "Lantern", "oil": 5 } }- Строка 4 Собирает Lamp отдельно и проверяет его значения.
В Keeper скрытый вход хранит ссылку на Framework, а childFrameworks сопоставляет слот с Framework.
schemas/keeper.json"lamp": { "label": "Lamp", "defaultValue": "lamp", "hidden": true }- Строка 14 Скрытый вход никогда не показывается в мастере.
schemas/keeper.json"childFrameworks": { "lamp": "lamp" }- Строка 31 Сопоставляет слот lamp с Framework с id lamp.
Конструктор возвращает входы Lamp рядом со своими значениями.
rules/keeper.tsexport 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 } } }, }; }- Строка 5 lamp это FrameworkRef. Платформа превращает id Framework из входа в такую ссылку.
- Строки 6-9 Тип результата перечисляет хранимые значения и дочерние слоты.
- Строка 12 Дочерний слот: какой Framework собрать и какие входы ему передать.
Передайте сценариям Keeper вход lamp и ожидайте Lamp, который собирает конструктор.
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 } } }- Строка 8 Скрытый вход.
- Строка 18 Дочерний элемент, который собирает конструктор, вместе с его значениями.
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 },- Строка 8 Скрытый вход в heal.json.
scenarios/heal.json"expectedChildren": { "lamp": { "name": "Lantern", "oil": 5 } }- Строка 30 Ожидаемый дочерний элемент в heal.json.
Выберите «Проверить и показать». Проверка должна пройти, прежде чем вы перейдёте дальше.
Теперь в проекте эти файлы. Раскройте любой отрывок выше, чтобы увидеть целый файл.
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 - Добавьте список и отклонённое действие
Список хранит контент, который персонаж получает в игре. Pack хранит Trinkets, второй Framework, чьи пресеты и есть контент. Перечислите его схему и два его сценария в манифесте.
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" ] }- Строки 8-12 Схема Trinket добавляется к двум другим.
- Строки 17-18 Сценарий Trinket и сценарий pack.
Создайте схему Trinket. Её Frame должен иметь id, который не использует ни один другой Frame в игре.
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 } } ] }- Строка 8 Вес с границами. Предложение читает его.
- Строки 10-16 У Trinket свой Frame.
- Строки 17-28 Пресеты, которые игрок может положить в рюкзак. Сценарий выбирает их по id.
Создайте его конструктор, его Frame и его сценарий.
rules/trinket.tsexport function construct( name: string, weight: number, ): { name: string; weight: number } { return { name: name, weight: weight }; }- Строка 5 Возвращает два хранимых значения.
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}}" } ] } }- Строки 22-23 Показывает имя и вес.
scenarios/new-trinket.json{ "id": "new-trinket", "framework": "trinket", "parameters": { "name": "Rope", "weight": 2 }, "expectedValues": { "name": "Rope", "weight": 2 } }- Строка 4 Собирает Trinket отдельно и проверяет его значения.
Объявите действие и список в схеме 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" } ],- Строки 19-23 Новое действие.
schemas/keeper.json"lists": { "pack": { "label": "Pack", "of": "trinket", "max": 4 } }- Строка 37 Список Pack: его подпись, Framework контента, который он хранит, и его размер.
Создайте файл правил действия.
rules/pack-trinket.tsexport function pack_trinket(self: ElementRef, trinket: ContentRef): void { list.add(self, "pack", trinket, offer.where({ weight: { lte: 3 } })); }- Строка 1 Выбранная безделушка приходит как ContentRef.
- Строка 2 Предложение решает, что можно положить в рюкзак. Только безделушки весом 3 или меньше.
Предложение может отказать, поэтому проверьте оба исхода. Создайте 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"] } }- Строки 19-24 Принято: верёвка достаточно лёгкая, поэтому её добавляют.
- Строки 25-31 Отказ: наковальня слишком тяжёлая. Шаг ожидает отказ NOT_OFFERED и отсутствие записей.
- Строка 33 После последнего шага в Pack лежит только верёвка.
Frame, который показывает список, использует version 2.0. Измените версию, объявите действие и область в dataContract и нарисуйте кнопку и область.
layouts/keeper.json"version": "2.0",- Строка 2 Frame со списком использует 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" }]- Строка 21 Псевдоним для нового действия.
- Строка 23 Объявляет область списка.
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." }- Строки 37-39 Кнопка, которая запускает действие.
- Строка 42 Область рисует список.
- Строка 44 Список для показа.
- Строка 45 Показывается, пока список пуст.
Выберите «Проверить и показать». Проверка должна пройти, прежде чем вы перейдёте дальше.
Теперь в проекте эти файлы. Раскройте любой отрывок выше, чтобы увидеть целый файл.
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
Что вы построили
Теперь у вашего Keeper есть выбор, производное значение, дочерний Framework, список с отклонённым действием и сценарии, которые проверяют каждое из них. В рецептах показаны более мелкие изменения по отдельности.

