[SOLVED] How to use a Bezier Curve

It’s been a day since I’m trying to recreate Bezier Curve in playcanvas I’ve tried so many ways but can’t make it
The only thing I could make is smoothly move game object to clicked object so it would be nice if someone could give me a hint to make this code done
I’ve been only using csharp and unity so far, I’ve only been using playcanvas for the first day, so it was pretty hard to get used to JS

var MovementController = pc.createScript('movementController');

MovementController.attributes.add('playerEntity', {type: 'entity', title: 'Player Entity'});
MovementController.attributes.add('desiredTime', {type: 'number', default: 0, title: 'Desired Time'});
MovementController.attributes.add('numberOfPoints', {type: 'number', default: 0, title: 'Number Of Points'});
MovementController.attributes.add('entityCamera', {type: 'entity', title: 'Camera'});

MovementController.tempVec3 = new pc.Vec3();
MovementController.startPosition = new pc.Vec3();
MovementController.tempEntity = new pc.Entity();
MovementController.closestEntity = new pc.Entity();

MovementController.prototype.initialize = function()
{
    this.pointsArray = this.app.root.findByTag("Points");
    this.elapsedTime = 0;
    this.percentageComplete = 0;
    this.distance = 0;
    this.startPosition = this.entity.getPosition();
    this.currentDist = 0;
    this.startDist = 0;
    this.app.mouse.on(pc.EVENT_MOUSEDOWN, this.onSelect, this);

    this.on('destroy', function() {
        this.app.mouse.off(pc.EVENT_MOUSEDOWN, this.onSelect, this);
    }, this);

    var tempPos = this.entity.getPosition();
    this.pointsArray[0].setPosition(tempPos);

    console.log("start method");
};

MovementController.prototype.update = function(dt)
{

    if(typeof this.tempEntity == 'undefined')
        return;
    if(typeof this.closestEntity == 'undefined')
    {
        console.log("once");
         this.closestEntity = this.findClosestCube(this.tempEntity);
    }


    this.elapsedTime += dt;
    this.numberOfPoints += dt;
    this.percentageComplete = clampBetween01(this.elapsedTime)/this.desiredTime;

    if(this.app.keyboard.wasPressed(pc.KEY_SPACE))
    {
        this.tempEntity = this.pointsArray[0];
    }


    this.currentDist = new pc.Vec3().distance(this.startPosition, this.tempEntity.getPosition());

    this.tempVec3 = calculateQuadraticBezierPoint(this.startPosition, this.closestEntity.getPosition(), this.tempEntity.getPosition(), this.numberOfPoints);
    var lerpedRotation = new pc.Quat().slerp(this.entity.getRotation(), this.tempEntity.getRotation(), this.percentageComplete);
    
    this.distance = new pc.Vec3().sub2(this.entity.getPosition(), this.tempVec3).length();
    this.entity.setRotation(lerpedRotation);
    this.entity.setPosition(this.tempVec3);

    if(this.distance < 0.0001)
    {
        var tempPos = this.tempVec3;
        var tempQuat = this.tempEntity.getRotation();
        this.entity.setPosition(tempPos);
        this.entity.setRotation(tempQuat);
        this.startPosition = tempPos;
    }

};

MovementController.prototype.findClosestCube = function(cube)
{
    var closest = null;
    var distance = 1000;
    for (var i = 0 ; i< this.pointsArray.length ; i++) 
    {
        var diff = this.pointsArray[i].getPosition().sub(cube.getPosition());
       
        var curDistance = diff.lengthSq();
        if (curDistance < distance && this.pointsArray[i] != this.tempEntity) 
        {
            closest = this.pointsArray[i];
            distance = curDistance;
        }
    }
    console.debug(closest.name);

    this.currentDist = new pc.Vec3().distance(this.startPosition, this.tempEntity.getPosition());
    this.startDist = this.currentDist;
    this.numberOfPoints = this.currentDist/this.startDist; 


    return closest;
};

