Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions packages/core/src/animation/AnimationClip.ts
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,15 @@ export class AnimationClip extends EngineObject {
this._length = 0;
}

/**
* Samples an animation at a given time.
* @param entity - The animated entity
* @param time - The time to sample an animation
*/
sampleAnimation(entity: Entity, time: number): void {
this._sampleAnimation(entity, time);
}

/**
* @internal
* Samples an animation at a given time.
Expand Down
11 changes: 6 additions & 5 deletions packages/core/src/animation/AnimationClipCurveBinding.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ export class AnimationClipCurveBinding {
/** The animation curve. */
curve: AnimationCurve<KeyframeValueType>;

private _tempCurveOwner: Record<number, AnimationCurveOwner<KeyframeValueType>> = {};
private _tempCurveOwner = new WeakMap<Entity, AnimationCurveOwner<KeyframeValueType>>();

/**
* @internal
Expand Down Expand Up @@ -63,10 +63,11 @@ export class AnimationClipCurveBinding {
* @internal
*/
_getTempCurveOwner(entity: Entity, component: Component): AnimationCurveOwner<KeyframeValueType> {
const { instanceId } = entity;
if (!this._tempCurveOwner[instanceId]) {
this._tempCurveOwner[instanceId] = this._createCurveOwner(entity, component);
let owner = this._tempCurveOwner.get(entity);
if (!owner) {
owner = this._createCurveOwner(entity, component);
this._tempCurveOwner.set(entity, owner);
}
return this._tempCurveOwner[instanceId];
return owner;
}
}
298 changes: 157 additions & 141 deletions packages/core/src/animation/Animator.ts

Large diffs are not rendered by default.

76 changes: 0 additions & 76 deletions packages/core/src/animation/AnimatorStateInstance.ts

This file was deleted.

