Preset (asset type)
When to use one
- The same settings recur across many assets or entities (e.g. "pixel
art": every sprite texture with
filter = nearestand no mipmaps; a tuned locomotion set for every character of one rig). - Designers swap configurations without editing code.
- A configuration needs a stable name other content can refer to.
If you're capturing a whole entity assembly, use a .bundle. If
you're factoring shared logic (not configuration), use a .module.
Where it lives
- Source:
/zero/source/.../<Name>.preset/ - Identity:
<Name>(the.presetsuffix strips). - Folder shape:
preset.yaml— the target and the values. Required.README.md— instance documentation. Required..metadata— agent-editable tags + free-form fields (title,description). Required.
The document
target: { __ref: "8cc28fc8-ed63-48ee-83a6-15f4192e27e1", type: assetType, name: texture }
values:
filter: nearest
generateMipmaps: false
targetnames the declarer by a guid envelope:__refis its guid;typeand the plainnametell a person reading the file what it points at.valuesholds only the fields this preset sets. Fields it leaves out keep whatever the asset or component has.
A component preset has the same shape, its target the component:
target: { __ref: "18dfece3-fe1b-4630-b7ad-0996f5043a2e", type: component, name: Locomotion }
values:
walkSpeed: 1.4
runSpeed: 2.5
How to create one
-- Capture what an asset has now:
local p = preset.capture("hero_sprite", "pixel_art")
-- Capture a component's live values on an entity:
local tint = preset.capture({ entity = id, component = "Model" }, "red_tint")
-- Both land at /zero/source/presets/<name>.preset/ (an absolute `.preset`
-- path lands where it says; `{ overwrite = true }` re-authors an existing
-- preset, keeping its guid).
-- Or author it directly:
asset.create("preset", "pixel_art", {
target = "@builtin::assetTypes.texture", -- an identity or a guid
values = { filter = "nearest" },
})
Each checks every value against the target's schema and refuses the whole write on any violation, naming each one.
How to use one
preset.values(p)returns(values, report). Every value is checked against the target's current schema: valid fields are returned, and each invalid one is listed by path inreport.rejected.preset.apply(p, into)writes the valid values into an asset the target governs, or into a component on an entity named{ entity = <id>, component = <name> }whose component is the target. A struct group merges field by field; any other field is written whole. Applying to something another declarer governs raises, naming both.preset.setValues(p, values)replaces the whole values table, checked first. Inside an undo operation the write is recorded into it, so undoing the operation puts back whatpreset.yamlheld.- A preset is a snapshot, not a live binding: applying it writes values once.
preset.apply("pixel_art", "enemy_sprite")
preset.apply("synty", { entity = id, component = "Locomotion" })
local values, report = preset.values("synty")
Defaults for new things — the Preset Manager
A world's Preset Manager rows make a preset the starting point for what is created from then on:
local p = preset.capture("hero_sprite", "pixel_art") -- or asset.create("preset", …)
preset.addDefault(p, { filter = "sprites/**" }) -- new things only
Each row names a declarer, a preset for it, and a filter. For a declarer,
the first row whose filter matches the new thing's path applies. The rows
live in .world_settings as [[presets]] blocks, sync with the world, and
are edited in the Settings panel's Preset Manager section.
The layers a new thing's settings take, each over the ones before it, field by field (a struct group merges field by field):
- the schema's declared defaults;
- the type's own
settingsDefaults; - the first matching Preset Manager row;
- the creating importer's settings for the type;
- the values the caller passed.
A component takes its public defaults, then the row, then the values the
caller passed.
Rows change only new things. A row's values are stamped into what is
created while it stands. Editing or removing a row changes nothing that
already exists, and opening a world applies nothing to its saved content. A
re-import or an overwrite = true re-authoring keeps the settings the asset
already holds.
The three creation moments, and what each filter reads:
| Moment | The row's declarer | The filter reads |
|---|---|---|
asset.create | an assetType whose instances share its schema | the new asset's path under /source (sprites/hero.texture); a runtime asset reads the path the same call would give a durable one |
| an import | an importer | the source file's path under /source at its first import (sprites/hero.png) |
component.add | a component | the entity's hierarchy path, names from the root down joined with / (level/props/crate) |
A component takes a row when it is added in edit mode, and in any mode
inside a scene build (a scene's build.luau, a SceneModule placement). A
component put back from existing content (a saved scene, a peer's record, a
template, a bundle's hierarchy, what a component's own code adds outside a
build) takes none. A rebuild restates a component only when its code states
it differently, so a row edit alone changes nothing a build already placed.
Checking what applies:
preset.defaults()lists the rows in order, each resolved, with every problem that keeps it from applying.preset.defaultFor(declarer, path)returns the values a new thing atpathwould take from the rows, and the row that gives them.
In the Inspector
Selecting a preset shows, beside its identity:
- Target — the declarer as an asset chip and the guid the preset names it by, with a notice when the target changed since the preset pinned it or does not resolve.
- Values — every field of the target's current schema as its typed
control, the way the target's own Settings section shows it. An edit is
checked against that schema, written with
preset.setValues, and is one undo step. A stored value the schema refuses is named with the reason. Beneath the fields, Apply to selection writes the preset into every selected asset its target governs and into the target component on every selected entity, as one undo step.
A preset still in the component-only form shows its values read-only, with a note that its next local edit converts it.
Every asset's and component card's Settings section offers Save as preset, which captures the values it shows as a preset targeting their declarer and selects it, and Apply preset…, which lists the presets targeting that declarer and applies the one picked.
How the target stays bound
- The preset resolves its target by the envelope's guid, and the preset's
.refspins that guid together with the target's checksum, so the link survives the target being renamed or moved. The envelope'snameis never an identity, so the guid is the preset's one link to its target. - Every read re-checks the values against the target's schema as it is
now.
report.pinnedis true once.refspins the target;report.targetChangedis true while the target's checksum differs from the one pinned, and a field the target dropped or retyped is then rejected by name while the rest still apply. - A
targetwritten as an identity string is accepted on read, resolves by identity and reads as unpinned; the preset's next local edit rewrites it as the envelope.
Discovery
asset.list("preset")— every registered preset.asset.inspect("<name>").detail—target,targetGuid,format,values,rejected,pinned,targetChanged.asset.validate("<name>")—preset.target_unresolvedwhen the target does not resolve,preset.value_rejectedfor each value its target rejects.
Authoring conventions
- Name presets by the configuration they capture (
pixel_art,hdr_skybox,synty), not by the declarer. - Keep
valuesto the fields that define the configuration; fields left out stay as each asset or component has them.
Common pitfalls
- Hand-edited values. A value the target's schema rejects is reported, not applied, and every edit of the file warns naming it.
- A table-valued component field (a
Field.table) is written whole bypreset.apply, so a preset holding one provides every key of it.
Related types
.assetType/.importer/.component— the declarers whose settings a preset holds..bundle— for multi-component / multi-entity assemblies.