Animation synchronisation in Three.js with Tween.js - animation

I'm trying to animate sprites along a path in Three.js using Tween.js by chaining the animations in order to have something like this :
----#----#----#----#----#---- etc
Every sprite has its own tween animation, and I just delay each tween animation at the beginning. Each sprite has in fact N animations along the path (that is not a straight line) and I chain them to have a loop effect.
Everything goes well if the FPS is perfectly stable, but my problem is that if at some point I have a FPS drop, the animations of the different sprites are not in sync anymore, and the space between sprites is not equal anymore. I potentially end up with something like this :
---#--#----#-#-#-----#--- etc
I was wondering if there is a better approach for this, like having only one tween animation for all the sprites, but I don't know how to introduce the offset between each sprite on many line segments.
I cannot post the exact code has it is part of a bigger app, and won't be usable as is, but it looks like this :
// create animations
for each (sprite) {
for each (segment) {
var currentAnimation = new TWEEN.Tween(sprite.position).to({
x : segment.endpoint.x,
y : segment.endpoint.y,
z : segment.endpoint.z
}, animationTime).easing(TWEEN.Easing.Linear.None);
currentAnimation.delay(delayTime * currentSpriteNumber);
previousAnimation.chain(currentAnimation);
}
lastAnimation.chain(firstAnimation);
lastAnimation.onComplete(onEachSpriteAnimationCompleted);
}
// start the animations
for each (sprite) {
spriteFirstAnimation.start();
}
// to remove the delay when each sprite animation has made one loop,
// and instantly replace the sprite at the beginning of the path
// (my paths are not closed)
var onEachSpriteAnimationCompleted = function() {
sprite.position.set(starting position);
for each (sprite animation) {
animation.delay(0);
}
}

Related

Make children of imported Three.js group follow pointer when their canvas gets hovered

In Three.js, I have a group of meshes that is loaded from outside with help of FBX loader. The group has six meshes inside. My task is to make this meshes follow pointer when they get hovered. More precisely, I'd like to have a sort of magnetic effect (just like navbar items in this pen, but with meshes in Three.js).
I think, firstly, I have to detect, where currently pointer is, i.e. get position of cursor in world coordinates system, and then translate meshes towards it. But when I try to get the position of cursor, it seems to be wrong.
Having said that, I have two questions:
How to get proper cursor's position relative to the world coordinates?
How to change position of each of the group's meshes so that they get translated against the cursor?
Here is what have I done so far:
Hi everyone.
In Three.js, I have a group of meshes that is loaded from outside with help of FBX loader. The group has six meshes inside. My task is to make this meshes follow pointer when their canvas get hovered. More precisely, I'd like to have a sort of magnetic effect (just like navbar items in this pen, but with meshes of Three.js).
I think, firstly, I have to detect, where currently pointer on canvas is, i.e. get position of cursor in world coordinates system, and then translate meshes towards it. But when I try to get the position of cursor, it seems to be wrong.
Having said that, I have two questions:
How to get proper cursor's position relative to the world coordinates?
How to change position of each of the group's meshes so that they get translated against the cursor?
Here is what have I done so far. Function that translates meshes isn't written yet. Mousemove callback returns pretty big digits, though:
// Load object and play a third-party animation
loader.load("Object_001.fbx", (object) => {
mixer = new THREE.AnimationMixer(object);
const action = mixer.clipAction(object.animations[0]);
action.play();
object.traverse((child) => {
if (child.isMesh) {
child.material.map = texture;
child.material.needsUpdate = true;
}
});
scene.add(object);
});
// log coordinates of the pointer
const mouse = new THREE.Vector3();
const position = new THREE.Vector3();
function onMouseMove(event) {
mouse.set(
(event.clientX / window.innerWidth) * 2 - 1,
-(event.clientY / window.innerHeight) * 2 + 1,
0.5
);
mouse.unproject(camera);
mouse.sub(camera.position).normalize();
const distance = -camera.position.z / mouse.z;
position.copy(camera.position).add(mouse.multiplyScalar(distance));
console.log(position);
}
wrapperElement.addEventListener("mousemove", onMouseMove);
Thanks in advance.
Made a codepen here:
https://codepen.io/cdeep/pen/YzxPPZQ
The cursor only exists in the canvas dom element which is a rendering of the camera view frustum.
The easiest way to make the object follow a mouse is to get the point of intersection of the mouse with another object in the 3d scene and set the object position to the intersection point. The above codepen showcases that.
raycaster.setFromCamera( mouse, camera );
const intersects = raycaster.intersectObjects([ground]);
if(intersects.length) {
const { point } = intersects[0];
cube.position.copy(point.setY(0.5));
}
You could also position it at a fixed distance from the mouse but it looks odd in my opinion:
const distance = 10;
raycaster.setFromCamera( mouse, camera );
const { origin, direction } = raycaster.ray;
cube.position.copy(origin.clone().add(direction.multiplyScalar(distance)));
Documentation for raycaster:
https://threejs.org/docs/index.html?q=ray#api/en/core/Raycaster
Raycasting is used for mouse picking (working out what objects in the
3d space the mouse is over) amongst other things.

