Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat/add AnimatorState script for handle animatorState's lifecycle #552

Merged
merged 47 commits into from
Oct 29, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
47 commits
Select commit Hold shift + click to select a range
74c11e6
Animator editor (#475)
luzhuang Sep 2, 2021
df25f03
feat: solve math design circular dependency (#485)
yangfengzzz Sep 7, 2021
f064cc8
Revert "feat: solve math design circular dependency (#485)" (#487)
GuoLei1990 Sep 7, 2021
2878bdf
feat: add rotateAxisAngle for Quaternion (#480)
JujieX Sep 7, 2021
7d17191
refactor: delete useless scene raycast (#493)
gz65555 Sep 8, 2021
aea4277
fix: group other uniform block (#504)
zhuxudong Sep 10, 2021
6b86f88
fix(primitivemesh): fix cylinder error (#526)
singlecoder Oct 8, 2021
750be3c
Perf: release gpu command (#503)
zhuxudong Oct 9, 2021
bc9487d
fix: reset clipTime (#530)
luzhuang Oct 9, 2021
365219c
fix: use string replace asset type to extend easily (#520)
gz65555 Oct 12, 2021
159e5a2
Fix/animator (#531)
luzhuang Oct 12, 2021
18909ed
Update README.md
eyworldwide Oct 12, 2021
3085ac8
Fix: cubic interpolation and crossfade bug: From fixed pose to dest p…
luzhuang Oct 12, 2021
b5bd22d
v0.5.3
GuoLei1990 Oct 12, 2021
871c102
v0.5.4
GuoLei1990 Oct 12, 2021
d9ef75d
v0.5.5
GuoLei1990 Oct 12, 2021
e8cc0e1
v0.5.6
GuoLei1990 Oct 12, 2021
3b4a242
fix(2d): opt shader precision (#542)
singlecoder Oct 15, 2021
e36cb48
fix: delete default gl state (#550)
zhuxudong Oct 25, 2021
afb4771
feat: stash
luzhuang Oct 25, 2021
ea0914f
feat: stash
luzhuang Oct 25, 2021
9012697
Merge branch 'dev/0.6' of https://github.com/oasis-engine/engine into…
luzhuang Oct 25, 2021
8deb477
feat: merge main
luzhuang Oct 25, 2021
82efab7
feat: add animatorStateScript
luzhuang Oct 25, 2021
c6255aa
feat: add animatorStateScript
luzhuang Oct 26, 2021
3bb4ee9
feat: add animatorStateScript
luzhuang Oct 25, 2021
6e6b9a5
feat: add animatorStateScript
luzhuang Oct 26, 2021
851a490
fix(2d): opt shader precision (#542)
singlecoder Oct 15, 2021
f757f7f
fix(gl): fix gl error (#555)
singlecoder Oct 26, 2021
e8b3097
v0.5.7
GuoLei1990 Oct 26, 2021
9159248
feat: merge main
luzhuang Oct 27, 2021
23f51f9
feat: merge main
luzhuang Oct 27, 2021
36bd30f
feat: update comment
luzhuang Oct 27, 2021
5d6370f
feat: update comment
luzhuang Oct 27, 2021
3ae034b
refactor: opt `StateMachineScript` code
GuoLei1990 Oct 28, 2021
5998c36
refactor: opt code
GuoLei1990 Oct 28, 2021
92bb4c6
refactor: opt code
GuoLei1990 Oct 28, 2021
0694d51
refactor: opt performance
GuoLei1990 Oct 28, 2021
b6984c3
refactor: opt code
GuoLei1990 Oct 28, 2021
473868d
refactor: opt code
GuoLei1990 Oct 28, 2021
978845b
refactor: opt code
GuoLei1990 Oct 28, 2021
c727d33
refactor: opt code
GuoLei1990 Oct 28, 2021
4ae43d5
refactor: opt code
GuoLei1990 Oct 28, 2021
fcdead5
refactor: opt code
GuoLei1990 Oct 28, 2021
c25ed7b
refactor: opt code
GuoLei1990 Oct 28, 2021
7f5e0bb
Merge pull request #20 from GuoLei1990/temp/animatorStateScript
luzhuang Oct 29, 2021
a4ceeab
feat: merge opt
luzhuang Oct 29, 2021
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
2 changes: 1 addition & 1 deletion lerna.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"npmClient": "npm",
"version": "0.5.6",
"version": "0.5.7",
"bootstrap": {
"hoist": true
},
Expand Down
4 changes: 2 additions & 2 deletions packages/controls/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@oasis-engine/controls",
"version": "0.5.6",
"version": "0.5.7",
"license": "MIT",
"scripts": {
"b:types": "tsc",
Expand All @@ -15,6 +15,6 @@
"types/**/*"
],
"dependencies": {
"oasis-engine": "0.5.6"
"oasis-engine": "0.5.7"
}
}
6 changes: 3 additions & 3 deletions packages/core/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@oasis-engine/core",
"version": "0.5.6",
"version": "0.5.7",
"license": "MIT",
"main": "dist/main.js",
"module": "dist/module.js",
Expand All @@ -14,9 +14,9 @@
"types/**/*"
],
"dependencies": {
"@oasis-engine/math": "0.5.6"
"@oasis-engine/math": "0.5.7"
},
"devDependencies": {
"@oasis-engine/design": "0.5.6"
"@oasis-engine/design": "0.5.7"
}
}
122 changes: 102 additions & 20 deletions packages/core/src/animation/Animator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import { AnimatorStateTransition } from "./AnimatorTransition";
import { AnimatorUtils } from "./AnimatorUtils";
import { AnimationProperty } from "./enums/AnimationProperty";
import { AnimatorLayerBlendingMode } from "./enums/AnimatorLayerBlendingMode";
import { AnimatorStatePlayState } from "./enums/AnimatorStatePlayState";
import { LayerState } from "./enums/LayerState";
import { AnimationCurveOwner } from "./internal/AnimationCurveOwner";
import { AnimationEventHandler } from "./internal/AnimationEventHandler";
Expand Down Expand Up @@ -406,35 +407,52 @@ export class Animator extends Component {
this._checkTransition(srcPlayData, crossFadeTransitionInfo, layerIndex);
switch (animLayerData.layerState) {
case LayerState.Playing:
this._updatePlayingState(srcPlayData, animLayerData, layerWeight, deltaTime, layerAdditive);
this._updatePlayingState(srcPlayData, animLayerData, layerIndex, layerWeight, deltaTime, layerAdditive);
break;
case LayerState.FixedCrossFading:
this._updateCrossFadeFromPose(destPlayData, animLayerData, layerWeight, deltaTime, layerAdditive);
this._updateCrossFadeFromPose(destPlayData, animLayerData, layerIndex, layerWeight, deltaTime, layerAdditive);
break;
case LayerState.CrossFading:
this._updateCrossFade(srcPlayData, destPlayData, animLayerData, layerWeight, deltaTime, layerAdditive);
this._updateCrossFade(
srcPlayData,
destPlayData,
animLayerData,
layerIndex,
layerWeight,
deltaTime,
layerAdditive
);
break;
}
}

private _updatePlayingState(
playData: AnimatorStatePlayData,
layerData: AnimatorLayerData,
layerIndex: number,
weight: number,
delta: number,
additive: boolean
): void {
const { curveOwners, eventHandlers } = playData.stateData;
const { state } = playData;
const { state, playState: lastPlayState, clipTime: lastClipTime } = playData;
const { _curveBindings: curves } = state.clip;
const lastClipTime = playData.clipTime;

playData.update();

const clipTime = playData.clipTime;
const { clipTime, playState } = playData;

eventHandlers.length && this._fireAnimationEvents(playData, eventHandlers, lastClipTime, clipTime);

if (lastPlayState === AnimatorStatePlayState.UnStarted) {
this._callAnimatorScriptOnEnter(state, layerIndex);
}
if (playState === AnimatorStatePlayState.Finished) {
this._callAnimatorScriptOnExit(state, layerIndex);
} else {
this._callAnimatorScriptOnUpdate(state, layerIndex);
}

for (let i = curves.length - 1; i >= 0; i--) {
const owner = curveOwners[i];
const value = this._evaluateCurve(owner.property, curves[i].curve, clipTime, additive);
Expand All @@ -446,7 +464,7 @@ export class Animator extends Component {
}
playData.frameTime += state.speed * delta;

if (playData.finished) {
if (playState === AnimatorStatePlayState.Finished) {
layerData.layerState = LayerState.Standby;
}
}
Expand All @@ -455,22 +473,51 @@ export class Animator extends Component {
srcPlayData: AnimatorStatePlayData,
destPlayData: AnimatorStatePlayData,
layerData: AnimatorLayerData,
layerIndex,
weight: number,
delta: number,
additive: boolean
) {
const crossCurveDataCollection = this._crossCurveDataCollection;
const srcCurves = srcPlayData.state.clip._curveBindings;
const { state: destState } = destPlayData;
const destCurves = destState.clip._curveBindings;
const { _crossCurveDataCollection: crossCurveDataCollection } = this;
const { _curveBindings: srcCurves } = srcPlayData.state.clip;
const { state: srcState, stateData: srcStateData, playState: lastSrcPlayState } = srcPlayData;
const { eventHandlers: srcEventHandler } = srcStateData;
const { state: destState, stateData: destStateData, playState: lastDstPlayState } = destPlayData;
const { eventHandlers: destEventHandler } = destStateData;
const { _curveBindings: destCurves } = destState.clip;
const { clipTime: lastSrcClipTime } = srcPlayData;
const { clipTime: lastDestClipTime } = destPlayData;

let crossWeight = destPlayData.frameTime / (destState._getDuration() * layerData.crossFadeTransition.duration);
crossWeight >= 1.0 && (crossWeight = 1.0);
srcPlayData.update();
destPlayData.update();

const srcClipTime = srcPlayData.clipTime;
const destClipTime = destPlayData.clipTime;
const { clipTime: srcClipTime } = srcPlayData;
const { clipTime: destClipTime } = destPlayData;

srcEventHandler.length && this._fireAnimationEvents(srcPlayData, srcEventHandler, lastSrcClipTime, srcClipTime);
destEventHandler.length &&
this._fireAnimationEvents(destPlayData, destEventHandler, lastDestClipTime, destClipTime);

if (lastSrcPlayState === AnimatorStatePlayState.UnStarted) {
this._callAnimatorScriptOnEnter(srcState, layerIndex);
}
if (crossWeight === 1 || srcPlayData.playState === AnimatorStatePlayState.Finished) {
this._callAnimatorScriptOnExit(srcState, layerIndex);
} else {
this._callAnimatorScriptOnUpdate(srcState, layerIndex);
}

if (lastDstPlayState === AnimatorStatePlayState.UnStarted) {
this._callAnimatorScriptOnEnter(destState, layerIndex);
}
if (destPlayData.playState === AnimatorStatePlayState.Finished) {
this._callAnimatorScriptOnExit(destState, layerIndex);
} else {
this._callAnimatorScriptOnUpdate(destState, layerIndex);
}

for (let i = crossCurveDataCollection.length - 1; i >= 0; i--) {
const { curveOwner, srcCurveIndex, destCurveIndex } = crossCurveDataCollection[i];
const { property, defaultValue } = curveOwner;
Expand All @@ -486,26 +533,40 @@ export class Animator extends Component {

this._applyCrossClipValue(curveOwner, srcValue, destValue, crossWeight, weight, additive);
}

this._updateCrossFadeData(layerData, crossWeight, delta, false);
}

private _updateCrossFadeFromPose(
destPlayData: AnimatorStatePlayData,
layerData: AnimatorLayerData,
layerIndex: number,
weight: number,
delta: number,
additive: boolean
) {
const crossCurveDataCollection = this._crossCurveDataCollection;
const { state: destState } = destPlayData;
const curves = destState.clip._curveBindings;
const { state, stateData, playState: lastPlayState } = destPlayData;
const { eventHandlers } = stateData;
const { _curveBindings: curves } = state.clip;
const { clipTime: lastDestClipTime } = destPlayData;

let crossWeight = destPlayData.frameTime / (destState._getDuration() * layerData.crossFadeTransition.duration);
let crossWeight = destPlayData.frameTime / (state._getDuration() * layerData.crossFadeTransition.duration);
crossWeight >= 1.0 && (crossWeight = 1.0);
destPlayData.update();

const destClipTime = destPlayData.clipTime;
const { clipTime: destClipTime } = destPlayData;

eventHandlers.length && this._fireAnimationEvents(destPlayData, eventHandlers, lastDestClipTime, destClipTime);

if (lastPlayState === AnimatorStatePlayState.UnStarted) {
this._callAnimatorScriptOnEnter(state, layerIndex);
}
if (destPlayData.playState === AnimatorStatePlayState.Finished) {
this._callAnimatorScriptOnExit(state, layerIndex);
} else {
this._callAnimatorScriptOnUpdate(state, layerIndex);
}

for (let i = crossCurveDataCollection.length - 1; i >= 0; i--) {
const { curveOwner, destCurveIndex } = crossCurveDataCollection[i];
const destValue =
Expand All @@ -523,7 +584,7 @@ export class Animator extends Component {
const { destPlayData } = layerData;
destPlayData.frameTime += destPlayData.state.speed * delta;
if (crossWeight === 1.0) {
if (destPlayData.finished) {
if (destPlayData.playState === AnimatorStatePlayState.Finished) {
layerData.layerState = LayerState.Standby;
} else {
layerData.layerState = LayerState.Playing;
Expand Down Expand Up @@ -766,7 +827,28 @@ export class Animator extends Component {
}
}

private _clearPlayData() {
private _callAnimatorScriptOnEnter(state: AnimatorState, layerIndex: number): void {
const scripts = state._onStateEnterScripts;
for (let i = 0, n = scripts.length; i < n; i++) {
scripts[i].onStateEnter(this, state, layerIndex);
}
}

private _callAnimatorScriptOnUpdate(state: AnimatorState, layerIndex: number): void {
const scripts = state._onStateUpdateScripts;
for (let i = 0, n = scripts.length; i < n; i++) {
scripts[i].onStateUpdate(this, state, layerIndex);
}
}

private _callAnimatorScriptOnExit(state: AnimatorState, layerIndex: number): void {
const scripts = state._onStateExitScripts;
for (let i = 0, n = scripts.length; i < n; i++) {
scripts[i].onStateExit(this, state, layerIndex);
}
}

private _clearPlayData(): void {
this._animatorLayersData.length = 0;
this._crossCurveDataCollection.length = 0;
this._animationCurveOwners.length = 0;
Expand Down
51 changes: 50 additions & 1 deletion packages/core/src/animation/AnimatorState.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { AnimationClip } from "./AnimationClip";
import { AnimatorStateTransition } from "./AnimatorTransition";
import { WrapMode } from "./enums/WrapMode";
import { StateMachineScript } from "./StateMachineScript";

/**
* States are the basic building blocks of a state machine. Each state contains a AnimationClip which will play while the character is in that state.
Expand All @@ -11,6 +12,13 @@ export class AnimatorState {
/** The wrap mode used in the state. */
wrapMode: WrapMode = WrapMode.Loop;

/** @internal */
_onStateEnterScripts: StateMachineScript[] = [];
/** @internal */
_onStateUpdateScripts: StateMachineScript[] = [];
/** @internal */
_onStateExitScripts: StateMachineScript[] = [];

private _clipStartTime: number = 0;
private _clipEndTime: number = Infinity;
private _clip: AnimationClip;
Expand All @@ -24,7 +32,7 @@ export class AnimatorState {
}

/**
* ƒThe clip that is being played by this animator state.
* The clip that is being played by this animator state.
*/
get clip(): AnimationClip {
return this._clip;
Expand Down Expand Up @@ -82,6 +90,28 @@ export class AnimatorState {
index !== -1 && this._transitions.splice(index, 1);
}

/**
* Adds a state machine script class of type T to the AnimatorState.
* @param scriptType - The state machine script class of type T
*/
addStateMachineScript<T extends StateMachineScript>(scriptType: new () => T): T {
const script = new scriptType();
script._state = this;

const { prototype } = StateMachineScript;
if (script.onStateEnter !== prototype.onStateEnter) {
this._onStateEnterScripts.push(script);
}
if (script.onStateUpdate !== prototype.onStateUpdate) {
this._onStateUpdateScripts.push(script);
}
if (script.onStateExit !== prototype.onStateExit) {
this._onStateExitScripts.push(script);
}

return script;
}

/**
* Clears all transitions from the state.
*/
Expand All @@ -95,4 +125,23 @@ export class AnimatorState {
_getDuration(): number {
return this._clipEndTime - this._clipStartTime;
}

/**
* @internal
*/
_removeStateMachineScript(script: StateMachineScript): void {
const { prototype } = StateMachineScript;
if (script.onStateEnter !== prototype.onStateEnter) {
const index = this._onStateEnterScripts.indexOf(script);
index !== -1 && this._onStateEnterScripts.splice(index, 1);
}
if (script.onStateUpdate !== prototype.onStateUpdate) {
const index = this._onStateUpdateScripts.indexOf(script);
index !== -1 && this._onStateUpdateScripts.splice(index, 1);
}
if (script.onStateExit !== prototype.onStateExit) {
const index = this._onStateExitScripts.indexOf(script);
index !== -1 && this._onStateExitScripts.splice(index, 1);
}
}
}
47 changes: 47 additions & 0 deletions packages/core/src/animation/StateMachineScript.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import { Animator } from "../animation/Animator";
import { AnimatorState } from "../animation/AnimatorState";

/**
* StateMachineScript is a component that can be added to a animator state. It's the base class every script on a state derives from.
*/
export class StateMachineScript {
/** @internal */
_destroyed: boolean = false;
/** @internal */
_state: AnimatorState;
/**
* onStateEnter is called when a transition starts and the state machine starts to evaluate this state.
* @param animator - The animator
* @param animatorState - The state be evaluated
* @param layerIndex - The index of the layer where the state is located
*/
onStateEnter(animator: Animator, animatorState: AnimatorState, layerIndex: number): void {}

/**
* onStateUpdate is called on each Update frame between onStateEnter and onStateExit callbacks.
* @param animator - The animator
* @param animatorState - The state be evaluated
* @param layerIndex - The index of the layer where the state is located
*/
onStateUpdate(animator: Animator, animatorState: AnimatorState, layerIndex: number): void {}

/**
* onStateExit is called when a transition ends and the state machine finishes evaluating this state.
* @param animator - The animator
* @param animatorState - The state be evaluated
* @param layerIndex - The index of the layer where the state is located
*/
onStateExit(animator: Animator, animatorState: AnimatorState, layerIndex: number): void {}

/**
* Destroy this instance.
*/
destroy(): void {
if (this._destroyed) {
return;
}

this._state._removeStateMachineScript(this);
this._destroyed = true;
}
}
Loading