[SOLVED] ApplyForce velocity accumulation

Hello, I recently discovered PlayCanvas and tried to do a test regarding rigidbody and raycasts.

I have a simple project with a skateboard, where I want a force to be added in the point of the board that was clicked. It does somewhat work, but after each click it seems that the force is accumulating itself until the board is simply thrown off the platform at a very high speed and flipping too much.

I’ve checked if the click function was being added each time it was performed, but it doesn’t (at least not by logging how many times it got called). I tried editing the rigidbody mass and dampings but the results are the same… Am I missing something? I’m used to Unity so I might have overlooked something that behaves differently…

Link to scene: https://playcanvas.com/editor/scene/1077939

(I’m only using Jump.js and Raycaster.js, the others are from the sample scene and not being used)

EDIT: I guess it’s worth mentioning this does not happens if I remove the vec3 forcepoint parameter of ApplyForce, but then I don’t get the flipping I want.

That’s a bit weird.

ApplyForce is meant to be used over a period of time and for this where you want an instant response, applyImpulse should be used instead. I still can’t explain this behaviour though.

Ah, looking at the API, the forcepoint is an offset from the position of the rigidbody, not the world position.

1 Like

Hmm, I think you want rigidbody.applyImpulse() (API), instead of applying a force. A force should be used for applying a constant force on a rigidbody every frame, like a gravity or some magnet. Impulses are more suited for one-time hit-and-forget type of scenarios.

Also note, that the relative point you supply as a second argument is in local space of the object. Currently you are providing a world space coordinate of wherever you clicked. If the board is standing in the world center, then the clicked point in world space will be the same for both the world and the board. But once the board moves, then the clicked point can no longer be used as a relative point.

2 Likes

Oh, so if I had to pick at which spot specificly on the board I wanted, should I get the point from the raycast result, get the position from the board with entity.getPosition (which I suppose will get the center point of the board) and add them both?

You need to get the difference :slight_smile:

1 Like

Whoops… :joy:

I managed to fix it by getting the diff between the two pc.Vec3 (the board and the raycast result world point) into a vec3 that I use as a parameter to the flip function now.

var Raycaster = pc.createScript('raycaster');



// initialize code called once per entity
Raycaster.prototype.initialize = function() {
    if (!this.entity.camera) {
        console.error('This script must be applied to an entity with a camera component.');
        return;
    }
    
    this.app.mouse.on(pc.EVENT_MOUSEDOWN, this.mousedown, this);
};

Raycaster.prototype.mousedown = function(e){

    //console.log("clicked!");
    this.doRaycast(e);
    
};

Raycaster.prototype.touchStart = function (e) {
    // Only perform the raycast if there is one finger on the screen
    if (e.touches.length === 1) {
        this.doRaycast(e.touches[0].x, e.touches[0].y);
    }
    e.event.preventDefault();
};

Raycaster.prototype.doRaycast = function (screenPos) {
    // The pc.Vec3 to raycast from (the position of the camera)
    var from = this.entity.getPosition();

    // The pc.Vec3 to raycast to (the click position projected onto the camera's far clip plane)
    var to = this.entity.camera.screenToWorld(screenPos.x, screenPos.y, this.entity.camera.farClip);

    // Raycast between the two points and return the closest hit result
    var result = this.app.systems.rigidbody.raycastFirst(from, to);

    // If there was a hit, store the entity
    if (result) {
        var hitEntity = result.entity;
        console.log('You selected ' + hitEntity.name);
        
        var boardVec3 = result.entity.getPosition();
        
        var worldVec3 = result.point;
        
        var diffVec = new pc.Vec3(worldVec3.x - boardVec3.x, worldVec3.y - boardVec3.y, worldVec3.z - boardVec3.z);
        
        if (hitEntity.name == 'Board'){
            hitEntity.script.jump.flip(diffVec);    
        }
    }
    else console.log ('did not find a valid target!');
};


// swap method called for script hot-reloading
// inherit your script state here
// Raycaster.prototype.swap = function(old) { };

// to learn more about script anatomy, please read:
// http://developer.playcanvas.com/en/user-manual/scripting/

I also switched from ApplyForce to ApplyImpulse on jump.cs:

var Jump = pc.createScript('jump');

var self = this;

var timesCalled = 0;


Jump.attributes.add('power', {
    type: 'number',    
    default: 1,
    min: 0.5,
    max: 500,
    precision: 2,
    description: 'Controls the maximum jump power from the board'
});


Jump.prototype.flip = function(boardPos){
    
    timesCalled++;
    
    console.log("flip!");
    //console.log(this.entity.rigidbody);
    //console.error(this.power);
    this.entity.rigidbody.applyImpulse(0, this.power, 0, boardPos.x, boardPos.y, boardPos.z);

};

//this is only a debug function to check how many time flips was called and the board's current position, I was using that to check if there wasnt any double calls.
Jump.prototype.getCoordinates = function(){
    
    var pos = this.entity.getPosition();
    
    console.error(pos);
    console.log(timesCalled);
    
};

Jump.prototype.update = function(dt){

 if (this.app.keyboard.wasPressed(pc.KEY_P)) {
        this.entity.script.jump.getCoordinates();
    } 
    
};



// swap method called for script hot-reloading
// inherit your script state here
// Jump.prototype.swap = function(old) { };

// to learn more about script anatomy, please read:
// http://developer.playcanvas.com/en/user-manual/scripting/

Though I have no clue about the applyForce behavior…

2 Likes