Skip to content

fix(anim): support cloning components with dynamically-added layers#8947

Merged
willeastcott merged 3 commits into
mainfrom
fix-anim-clone-multiple-layers
Jun 23, 2026
Merged

fix(anim): support cloning components with dynamically-added layers#8947
willeastcott merged 3 commits into
mainfrom
fix-anim-clone-multiple-layers

Conversation

@willeastcott

Copy link
Copy Markdown
Contributor

Fixes #6395

Cloning an entity whose anim component had a layer added at runtime via addLayer() (rather than via a loaded state graph) threw TypeError: Cannot read properties of undefined (reading 'assignAnimation'). This reproduces by opening the animation/layer-masks example and appending modelEntity.clone().

Cause: layers (and states) added dynamically via addLayer() / assignAnimation() are not recorded in the component's _stateGraph. On clone, initializeComponentData rebuilds the clone's layers from that stale state graph, then iterates the source's live layers doing component.layers[i].assignAnimation(...) — for any layer missing from the state graph, component.layers[i] is undefined.

Fix: resolve the clone's layer by name (creating it via addLayer if the state graph didn't recreate it) and assign onto that layer instead of the index. No behavior change for the state-graph/Editor-driven path, where all layers already exist.

Verified with a new regression test in test/framework/components/anim/component.test.mjs that mirrors the issue's repro (base layer + a dynamically-added additive second layer). It throws the documented error before the fix and passes after.

🤖 Generated with Claude Code

Cloning a component crashed when a layer had been added at runtime via
addLayer(), because the clone rebuilt its layers from the (stale) state
graph and then indexed layers that didn't exist. Resolve each layer by
name when assigning animations, creating it if missing.

Fixes #6395

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@github-actions

github-actions Bot commented Jun 23, 2026

Copy link
Copy Markdown

Build size report

This PR changes the size of the minified bundles.

Bundle Minified Gzip Brotli
playcanvas.min.js 2283.1 KB (+0.0 KB, +0.00%) 585.6 KB (+0.0 KB, +0.00%) 455.3 KB (+0.3 KB, +0.07%)
playcanvas.min.mjs 2280.5 KB (+0.0 KB, +0.00%) 584.8 KB (+0.0 KB, +0.00%) 455.2 KB (+0.3 KB, +0.06%)

@mvaligursky mvaligursky left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤖 Automated PR review — posted on my behalf by Claude Code (Opus 4.8). Not a human review; flagging for awareness.


Verdict: looks correct and well-scoped. No blocking issues.

I verified the core assumption: AnimComponent.addLayer() (src/framework/components/anim/component.js:383) is idempotent — it calls findAnimationLayer(name) and returns the existing layer if present, only creating one otherwise. So resolving the clone's layer by name handles both paths cleanly:

  • state-graph / Editor-driven layers — already recreated by loadStateGraph(), so addLayer() returns the existing instance (the weight/mask/blendType args are ignored, which is fine since the existing layer already carries the graph's values).
  • dynamically-added layers — absent from _stateGraph, so addLayer() creates them with the source layer's weight/mask/blendType, which is exactly what the old index-based code couldn't do.

Other angles checked:

  • Breaking changes: none. Behavior is identical for the existing all-layers-present path.
  • Layer ordering / index integrity: preserved — iteration is in data.layers order and _layerIndices stays consistent.
  • Performance: negligible (one extra name lookup per layer at clone time).
  • Test: the regression test mirrors the issue repro and also adds a single-layer guard. Good coverage of the additive blend-type round-trip and independent-instance check.

Minor (optional, non-blocking): the weight/mask/blendType passed to addLayer() are silently dropped for layers that already exist. That's correct today, but a short comment noting "args only apply when the layer is newly created here" could save a future reader a double-take.

@willeastcott willeastcott merged commit 272e95a into main Jun 23, 2026
10 checks passed
@willeastcott willeastcott deleted the fix-anim-clone-multiple-layers branch June 23, 2026 09:46
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Undefined access when anim component with multiple layers is cloned

2 participants