FlowCanvas Forums › Support › Transfer bone values with delay
I’m trying to transfer the bone rotation values from one skeleton to another. My first attempt was to get/set local euler angles from/to every bone, which worked fine!
Now, I would like to refine the script so that it becomes modular applicable to different characters without re-assigning every individual bone by hand. I tried to use the get/set bone transform/rotation functions, to which an animator variable can be assigned, and was hoping that I can simply change the dependencies with the animator variable. However, it doesn’t really transfer values from one skeleton to another. Maybe the problem is not related to the script but to the animator object itself. Any hint would be great!
What I want to achieve is creating character with 10 arms, like a Shiva (example image attached). Thus I need to assign the script to a several instances of the arm rig. Additionally, it needs a time offset for each arm pair. I didn’t get to this point so far, as I’m still struggling with the rotation transfer. However, from looking at the FlowCanvas functions, I assume that the time delay can be achieved by creating a list of the rotation values and read out later positions from this list, right?
Many thanks for any help!
Currently, I am using this nodes: Get Bone Transform -> Get local Rotation -> Set Bone local Rotation to get the values from the source skeleton and send it to the target skeleton. The debug mode shows that values are being sent from “Get local Rotation” to “Set Bone local Rotation”. But the target skeleton is not moving at all. I hope I have assigned all animator dependencies and settings correctly (e.g. activated “IK Pass” in the target animator), but there must be something I’ve missed (I’m new to Unity). Grateful for any hints. Here is an animated example of what I am trying to achieve: https://vimeo.com/248464203
Thanks for any help!
Hmm. Does the target skeleton has any animation of it’s own by any chance?
I think that even if you copy/paste bone transform values per-frame to a target skeleton, if that skeleton has any animation or is anyhow being controlled by an Animator component, then the Animator animation will “win” over the copy/pasted values.
For what you want to achieve, and considering I understood correctly, I think that you don’t even need an Animator controller on the target “duplicate” skeletons, but I might be wrong.
Join us on Discord: https://discord.gg/97q2Rjh
The animator component needs to be assigned to the “Set Bone Local Rotation” node (screenshot), although there is no keyframe in the animation. The description of that node says “Sets local rotation of a human bone during a IK pass”. Maybe it needs an additional node to trigger the pass? The values from the source are streaming fine, but somehow the target does not react.
Ahh. This method works in the IK Pass. That means that you probably need to call this method in “OnAnimatorIK” event instead of a regular “Update” event. To do so, please try using the Animator event node (“Events/Object/Animator”) and more specifically the “On Animator IK” port of that node, in place of the “Update” node you have now.
Just make sure, to assign the “tree pose” animator gameobject you have in the “target” animator field of that event node’s inspector.
Let me know if that works for you.
Thank you.
Join us on Discord: https://discord.gg/97q2Rjh
That works perfect, thanks a lot! Now it needs a time offset from the source to the target skeleton, so that the target skeleton plays the animation with a short delay. thus all arms will have an offset based on the delay of the motion. Couldn’t find a “time delay” node, but maybe there is a way to buffer the rotation values for a certain time and read it out after a given time constantly. Or an array in which values are stored every 1/30 seconds and then been read out by ID (higher ID number would cause longer delay). Would be great if you could give me a hint how to approach this in flowCanvas. Many thanks!
Hello again and sorry for the late reply!
There is actually a “wait” node, but I don’t think it will work for what you are after.
I will try create a “buffer delay” / “echo” node and post it here for you once ready (expect tomorrow please)
I think it is an interesting node to have anyway! 🙂
Thanks!
Join us on Discord: https://discord.gg/97q2Rjh
That would be awesome! Many thanks!
Btw, that might be pretty close to a damper function too, where time based values are interpolated over a certain timespan. Could be quite useful to ease user controlled camera movements, soften jitter in mocap’s, etc…
Any news about this new node? It’s getting more and more crucial for my current project to write/store joint rotation values in a buffer and delay, replay or interpolate/damp them.
Hello again and sorry for the late reply.
I’ve just manage to create this node you requested as well as a damp node (for floats and vectors).
Since I find them to be useful and will be included in the next, the best way for you to use them now would be to open up the file “Time.cs” and copy/paste the following code in there instead of sending you a separate package file.
Here is the code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 |
[Category("Time")] [Description("Input values are buffered and returned in order of buffering once the buffered amount reaches the buffer size. In practice this creates a delayed value output when the node is constantly updated per-frame.\nWhen the node ceases to update, a new buffer is created.")] [ExposeAsDefinition] public class Buffer<T> : PureFunctionNode<T, T, int> { private int lastFrame = -10; private Queue<T> q; public override T Invoke(T value, int size = 60) { var currentFrame = Time.frameCount; //init if (currentFrame - lastFrame > 1){ q = new Queue<T>(); } lastFrame = currentFrame; q.Enqueue(value); return q.Count >= size? q.Dequeue() : q.Peek(); } } [Name("Damp (Float)")] [Category("Time")] [Description("Returns a smoothly interpolated value towards the input value.")] public class DampFloat : PureFunctionNode<float, float, float>{ private float last; public override float Invoke(float value, float damp = 1f){ last = Mathf.Lerp(last, value, damp * Time.deltaTime); return last; } } [Name("Damp (Vector3)")] [Category("Time")] [Description("Returns a smoothly interpolated value towards the input value.")] public class DampVector3 : PureFunctionNode<Vector3, Vector3, float>{ private Vector3 last; public override Vector3 Invoke(Vector3 value, float damp = 1f){ last = Vector3.Lerp(last, value, damp * Time.deltaTime); return last; } } |
Simply paste the code anywhere within the Time.cs file and within the namespace brackets of course.
There is a big difference between Buffer and Damp of course:
– Buffer, will output the exact input value in the exact order they were queued. Buffer also works with all types.
– Damp, will instead output an interpolated value (float or Vector) towards the input value.
Let me know if this works for you.
Thanks!
Join us on Discord: https://discord.gg/97q2Rjh