-
Notifications
You must be signed in to change notification settings - Fork 1.4k
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
Anim Component #2007
Anim Component #2007
Conversation
Would it be possible to make API code examples for different use cases. Just a snippet code. Even as just concept, to see what API will be like.
|
…state animations to be set
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just a few comments on the draft (which may have already changed).
I've went ahead and updated state name to node name for future proofing. |
How come most of the classes and methods are made private (using |
@godiagonal They are private because we wanted to deploy the API, ensure everything was stable, maybe iterate on what's deployed a bit, and then, when we're confident we will make no more changes that break backwards compatibility, we'll make it public (probably in 1.29.0). |
Oh I see, thanks for clarifying! |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
GR8TM!!! just a small comment from me
var child; | ||
for (var j = 0; j < entityChildren.length; j++) { | ||
if (entityChildren[j].name === entityHierarchy[i + 1]) | ||
child = entityChildren[j]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
should you break here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
- pc.AnimComponent#assignAnimation: function (stateName, animTrack, layerName);
First parameter is stateName. Are we going to use this function to assing animations to BlendTrees as well? If so, perhaps the parameter should be 'nodeName' or something? Same for removeStateAnimations.I've went ahead and updated state name to node name for future proofing.
maybe update the PR description as well
}); | ||
|
||
// load the state graph asset resource and assign the animation glb asset resources to the appropriate states | ||
modelEntity.findComponent('anim').loadStateGraph(stateGraph.resource); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
store modelEntity.findComponent('anim') in variable and reuse on following lines
"speed": 1.0 | ||
}, | ||
{ | ||
"name": "END" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
should END state be optional here (and not needed to be specified) if noting references it?
this._animEvaluator.findClip(animation.name).blendWeight = interpolatedTime * animation.weight / state.totalWeight; | ||
} | ||
} else { | ||
this._isTransitioning = false; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
it feels like we only allow a single transition at any time .. that seems limited?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Theres only ever one transition but a transition can contain multiple blends of previous states. You can be midway through a transition from A > B and then blend those into a new transition (A > B) > C. The this._transitionPreviousStates
is used to record all previous states and their weights during a single transition.
if (entityHierarchy.length === 1) { | ||
return currEntity; | ||
} | ||
return currEntity._parent.findByPath(entityHierarchy.join('/')); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just FYI - I believe findByPath
is sloooooow, so if this is performance-sensitive code, this may become an issue. Not blocking for merging this PR tho, IMHO.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok, i'd say we can test this then. The binder runs this once when the animation is first loaded and before it's played.
// else | ||
// return null; | ||
// } | ||
// return currEntity; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Delete commented out code? 😄
// Non-serialized | ||
this.stateGraph = null; | ||
this.layers = []; | ||
this.layerIndicies = {}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
layerIndicies
-> layerIndices
Something here that I don't see that could be very handy is allowing for wildcard transitions. For example, if the trigger Currently, the only way I can see to do this would be to |
I was also told to leave a Feature Request here regarding Blend Trees. I already posted the information on the forum, but I'll do a direct Copy+Paste belowI’m working with the anim component that was introduce with PR#2007 and I’ve got to say it’s a massive improvement to the previous workflow. However, I’ve been browsing through the PR and while I’ve seen Blend Trees being mentioned a few times I haven’t found anything that explains how they would be set up. I’m not super familliar with animations, but in Unity the type of tree I want to create is called a 2D Freeform Directional tree and it allows you to declare animations that should play on a graph via a 2D vector. Example below: With this tree if you were holding (W and D) [traditional WASD controls] it would blend between the run-forward and run-right animation to create a run-forward-right animation effect. This makes movement in games feel very fluid. I’d really like to finish getting the fluidity of our character ironed out so we can move forward with transferring the remainder of our client to PC. CurrentlyCurrently I'm using a small (not sanity-checked) utility for creating transitions, just to save me some typing and increase readability: function createTransition(from, to, time, _conditions) {
if(!_conditions) _conditions = [[]];
var conditions = [];
for(var row = 0; row < _conditions.length; row++) {
var condition = _conditions[row];
if(condition.length != 3) continue;
conditions.push({
parameterName: _conditions[row][0],
predicate: toPredicate(_conditions[row][1]),
value: _conditions[row][2]
});
}
return {
from: from,
to: to,
time: time,
conditions: conditions
};
} In order to get my animations smooth between the different WASD states it requires a lot of transitions, some with higher priority than others (which I used declaration order instead of priority here, as it seems to work the same). Example of transitions transitions: [
// Initial State
createTransition("START", "Idle", 0),
// Idle to walking
createTransition("Idle", "WalkForward", 0.1, [["vSpeed", "=", 1]]),
createTransition("Idle", "WalkBackward", 0.1, [["vSpeed", "=", -1]]),
createTransition("Idle", "WalkRight", 0.1, [["hSpeed", "=", 1]]),
createTransition("Idle", "WalkLeft", 0.1, [["hSpeed", "=", -1]]),
// Walking to running
createTransition("WalkForward", "Sprinting", 0.3, [["vSpeed", "=", 1], ["sprinting", "=", true]]),
// Running back to walking
createTransition("Sprinting", "WalkForward", 0.3, [["vSpeed", "=", 1], ["sprinting", "=", false]]),
// Running back to idle
createTransition("Sprinting", "Idle", 0.1, [["vSpeed", "=", 0], ["sprinting", "=", false]]),
// Running to back-pedaling
createTransition("Sprinting", "WalkForward", 0.3, [["vSpeed", "=", -1], ["sprinting", "=", false]]),
// Opposing States
createTransition("WalkForward", "WalkBackward", 0.25, [["vSpeed", "=", -1]]),
createTransition("WalkBackward", "WalkForward", 0.25, [["vSpeed", "=", 1]]),
createTransition("WalkRight", "WalkLeft", 0.25, [["hSpeed", "=", -1]]),
createTransition("WalkLeft", "WalkRight", 0.25, [["hSpeed", "=", 1]]),
// Diagnal state transitions
// -----
// Forward bi-directional transitions
createTransition("WalkForward", "WalkRight", 0.25, [["vSpeed", "=", 0], ["hSpeed", "=", 1]]),
createTransition("WalkForward", "WalkLeft", 0.25, [["vSpeed", "=", 0], ["hSpeed", "=", -1]]),
// Right bi-directional transitions
createTransition("WalkRight", "WalkForward", 0.25, [["hSpeed", "=", 0], ["vSpeed", "=", 1]]),
createTransition("WalkRight", "WalkBackward", 0.25, [["hSpeed", "=", 0], ["vSpeed", "=", -1]]),
// Backwards bi-direction transitions
createTransition("WalkBackward", "WalkRight", 0.25, [["vSpeed", "=", 0], ["hSpeed", "=", 1]]),
createTransition("WalkBackward", "WalkLeft", 0.25, [["vSpeed", "=", 0], ["hSpeed", "=", -1]]),
// Left bi-directional transitions
createTransition("WalkLeft", "WalkBackward", 0.25, [["hSpeed", "=", 0], ["vSpeed", "=", -1]]),
createTransition("WalkLeft", "WalkForward", 0.25, [["hSpeed", "=", 0], ["vSpeed", "=", 1]]),
// Walking to Idle
createTransition("WalkForward", "Idle", 0.1, [["vSpeed", "=", 0]]),
createTransition("WalkBackward", "Idle", 0.1, [["vSpeed", "=", 0]]),
createTransition("WalkRight", "Idle", 0.1, [["hSpeed", "=", 0]]),
createTransition("WalkLeft", "Idle", 0.1, [["hSpeed", "=", 0]]),
], Keep in mindWhile these may blend to and from each-other when the correct combination of inputs is pressed/released, there's no continuous state of blending that takes place. This is as smooth as I was able to make it, without blending between two animations if say |
@ChristianTucker Thanks for the feedback, it's super appreciated! With regards to the two features you've mentioned: A wild card transition state is something I omitted from the initial release to reduce complexity of the anim component code but it's something i'll look to support asap now that the component has had some testing. I can definitely see how larger state graphs can become 'spiderweb like' without this feature. I should be able to get this in fairly soon. Blend trees are the next major feature in the pipeline for the Anim Component! You'll be able to create blend tree nodes which can contain standard anim states or other blend tree nodes as their children. There will be support for 1D & 2D blend trees as a minimum. They will be controllable via parameters so these blend tree states will support continuous blends based on the parameter values. Please be sure to let me know if there's any other features you're keen to get in! |
@ellt92 Awesome, and thank you for all of the work you've put in on the animation system. Animations are the main reason I've left PlayCanvas a few times in the past, but thanks to the StateGraph I can do quite a bit that I couldn't before. I'll be EAGERLY waiting a wildcard implementation as I'm working on rushing our Client out as soon as possible. In the mean-time I'll just write a helper to simulate a wildcard by creating transitions from all known states, then exit back to idle. Please ping me as soon as either one of these features are ready, I'll be using this system in my upcoming game and will report any problems I notice back. |
@ChristianTucker The 'any' state has now been implemented in the AnimComponent. It'll go out with the next engine release! |
@ellthompson Awesome! Any plans for skeletal bone masking for animations in the near future? |
This PR introduces a new Anim component to the engine which can be used to animate the entity of the component its attached to. A state graph must also be supplied to the component which defines a set of animation states that can be active, when those states should activate and what the blending between new and old states should look like. Animation assets can then be linked to each state in the graph, after which the component will become playable. These animations can be supplied as either GLB assets (for model animations) or animation clips (to animate arbitrary properties of the components entity and its children).
Anim Component API:
State Graph Asset Example:
Character animation example using the state graph asset example above:
State Graph Asset Schema:
Animation Clip:
These are used to create
pc.AnimTrack
objects which can be passed into thelinkAnimationToState
function. The track can be retrieved from the resource of an animation clip.json
asset that has been loaded using theanimationclip
asset type.The example below flashes the light component of the signalLight entity red every quarter of a second.
Each curve in an animation is associated with an input and output array via index. These arrays define the keyframe pairs for the curve (input = time, output = value). In the clip above the single curve is using the only input and output arrays defined.
Curve path strings should be encoded and decoded using an instance of
pc.AnimPropertyLocator()
. Using this class ensures that all names have their forward slash and full stop characters correctly escaped. It provides two functions which can be used as follows:Engine examples:
Done: Animate component properties
Done: Character animation