Эта страница ещё не переведена — показана английская версия.
Entity presets
Пресет собирает аспекты сущности — данные, состояния, RPC, события, хуки — в именованный набор под один игровой сценарий. Пресет можно применить как есть, подстроить его числа или вывести из него свой. Применённый пресет добавляет аспекты вашему типу, но ни подо что этот тип не ставит: пресет — не модуль, и у него нет ничего своего, от чего можно было бы наследоваться. Характеристики, способности, снаряды, таблицы выпадения и объекты мира — это пять пресетов, а не пять подсистем: то же объявление, та же синхронизация, тот же порядок хуков.
Когда это нужно
- У объекта в игре есть числа, которые упираются в границы, восстанавливаются и на границах запускают переход.
- Действию нужны стоимость, перезарядка, фазы и эффекты, а вызываться оно должно одной командой клиента.
- Что-то летит, и попадание нужно честно засчитать стрелку с лагом.
- Добыча должна выпадать по взвешенным шансам, и если игрок оспорит выпадение, результат должен воспроизводиться в точности.
- На карте есть обстановка — двери, кнопки, ловушки, разрушаемые объекты, — и её состояния должны сохраниться для игрока, который вошёл посреди раунда.
- Пресеты не нужны, если сущность (entity) — это просто синхронизируемые данные. Объявите поля, и всё.
Кто что делает
| Актор | Что делает на этой странице |
|---|---|
schema-author | объявляет характеристики, способности, снаряды, таблицы выпадения, объекты мира |
room-owner | настраивает числа пресетов, разыгрывает таблицы выпадения, создаёт объекты мира |
player | применяет способности, стреляет, подбирает добычу, взаимодействует с объектами |
Коротко
Crate из двух готовых пресетов: объявляется один раз, создаётся одной строкой и настраивается на 250 HP// derived once, in the schema
[Entity("crate", Persistence = Persistence.Runtime, Presets = new[] { Preset.WorldObjects, Preset.Stats })]
public class Crate { [Stat(Max = 100, AtMin = "broken")] public Stat Hp; }
// then one line per crate, on the room host
var crate = await room.Create<Crate>(at: pos, tune: c => c.Hp.Max = 250);// derived once, in the schema
@Entity('crate', { persistence: Persistence.Runtime, presets: [Preset.WorldObjects, Preset.Stats] })
export class Crate { @Stat({ max: 100, atMin: 'broken' }) hp: Stat; }
// then one line per crate, on the room host
const crate = await room.create<Crate>({ at: pos, tune: (c) => { c.hp.max = 250; } });# derived once, in the schema
@entity("crate", persistence=Persistence.RUNTIME, presets=[Preset.WORLD_OBJECTS, Preset.STATS])
class Crate:
hp = stat(max=100, at_min="broken")
# then one line per crate, on the room host
crate = await room.create(Crate, at=pos, tune={"hp.max": 250})Как объявлять через атрибуты в Go, пока не решено (O-1). Примеры появятся, когда выберут способ записи.
// declarations compile into the same pushed model — playserv push from the UE project or CI
UCLASS(PSEntity = (Name = "crate", Persistence = "Runtime", Presets = "world-objects, stats"))
class UCrate : public UObject
{
GENERATED_BODY()
UPROPERTY(PSStat = (Max = 100, AtMin = "broken")) FPSStat Hp;
};
// then one line per crate, on the room host (dedicated server / master-client)
Room->Entities->Of<UCrate>()->Create(FPSIdempotencyKey(CrateId),
[SpawnPosition](UCrate& Crate) { Crate.Position = SpawnPosition; }); // Position — from the world-objects preset
// co_await support ships later: a built-in SDK coroutine wrapper that respects Unreal's GC and threading.// derived once, in the schema
[Entity("crate", Persistence = Persistence.Runtime, Presets = new[] { Preset.WorldObjects, Preset.Stats })]
public class Crate { [Stat(Max = 100, AtMin = "broken")] public Stat Hp; }
// then one line per crate, on the room host
var crate = await room.Create<Crate>(at: pos, tune: c => c.Hp.Max = 250);Модель
Пресет не вводит новых понятий. Всё, что он добавляет, выражается средствами, которые уже даёт сущность: аспектами, машинами состояний, хуками, режимом хранения.
Что даёт каждый пресет и где он настраивается.
| Пресет | Что даёт ему контракт | Где настраивается |
|---|---|---|
stats | аспект числовых характеристик (stat) с границами, восстановлением и модификаторами, а также хук на достижение порога: 0 HP становится переходом машины состояний, а не if в вашем коде | в объявлении поля; числа можно менять в панели прямо на живой игре |
abilities | аспект с набором способностей (ability), машину фаз применения, стоимость и перезарядку | в объявлении способности |
projectiles | тип с режимом хранения runtime, аспект баллистики и событие (event) попадания | в объявлении снаряда: замена одного атрибута меняет модель полёта |
drops | аспект таблицы выпадения (drop table) с весами и хук после смерти | в записях таблицы и их весах |
world objects | машину состояний интерактивного объекта и аспект условия взаимодействия | в объявлении пресета или для отдельного экземпляра при создании |
| inventory | тип с владельцем, который хранит ref на предмет каталога (catalog), аспект стопки с приращением и предел на одного владельца с объявленным поведением при переполнении | в объявлении типа |
У Inventory есть и отдельная страница.
Что верно для любого пресета.
| Всегда | Что это |
|---|---|
где он находится | на сущности, в виде аспектов: его данные синхронизируются как любые другие, его состояния — это состояния сущности, его RPC — RPC сущности, а хуки выполняются в порядке хуков сущности |
настройка | это живая конфигурация, а не повторный деплой. Поэтому панель показывает границу характеристики, перезарядку и вес выпадения в одном дереве |
свой пресет | выводится композицией, а не наследованием: Crate — это world-objects плюс stats, и результат по-прежнему остаётся аспектами на сущности |
Ошибки
- Объявить характеристику, способность, снаряд, таблицу выпадения или объект мира — значит изменить схему (schema), а это могут
fnилиadm. Если это попытается сделать игрок (player) или клиентский ключ, он получитforbidden, и ничего не будет объявлено — ни целиком, ни наполовину. Это другой отказ, чем те, что игрок встречает внутри разрешённого ему вызова: перезарядка ещё идёт, не хватает средств, нетitem:key.bronze. У каждого из них свой код.
Остальные отказы пресета — это отказы сущности. Два касаются именно пресетов:
- Частично заполненный пресет не проходит проверку при деплое. Пресет несёт согласованный набор: половину объявления отклонят ещё до выпуска, а не позволят ей странно вести себя в матче.
- Пресет нельзя пометить свойством, которое противоречит его собственной механике: например, аспект, для которого у клиента нет правил, нельзя объявить предсказуемым. Это тоже ловится при деплое.
Ограничения
Потолки здесь те же, что у сущности: число аспектов и машин состояний на тип, размер хранимого экземпляра, частота изменений одного экземпляра. Сам пресет объявля ет только предел на одного владельца — его несёт пресет с владельцем. На границе предел ведёт себя одним из трёх способов, и значения по умолчанию нет: refuse · redirect в объявленную корзину владельца · discard with event. Конкретные числа появятся вместе с главой об ограничениях платформы.