MovementController.prototype.onSelect = function (e)
{
    var from = this.entityCamera.camera.screenToWorld(e.x, e.y, this.entityCamera.camera.nearClip);
    var to = this.entityCamera.camera.screenToWorld(e.x, e.y, this.entityCamera.camera.farClip);


    var result = this.app.systems.rigidbody.raycastFirst(from, to);
    if (result)
    {
        this.tempEntity = result.entity;
        this.startPosition = this.entity.getPosition();
    }

};

also this is bezier code

var BezierCurve = pc.createScript('bezierCurve');

// initialize code called once per entity

BezierCurve.prototype.initialize = function()

{

   

};

function calculateLinearBezierPoint(point1, point2, time)

{

    return point1 + time * (point2 - point1);

}

function calculateQuadraticBezierPoint(point1, point2, point3, t)

{

    var u = 1 - t;

    var tt = t * t;

    var uu = u * u;

    var bPoint = new pc.Vec3(point1.x * uu, point1.y * uu, point1.z * uu);

    bPoint.add(new pc.Vec3(2 * u * t * point2.x, 2 * u * t * point2.y, 2 * u * t * point2.z));

    bPoint.add(new pc.Vec3(point3.x * tt, point3.y * tt, point3.z * tt));

   

    return bPoint;

}

Hi @insainityyy,

Check this project, it shows how to draw a bezier curve using a third party library:

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


is this supposed to be like this?
btw, there is app.drawLine for debug purposes
what an awesome news

1 Like

Yes, that’s a minified library. It’s like that to reduce the download size. You don’t need to read/edit that file, check the PlayCanvas script on how it’s being used.

The original library can be found here: Bezier.js, for doing Bezier curve things

1 Like

Can I still share my issue here?
So my problem is a value that’s called

this.spawnPosition = 0;

I assign it to spawn position of my game object in initialize method

 this.spawnPosition = this.entity;

and then, on the pressed space button I assign finish position to this spawn position

MovementController.prototype.onSpace = function (e)
{
    this.finishEntity = this.spawnPosition;

};

and for some magic reasons, my spawn position is the same as finishEntity position! always!
Even though I’m not doing anything with spawn position besides that

So two things, if you are trying to get the position you should be using getPosition() on the entity. That will return a reference of the entity position, if you’d like to get two different position on start/end, you need to clone that vector.

Example:

this.spawnPosition = this.entity.getPosition().clone();
1 Like

Clone works great, thanks!
is there any reason why direct assignment does not work the way it should work in principle?

Apart from number, string, boolean value types, everything else in JavaScript is being assigned by reference.

That means your this.spawnPosition property didn’t really hold the full position data, but a reference to a pc.Vec3. That pc.Vec3 returned by getPosition() is used internally by the entity to update the position of it per frame.

So as soon as the next frame renders the position of that Vec3 updates and changes. And your this.spawnPosition property reflects that.

But if you clone that vector as we do now, it holds a reference to a new pc.Vec3 that isn’t being referenced by any other object and its position never changes.

1 Like

huh, didn’t know about that one! (once again I mostly use c#)
So this means I should always use clone() if I want to “assign” anything that is not number, string, boolean type value?

Clone is a public method of the Vec3 (and Vec2, Vec4) classes, you can see it here. It’s not available in most other JavaScript objects unless a developer or you has created.

https://developer.playcanvas.com/api/pc.Vec3.html

In general when developing with JavaScript you should keep in mind that most assignments are by reference. There isn’t a need to clone/assign by value always, references are good. They not only reduce memory usage but make coding powerful by allowing different parts of your codebase to use the same object.

So it’s a matter of what you need and how you use it.

image
image
why does this code start doing its task, even if it does not meet the requirements? lol
Maybe I haven’t slept enough to notice the mistake, but this confuses me a lot

https://developer.playcanvas.com/api/pc.Vec3.html
so basically we compare two vec3 with a.equals(vec3) function!