Skip to content

Instantly share code, notes, and snippets.

@jovannic
Created May 1, 2020 20:30
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jovannic/996c57514f9f3f02e62c513a6cf0e617 to your computer and use it in GitHub Desktop.
Save jovannic/996c57514f9f3f02e62c513a6cf0e617 to your computer and use it in GitHub Desktop.
Animator.ApplyJointVelocities
Animator.ApplyJointVelocities(motors : Array<Motor6D>) : void

Given the current set of AnimationTracks playing, and their current times and play speeds, compute relative velocities between the parts and apply them to Motor6D.Part1 (the part which Animator considers the "child" part).

This method will not apply velocities for a given joint if both of the joint's parts are currently part of the same assembly, e.g. if they are still connected directly or indirectly by Motors or Welds so that Part0:GetRootPart() == Part1:GetRootPart().

This method will not disable or remove the joints for you. You must disable or otherwise remove the rigid joints from the spanning tree before calling this method.

The given Motor6Ds are not required to be descendants of the the DataModel. Removing the joints from the DataModel before calling this method is supported. (since the joints must be removed from the spanning tree while still having both parts set, we must allow this)

This method will be amended to support AnimationConstraints in addition to Motors if and when we release the new joint.

Why We Won't Remove Joints for You

How the joints are disabled or removed requires some special care to do correctly with FilteringEnabled.

For the smoothest transition without visible hitches from temporary mechanism inconsistencies while waiting for mechanism structure changes to replicate, or waiting for network ownership assignment to take effect after a mechanism root change you would ideally remove the rigid joints initially on the network owner (keeping all parts in the same mechanism with the same root), and then send an event requesting the server to make the same change (if the owner is not the server). Initiating such a delicate dance as a non-obvious side effect doesn't seem appropriate.

If it did remove joints for you you could call this on the server and it would always "just work", but those velocity assignments that would have to be replicated from the server to the client won't match animation on the client by the time they are received on the network owner client, leaving you with a visual discontinuity in velocity that defeats the whole point of calling this method at all. If you called this on the client you either have to rely on the FilteringEnabled exception for removing Joints under Player.Character, that is only valid for characters or very explicitly message the server requesting it make the same change.

The benefits of using this method are subtle and it is difficult to use effectively, but the effects of using it wrong are at least minor and obvious. What it does do is very narrow in scope. It sets part velocity when called correctly. If not called correctly it just doesn't do that. It's an advanced feature.

Also how should we remove the joints? There's several options. Set Parent = nil, Destroy, or soon JointInstance.Enabled. How do we pick? It seems easier to let the caller decide.

Order Matters

Velocities are applied in the order passed in. If they are not already in tree order with the joints closest to root first you may get strange results.

If the joints have already been removed from the spanning tree we can't automatically sort them into tree order for you. We don't know what the root is from the spanning tree.

We could guess based on which parts are Part0/Part1 given the already assumed parent-child relationship direction uses by the Animator and velocity assignment. Is it worth it? I implemented a similar sort for Skeleton once before, but the best I could come up with was O(n^2) worst case. You're forced to use an orderly bubble sort because your sort comparison isn't a proper context free less-than comparison.

Rejected Alternatives

  • Take a list of parts. To get relative velocities we need relationships between parts. We could potentially try all possible pairs in the given set, but that's O(n^2) and a terrible idea.
  • Apply automatically when joints are removed Seems intuitive, but event ordering issues make this a nightmare.
  • Automatically remove passed in joints before assigning velocities: This one gets its own section above.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment