What it does
entity_proxy
Entity proxy metatable layer. Replaces the per-call closure-pushing
entity proxy (push_entity_proxy in
crates/zero_scripting/src/ffi_callbacks.rs) with a thin Luau metatable
that calls into the __native_transform.* / __native_entity.* flat
FFI bindings. The user-facing API is unchanged:
entity(id).localPosition = { x, y, z }, entity(id).despawn(),
entity(id).name, etc. all still work.
After warm-up, steady-state allocation is zero: same id returns
the same proxy (weak-valued cache), sub-proxies are lazily allocated
once and cached on the proxy, and each method binding is a closure
created on first access and cached as __m_<name>.
Exports
M.build(id: string) -> any— entity proxy factory. Returns a cached proxy when one exists; otherwise allocates and caches a fresh one. Also bound as_G.__build_entity_proxyfor the Rust-sidepush_entity_proxycall.M.EntityProxyMT,M.PositionProxyMT,M.RotationProxyMT,M.LocalPositionProxyMT,M.LocalRotationProxyMT,M.LocalScaleProxyMT,M.LossyScaleProxyMT,M.TransformProxyMT,M.AttributeProxyMT— exported metatables; consumers that want to extend the proxies can read them, but the proxies are sealed externally (every__newindexerrors).
Usage
local EntityProxy = require("@builtin::modules.entity_proxy")
local proxy = EntityProxy.build("some-entity-id")
proxy.localPosition = { 0, 1, 2 }
proxy.localRotation.eulerAngles = { 0, 90, 0 }
print(proxy.name)
proxy.despawn()
Notes
- World vs local space (post Bevy hierarchy migration):
.position/.rotationare world-space — engine back-computesTransform.{translation, rotation}on set, decomposes the world matrix on get..localPosition/.localRotation/.localScaleare parent-relative direct reads/writes onTransform.*..lossyScaleis read-only world-space scale (approximate under non-uniform parent scale + rotation; mirrors Unity)..worldMatrix()answers the whole composed object-to-world matrix as 16 column-major numbers, translation in elements 13, 14, 15 — the placement the renderer draws the entity's geometry at, carrying the skew a decomposition into position / rotation / scale folds away. Also readable as.transform.worldMatrix().- Bare
.scalewas removed — accessing it raises an error pointing callers atlocalScale/lossyScale.
- Sealed proxies: every proxy errors on
__newindex(sealing). Mutations go throughposition.set/attribute.set/ etc., not direct field writes. - Caching: the entity-proxy cache is keyed by
idwith weak values (__mode = "v"), so dropped proxies are GC'd. Within one proxy, theM_PREFIX-keyed cache survives until the proxy itself is collected. - The
.componentsub-proxy is built by__native_entity.component_subproxy— Rust-side helper that constructs the ~15-closure component-proxy table once per entity. Subsequent.componentaccesses hit the cached entry.
Discussion
Scoped to this part · feeds back into the world's score.
Sign in to post.sign in
No comments yet. Be the first.