9 changes: 3 additions & 6 deletions packages/core/src/animation/AnimatorStateMachine.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,9 @@ export class AnimatorStateMachine {

/**
* The state will be played automatically.
* @remarks When the Animator's AnimatorController changed or the Animator's onEnable be triggered. Cleared to `null` if the state is removed via `removeState`.
* @remarks When the Animator's AnimatorController changed or the Animator's onEnable be triggered.
*/
defaultState: AnimatorState | null = null;
defaultState: AnimatorState;

/** @internal */
_entryTransitionCollection = new AnimatorStateTransitionCollection();
Expand Down Expand Up @@ -68,11 +68,8 @@ export class AnimatorStateMachine {
const index = this.states.indexOf(state);
if (index > -1) {
this.states.splice(index, 1);
delete this._statesMap[name];
if (this.defaultState === state) {
this.defaultState = null;
}
}
delete this._statesMap[name];
}

/**
Expand Down
2 changes: 1 addition & 1 deletion packages/core/src/animation/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ export { Animator } from "./Animator";
export { AnimatorController } from "./AnimatorController";
export { AnimatorControllerLayer } from "./AnimatorControllerLayer";
export { AnimatorState } from "./AnimatorState";
export { AnimatorStateInstance } from "./AnimatorStateInstance";
export { AnimatorStatePlayData } from "./internal/AnimatorStatePlayData";
export { AnimatorStateMachine } from "./AnimatorStateMachine";
export { AnimatorStateTransition } from "./AnimatorStateTransition";
export { AnimatorConditionMode } from "./enums/AnimatorConditionMode";
Expand Down
6 changes: 4 additions & 2 deletions packages/core/src/animation/internal/AnimationEventHandler.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import { IPoolElement } from "../../utils/ObjectPool";
import { AnimationEvent } from "../AnimationEvent";

/**
* @internal
*/
export class AnimationEventHandler {
export class AnimationEventHandler implements IPoolElement {
event: AnimationEvent;
handlers: Function[] = [];

dispose() {}
}
38 changes: 11 additions & 27 deletions packages/core/src/animation/internal/AnimatorLayerData.ts
Original file line number Diff line number Diff line change
@@ -1,48 +1,32 @@
import { Component } from "../../Component";
import { AnimatorControllerLayer } from "../AnimatorControllerLayer";
import { AnimatorState } from "../AnimatorState";
import { AnimatorStateInstance } from "../AnimatorStateInstance";
import { AnimatorStateTransition } from "../AnimatorStateTransition";
import { LayerState } from "../enums/LayerState";
import { AnimationCurveLayerOwner } from "./AnimationCurveLayerOwner";
import { AnimatorStateData } from "./AnimatorStateData";
import type { AnimatorStatePlayData } from "./AnimatorStatePlayData";
import { AnimatorStatePlayData } from "./AnimatorStatePlayData";

/**
* @internal
*/
export class AnimatorLayerData {
layerIndex: number;
layer: AnimatorControllerLayer;
curveOwnerPool: Record<number, Record<string, AnimationCurveLayerOwner>> = Object.create(null);
animatorStateDataMap: WeakMap<AnimatorState, AnimatorStateData> = new WeakMap();
instanceMap: WeakMap<AnimatorState, AnimatorStateInstance> = new WeakMap();
srcPlayData: AnimatorStatePlayData | null = null;
destPlayData: AnimatorStatePlayData | null = null;
curveOwnerPool: WeakMap<Component, Record<string, AnimationCurveLayerOwner>> = new WeakMap();
animatorStateDataMap: Record<string, AnimatorStateData> = {};
srcPlayData: AnimatorStatePlayData = new AnimatorStatePlayData();
destPlayData: AnimatorStatePlayData = new AnimatorStatePlayData();
layerState: LayerState = LayerState.Standby;
crossCurveMark: number = 0;
manuallyTransition: AnimatorStateTransition = new AnimatorStateTransition();
crossFadeTransition: AnimatorStateTransition;
crossLayerOwnerCollection: AnimationCurveLayerOwner[] = [];

getOrCreateInstance(state: AnimatorState): AnimatorStateInstance {
const map = this.instanceMap;
let instance = map.get(state);
if (!instance) {
instance = new AnimatorStateInstance(state);
map.set(state, instance);
}
return instance;
}

completeCrossFade(): void {
this.srcPlayData = this.destPlayData;
this.destPlayData = null;
this.crossFadeTransition = null;
}

clearCrossFadeSlot(): void {
this.destPlayData = null;
this.crossFadeTransition = null;
switchPlayData(): void {
const srcPlayData = this.destPlayData;
const switchTemp = this.srcPlayData;
this.srcPlayData = srcPlayData;
this.destPlayData = switchTemp;
}

resetCurrentCheckIndex(): void {
Expand Down
6 changes: 2 additions & 4 deletions packages/core/src/animation/internal/AnimatorStateData.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,6 @@ import { AnimationEventHandler } from "./AnimationEventHandler";
export class AnimatorStateData {
curveLayerOwner: AnimationCurveLayerOwner[] = [];
eventHandlers: AnimationEventHandler[] = [];
eventsBuiltVersion = -1;
eventsBuiltScriptsVersion = -1;

constructor(readonly state: AnimatorState) {}
state: AnimatorState = null;
clipChangedListener: () => void = null;
}
81 changes: 59 additions & 22 deletions packages/core/src/animation/internal/AnimatorStatePlayData.ts
Original file line number Diff line number Diff line change
@@ -1,36 +1,75 @@
import { AnimatorStateInstance } from "../AnimatorStateInstance";
import { AnimationClip } from "../AnimationClip";
import { AnimatorState } from "../AnimatorState";
import { AnimatorStateTransition } from "../AnimatorStateTransition";
import { AnimatorStatePlayState } from "../enums/AnimatorStatePlayState";
import { WrapMode } from "../enums/WrapMode";
import { StateMachineScript } from "../StateMachineScript";
import { AnimatorStateData } from "./AnimatorStateData";

/**
* @internal
* Per-instance runtime data for an AnimatorState.
* Proxies read-only properties from the shared AnimatorState asset,
* while providing per-instance mutable properties (e.g. speed, wrapMode).
*/
export class AnimatorStatePlayData {
/** @internal */
state: AnimatorState;
/** @internal */
stateData: AnimatorStateData;
/** @internal */
playedTime: number;
playState: AnimatorStatePlayState;
/** @internal */
clipTime: number;
/** @internal */
currentEventIndex: number;
/** @internal */
isForward = true;
/** @internal */
offsetFrameTime: number;
/** Per-instance speed. Initialized from AnimatorState.speed, safe to modify without affecting other instances. */
speed: number = 1.0;
/** Per-instance wrap mode. Initialized from AnimatorState.wrapMode, safe to modify without affecting other instances. */
wrapMode: WrapMode = WrapMode.Loop;

playedTime: number = 0;
playState: AnimatorStatePlayState = AnimatorStatePlayState.UnStarted;
clipTime: number = 0;
currentEventIndex: number = 0;
isForward: boolean = true;
offsetFrameTime: number = 0;
// ── Proxy properties from AnimatorState (read-only) ──

private _changedOrientation: boolean = false;
/** The name of the state. */
get name(): string {
return this.state.name;
}

constructor(public readonly instance: AnimatorStateInstance) {}
/** The clip played by this state. */
get clip(): AnimationClip {
return this.state.clip;
}

reset(stateData: AnimatorStateData, offsetFrameTime: number): void {
const state = this.instance._state;
this.stateData = stateData;
this.offsetFrameTime = offsetFrameTime;
/** The transitions going out of this state. */
get transitions(): Readonly<AnimatorStateTransition[]> {
return this.state.transitions;
}

/**
* Add a state machine script to the underlying AnimatorState.
*/
addStateMachineScript<T extends StateMachineScript>(scriptType: new () => T): T {
return this.state.addStateMachineScript(scriptType);
}

private _changedOrientation = false;

reset(state: AnimatorState, stateData: AnimatorStateData, offsetFrameTime: number): void {
this.state = state;
this.playedTime = 0;
this.offsetFrameTime = offsetFrameTime;
this.stateData = stateData;
this.playState = AnimatorStatePlayState.UnStarted;
this.clipTime = state.clipStartTime * state.clip.length;
this.currentEventIndex = 0;
this.isForward = true;
this._changedOrientation = false;
state._transitionCollection.needResetCurrentCheckIndex = true;
this.speed = state.speed;
this.wrapMode = state.wrapMode;
this.state._transitionCollection.needResetCurrentCheckIndex = true;
}

updateOrientation(deltaTime: number): void {
Expand All @@ -46,12 +85,11 @@ export class AnimatorStatePlayData {

update(deltaTime: number): void {
this.playedTime += deltaTime;
const instance = this.instance;
const state = instance._state;
const state = this.state;
let time = this.playedTime + this.offsetFrameTime;
const duration = state._getDuration();
this.playState = AnimatorStatePlayState.Playing;
if (instance.wrapMode === WrapMode.Loop) {
if (this.wrapMode === WrapMode.Loop) {
time = duration ? time % duration : 0;
} else {
if (Math.abs(time) >= duration) {
Expand All @@ -69,9 +107,8 @@ export class AnimatorStatePlayData {
}
}

private _correctTime(): void {
const state = this.instance._state;
// Reverse playback at clipTime=0 would step into negatives; jump to clipEnd.
private _correctTime() {
const { state } = this;
if (this.clipTime === 0) {
this.clipTime = state.clipEndTime * state.clip.length;
}
Expand Down
Loading
Loading