mirror of
https://github.com/galacean/engine.git
synced 2026-06-21 18:02:50 +08:00
fix(animation): invalidate cache on state removal
This commit is contained in:
@@ -19,6 +19,9 @@ export class AnimatorController extends ReferResource {
|
||||
_layersMap: Record<string, AnimatorControllerLayer> = {};
|
||||
|
||||
private _updateFlagManager: UpdateFlagManager = new UpdateFlagManager();
|
||||
private _onStateMachineChanged = (): void => {
|
||||
this._updateFlagManager.dispatch();
|
||||
};
|
||||
|
||||
/**
|
||||
* The layers in the controller.
|
||||
@@ -112,6 +115,7 @@ export class AnimatorController extends ReferResource {
|
||||
addLayer(layer: AnimatorControllerLayer): void {
|
||||
this._layers.push(layer);
|
||||
this._layersMap[layer.name] = layer;
|
||||
layer.stateMachine._setChangeCallback(this._onStateMachineChanged);
|
||||
layer._setEngine(this._engine);
|
||||
this._updateFlagManager.dispatch();
|
||||
}
|
||||
@@ -123,6 +127,7 @@ export class AnimatorController extends ReferResource {
|
||||
removeLayer(layerIndex: number): void {
|
||||
const theLayer = this.layers[layerIndex];
|
||||
this._layers.splice(layerIndex, 1);
|
||||
theLayer.stateMachine._setChangeCallback(null);
|
||||
delete this._layersMap[theLayer.name];
|
||||
this._updateFlagManager.dispatch();
|
||||
}
|
||||
@@ -131,6 +136,10 @@ export class AnimatorController extends ReferResource {
|
||||
* Clear layers.
|
||||
*/
|
||||
clearLayers(): void {
|
||||
const { _layers: layers } = this;
|
||||
for (let i = 0, n = layers.length; i < n; i++) {
|
||||
layers[i].stateMachine._setChangeCallback(null);
|
||||
}
|
||||
this._layers.length = 0;
|
||||
for (let name in this._layersMap) {
|
||||
delete this._layersMap[name];
|
||||
@@ -151,6 +160,7 @@ export class AnimatorController extends ReferResource {
|
||||
_setEngine(engine: Engine): void {
|
||||
const { _layers: layers } = this;
|
||||
for (let i = 0, n = layers.length; i < n; i++) {
|
||||
layers[i].stateMachine._setChangeCallback(this._onStateMachineChanged);
|
||||
layers[i]._setEngine(engine);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@ export class AnimatorStateMachine {
|
||||
readonly states: AnimatorState[] = [];
|
||||
|
||||
private _engine: Engine;
|
||||
private _onChanged: (() => void) | null = null;
|
||||
|
||||
/**
|
||||
* The state will be played automatically.
|
||||
@@ -72,6 +73,7 @@ export class AnimatorStateMachine {
|
||||
if (this.defaultState === state) {
|
||||
this.defaultState = null;
|
||||
}
|
||||
this._onChanged?.();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -170,4 +172,11 @@ export class AnimatorStateMachine {
|
||||
states[i]._setEngine(engine);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
_setChangeCallback(onChanged: (() => void) | null): void {
|
||||
this._onChanged = onChanged;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1487,6 +1487,59 @@ describe("Animator test", function () {
|
||||
}
|
||||
});
|
||||
|
||||
it("removing a state invalidates cached layer data before the state name is reused", () => {
|
||||
const entity = new Entity(engine);
|
||||
const localAnimator = entity.addComponent(Animator);
|
||||
const controller = new AnimatorController(engine);
|
||||
const layer = new AnimatorControllerLayer("layer");
|
||||
controller.addLayer(layer);
|
||||
|
||||
const oldState = layer.stateMachine.addState("Temp");
|
||||
const oldClip = new AnimationClip("old-temp-clip");
|
||||
const oldCurve = new AnimationFloatCurve();
|
||||
const oldStart = new Keyframe<number>();
|
||||
const oldEnd = new Keyframe<number>();
|
||||
oldStart.time = 0;
|
||||
oldStart.value = 0;
|
||||
oldEnd.time = 1;
|
||||
oldEnd.value = 1;
|
||||
oldCurve.addKey(oldStart);
|
||||
oldCurve.addKey(oldEnd);
|
||||
oldClip.addCurveBinding("", Transform, "position.x", oldCurve);
|
||||
oldState.clip = oldClip;
|
||||
localAnimator.animatorController = controller;
|
||||
|
||||
try {
|
||||
localAnimator.play("Temp");
|
||||
let layerData = localAnimator["_animatorLayersData"][0];
|
||||
expect(layerData.animatorStateDataMap.has(oldState)).to.eq(true);
|
||||
|
||||
layer.stateMachine.removeState(oldState);
|
||||
const newState = layer.stateMachine.addState("Temp");
|
||||
const newClip = new AnimationClip("new-temp-clip");
|
||||
const newCurve = new AnimationFloatCurve();
|
||||
const newStart = new Keyframe<number>();
|
||||
const newEnd = new Keyframe<number>();
|
||||
newStart.time = 0;
|
||||
newStart.value = 0;
|
||||
newEnd.time = 1;
|
||||
newEnd.value = 2;
|
||||
newCurve.addKey(newStart);
|
||||
newCurve.addKey(newEnd);
|
||||
newClip.addCurveBinding("", Transform, "position.x", newCurve);
|
||||
newState.clip = newClip;
|
||||
|
||||
localAnimator.play("Temp");
|
||||
layerData = localAnimator["_animatorLayersData"][0];
|
||||
|
||||
expect(localAnimator.getCurrentAnimatorState(0)._state).to.eq(newState);
|
||||
expect(layerData.animatorStateDataMap.has(oldState)).to.eq(false);
|
||||
expect(layerData.animatorStateDataMap.has(newState)).to.eq(true);
|
||||
} finally {
|
||||
entity.destroy();
|
||||
}
|
||||
});
|
||||
|
||||
it("Clone", () => {
|
||||
expect(animator.entity.clone().getComponent(Animator).animatorController).to.eq(animator.animatorController);
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user