Smooth LookAt with Tween?

Right now i use a lot of code to get a smooth look at function. But is there a way to use Tween for that? (I already try a couple of Tween options but i don’t get it worked with Tween).

Here is one way to do it:

    var lookFrom = new pc.Vec3(startPos.x, startPos.y, startPos.z);

    var tween = this.app
    .tween(lookFrom)
    .to( lookΤο, duration, pc.SineInOut)
    .on('update', function () {
        
        this.cameraEntity.lookAt(lookFrom);
        
    }.bind(this) )
    .start();

I have a few problems with this way. Maybe it’s bacause i want to use it in the update?
It also seems that changing the duration, for example, from 1 to 5 makes no difference.

You shouldn’t be using tweens in your script update method. The last method of the tween, start(), registers the tween to the tween.js update cycle.

That takes care of updating, so normally you should initialize a tween only once, usually in your script initialize method.

Now i have this in the update:

    if (!playerIsClimbing) {
        var lookFrom = new pc.Vec3(this.entity.getPosition().x, this.entity.getPosition().y, this.entity.getPosition().z);

        var tween = this.app
        .tween(lookFrom)
        .to(new pc.Vec3(mousePoint.getPosition().x, this.entity.getPosition().y, mousePoint.getPosition().z), 5, pc.SineInOut)
        .on('update', function () {

            this.entity.lookAt(lookFrom);

        }.bind(this) )
        .start();

        //this.rotateTowardThis = new pc.Vec3(mousePoint.getPosition().x, player.getPosition().y, mousePoint.getPosition().z);
    }
    
    else {
        //this.rotateTowardThis = null;
    }

The player is looking at the mouse point but not stable (i think because it try to move the y also). The duration has no effect.

You should initialize your tween only once with your from/to values and get a handle from it:

        this.tween = this.app
        .tween(lookFrom)
        .to(new pc.Vec3(mousePoint.getPosition().x, this.entity.getPosition().y, mousePoint.getPosition().z), 5, pc.SineInOut)
        .on('update', function () {

            this.entity.lookAt(lookFrom);

        }.bind(this) );

Then in your gameplay loop/event handlers when you want to enable it you call:

this.tween.start();

And to stop it:

this.tween.stop();

If your look from/to values change you will have to initialize your tween again (once!) and keep using the start/stop methods.

If i initialize and also start it at the start of the game, the player rotate a little bit (also the y, what is not good) and then nothing happend anymore.

That’s why I said, initialize it without starting it, get a handle to the tween and use that handler to start it later.

Check the code samples I’ve posted.

Yes but where i have to start it if i can’t start it at the begin of the game and also can’t place it in the update (what gives the same result). :crazy_face:

You can place it in the update loop as long as you make sure that you are initializing it only once. That’s very specific to your game loop logic/context.

For example one way of doing it:

if( this.tween === undefined){

        this.tween = this.app
        .tween(lookFrom)
        .to(new pc.Vec3(mousePoint.getPosition().x, this.entity.getPosition().y, mousePoint.getPosition().z), 5, pc.SineInOut)
        .on('update', function () {

            this.entity.lookAt(lookFrom);

        }.bind(this) )
        .start();

} 

Since this.tween will be undefined initially, when in your update loop you reach that point it the code will run once since this.tween will start referencing the tween (so it won’t be undefined).

It’s nog working (maybe because i don’t understand). The mouse is moving all the time so i don’t understand how to do it once. If i use your examples the player only rotates once at the begin.

Here is an example of smooth lookAt tweens in an update loop:

https://playcanvas.com/editor/scene/863141

Now if you would like a smooth mouse movement I wouldn’t propose doing it with tweens, but adding some inertia to your 3D maths.

This can be a bit complex but the default Playcanvas model viewer starter kit implements that, so you can study that code.

No, i don’t need a smooth mouse movement. The player character has to look always at the mouse point. I think you have played my game so you know how it works right now. It is good, but i like to do it with Tween if that was possible.

1 Like

Sounds like you would want something like Unity’s rotaretowards function that moves towards where the mouse pointer is at a constant speed?

Yes, right know i use this solution:

    if (!playerIsClimbing) {
        this.rotateTowardThis = new pc.Vec3(mousePoint.getPosition().x, player.getPosition().y, mousePoint.getPosition().z);
    }
    
    else {
        this.rotateTowardThis = null;
    }
Player.prototype.updateLookAt = function (dt) {
    if (this.rotateTowardThis !== null) {
        this.rotateTime += dt; 
        var percent = this.rotateTime / 5;

        var original_rotation = new pc.Quat();
        var final_rotation = new pc.Quat();

        original_rotation.copy(this.entity.getRotation());
        this.entity.lookAt(this.rotateTowardThis); 
        final_rotation.copy(this.entity.getRotation());

        this.entity.setRotation(original_rotation);

        var new_rotation = this.rotateTowards(original_rotation, final_rotation, 0.05);

        this.entity.setRotation(new_rotation);

        if (percent > 0.05){
            this.rotateTowardThis = null;
            this.rotateTime = 0;
        }
    }
};

// Quat functions
// Get the dot product between two quaternions
Player.prototype.dot = function (quat_left, quat_right) {
    var dot = quat_left.x * quat_right.x + quat_left.y * quat_right.y + 
        quat_left.z * quat_right.z + quat_left.w * quat_right.w;

    return dot;
};

// Returns the angle in degrees between two rotations /a/ and /b/.
Player.prototype.quatAngle = function (quat_a, quat_b) {
    var dot = this.dot(quat_a, quat_b);
    
    if(quat_a.equals(quat_b) )
    {
        return 0;
    }        
    
    var rad2Deg = 1 / (Math.PI / 180);

    var angle = Math.acos(Math.min(Math.abs(dot), 1)) * 2 * rad2Deg;

    return angle;   
};

// Rotates a rotation A towards B.
Player.prototype.rotateTowards = function (quat_a, quat_b, percent) {
    var angle = this.quatAngle(quat_a, quat_b);
        
    if (angle === 0)
    {
        return quat_b;
    }
    
    return new pc.Quat().slerp(quat_a, quat_b, percent); 
};