Multiple overlapping animations on the same element in svg.js

I want to start a animation on an element while a previous animation is still active. However, calling animate() on the element queues the new animation at the end of the current animation.
For example, consider an animation where an element is being moved to a new position. Now, I also want to make it fade out when it reaches a certain position. The following queues the “once” animation at the end of the move, rather than at 80%.
rect.animate(1000).move(100, 100)
.once(0.8, function(pos, eased) {
rect.animate(200).opacity(0);
});
How do I make the element start fading out when it reaches 80% of the move? The API seems to be designed for chaining animations rather simultaneous overlapping animations.
What you are trying to do is a bit more complicated. Unforrtunately its not possible to "hack" into the current animation and add a new animation on the fly.
However what you can do is adding a new property which should be animated:
var fx = rect.animate(1000).move(100, 100)
.once(0.8, function(pos, eased) {
fx.opacity(0);
});
As you will notice that has its own problems because the oopacity immediately jumps to 80%. So this is not an approach which works for you.
Next try: Use the during method:
var morh = SVG.morph(
var fx = rect.animate(1000).move(100, 100)
.once(0.8, function(pos, eased) {
fx.during(function(pos, morphFn, easedPos) {
pos = (pos - 0.8) / 0.2
this.target().opacity(pos)
}
});
We just calculate the opacity ourselves

Unity3D: How to "chain" animations without resetting?

I imported a cube mesh that contains four smaller sub-cubes.
The mesh also contains an animation that pushes the smaller cubes out of the big one. When this is done, one of the small cubes "splits" again (it's not really splitting - it just pushes four children out again).
I'd like to trigger these animations when the user clicks on the big cube.The animation was initially exported as one clip but I cut it in half so that I can either trigger the first or the second split.
I attached a Box Collider to the big cube to be able to get an OnMouseDown event and from there I pass an int parameter to my AnimationController that starts the appropriate animation:
public class ClickToAnimate : MonoBehaviour {
private Animator _animator;
private int _clicks = 1;
void Start()
{
_animator = GetComponent<Animator>();
}
void OnMouseDown()
{
_animator.SetInteger("Clicks", _clicks);
if (_clicks == 3)
{
_clicks = 1;
}
else
{
_clicks++;
}
}
}
The state switching works fine but when I transition from FirstSplit to SecondSplit, the first animation is reverted so that the four medium cubes are driven back in the big cube before the second animation is played.
I tried enabling "Apply Root Motion" but that doesn't help (probably because the big cube is never actually moving?). So how can I "chain" these animations without resetting them in each step? Are my general ideas regarding the AnimationController and animation splitting correct to handle this and what am I missing?

How to give control back to animation in Unity3D once altering objects location

I have a character made of up child objects that are animated using Unity3D's animation system.
While the player is walking, I can programmatically move the hand object up to catch a ball using the following code.
hand.position.y = ball.transform.position.y;
I need the hand object to go back to following the walk animation after it touches the ball, but instead it just stays at the exact position since it was set.
You want to use inverse kinematics and let Unity do the work of figuring out positioning for you. Here's a quick-and-dirty (untested) example for catching a ball (it's in C#, but it should be pretty similar for UnityScript):
// in a script on the same GameObject as your animation controller
bool isCatching;
Transform ball;
void OnAnimatorIK (int layer) {
if (isCatching) {
// set position and rotation weights for your catching hand
animator.SetIKPosition(AvatarIKGoal.RightHand, ball.position);
animator.SetIKRotation(AvatarIKGoal.RightHand, ball.rotation);
} else {
// return your position and rotation weights back to their defaults (probably 0f?)
}
}
You'll need to do some work (possibly raycasting or just checking distance and direction) to determine when to set the isCatching flag to true, and you'll want to play with the weights for position and rotation to make it look natural. The IK manual entry has more detailed information.

Rigged mesh doesn't animate (despite the bones moving)

I have a model with a skeleton, all set up.
I've added the animations and Animation Helper to see the model move.
The result is odd - the bones are moving (according to animation helper rendering), but the actual model itself does not.
It's better explained with an gif:
Animation is attached to the mesh using:
animation = new THREE.Animation( mesh, geometry.animation );
Any suggestions on what might be wrong?
The solution was pretty simple actually:
for ( var k in materials ) {
materials[k].skinning = true;
}

Resources