Attempting to set rotation causes error

    
    if (isLeftButtonPressed) {
        rb.applyForce(this.leftDirection);
        
        console.log(this.player);
        this.player.setLocalEulerAngles( 30, 90, 60);
    }

Applying force works as expected, but as soon as I try to set the rotation, everything breaks. Here’s the link to the project: https://launch.playcanvas.com/987595

I’ve tried a few variations, but as soon as I try to rotate the player entity, errors. What’s happening?

Can i see the full script?

Yes, here it is:

var Gamepad = pc.createScript('gamepad');

Gamepad.attributes.add("leftButton", {type: "entity", default: "", title: "Left Button"});
Gamepad.attributes.add("rightButton", {type: "entity", default: "", title: "Right Button"});
Gamepad.attributes.add("upButton", {type: "entity", default: "", title: "Up Button"});
Gamepad.attributes.add("downButton", {type: "entity", default: "", title: "Down Button"});
Gamepad.attributes.add("jumpButton", {type: "entity", default: "", title: "Jump Button"});

Gamepad.attributes.add("leftDirection", {type: "vec3", default: [0,0,0], title: "Direction to move left"});
Gamepad.attributes.add("rightDirection", {type: "vec3", default: [0,0,0], title: "Direction to move right"});
Gamepad.attributes.add("upDirection", {type: "vec3", default: [0,0,0], title: "Direction to move up"});
Gamepad.attributes.add("downDirection", {type: "vec3", default: [0,0,0], title: "Direction to move down"});
Gamepad.attributes.add("jetpackSpeed", {type: "number", default: 1, title: "Jetpack speed"});

Gamepad.attributes.add("player", {type: "entity", default: "", title: "Player"});

var isLeftButtonPressed = false;
var isRightButtonPressed = false;
var isUpButtonPressed = false;
var isDownButtonPressed = false;
var isJumpButtonPressed = false;

var force;

// initialize code called once per entity
Gamepad.prototype.initialize = function() {
    
    this.leftButton.element.on('mousedown', this.onLeftPress, this);
    this.leftButton.element.on('mouseup', this.onLeftRelease, this);
    this.leftButton.element.on('touchstart', this.onLeftPress, this);
    this.leftButton.element.on('touchend', this.onLeftRelease, this);
    
    this.rightButton.element.on('mousedown', this.onRightPress, this);
    this.rightButton.element.on('mouseup', this.onRightRelease, this);
    this.rightButton.element.on('touchstart', this.onRightPress, this);
    this.rightButton.element.on('touchend', this.onRightRelease, this);
    
    this.upButton.element.on('mousedown', this.onUpPress, this);
    this.upButton.element.on('mouseup', this.onUpRelease, this);
    this.upButton.element.on('touchstart', this.onUpPress, this);
    this.upButton.element.on('touchend', this.onUpRelease, this);
    
    this.downButton.element.on('mousedown', this.onDownPress, this);
    this.downButton.element.on('mouseup', this.onDownRelease, this);
    this.downButton.element.on('touchstart', this.onDownPress, this);
    this.downButton.element.on('touchend', this.onDownRelease, this);
    
    this.jumpButton.element.on('mousedown', this.onJumpPress, this);
    this.jumpButton.element.on('mouseup', this.onJumpRelease, this);
    this.jumpButton.element.on('touchstart', this.onJumpPress, this);
    this.jumpButton.element.on('touchend', this.onJumpRelease, this);
    
    this.force = new pc.Vec3();
};

// update code called every frame
Gamepad.prototype.update = function(dt) {
    var rb = this.player.rigidbody;
    
    if (isLeftButtonPressed) {
        rb.applyForce(this.leftDirection);
        
        console.log(this.player);
        this.player.setLocalEulerAngles( 30, 90, 60);
    }
    if (isRightButtonPressed) {
        rb.applyForce(this.rightDirection);
    }
    if (isUpButtonPressed) {
        rb.applyForce(this.upDirection);
    }
    if (isDownButtonPressed) {
        rb.applyForce(this.downDirection);
    }
    if (isJumpButtonPressed) {
        this.force.copy(this.player.up).scale(this.jetpackSpeed);
        rb.applyForce(this.force);
    }
};


Gamepad.prototype.onLeftPress = function (event) {
    isLeftButtonPressed = true;
};
Gamepad.prototype.onLeftRelease = function (event) {
    isLeftButtonPressed = false;
};

Gamepad.prototype.onRightPress = function (event) {
    isRightButtonPressed = true;
};
Gamepad.prototype.onRightRelease = function (event) {
    isRightButtonPressed = false;
};

Gamepad.prototype.onUpPress = function (event) {
    isUpButtonPressed = true;
};
Gamepad.prototype.onUpRelease = function (event) {
    isUpButtonPressed = false;
};

Gamepad.prototype.onDownPress = function (event) {
    isDownButtonPressed = true;
};
Gamepad.prototype.onDownRelease = function (event) {
    isDownButtonPressed = false;
};

Gamepad.prototype.onJumpPress = function (event) {
    isJumpButtonPressed = true;
};
Gamepad.prototype.onJumpRelease = function (event) {
    isJumpButtonPressed = false;
};

@knipsch you can’t set EulerAngles whiles using rigidbodies try using teleport. (this.entity.rigidbody.teleport(x,y,z,rx,ry,rz);)

Ah okay, thank you!

I replaced setEulerAngles with teleport, but now I’m running into another issue. When I use teleport, it sets the position of the rigidbody, which cancels out the forces I’m trying to add. Is it not possible to add force and set rotation of an entity at the same time?

For now, I’ve created a child entity that rotates while physics forces act on the parent entity, but I have a feeling there might be some side effects (especially related to the compound collider I’m using). It’s a decent workaround, but not perfect.

There are a few ways to approach it and you just need to decide what suits you better.

It is a common practice to decouple an entity that represents a visual mesh, from the entity that holds a rigidbody/collision mesh. The body should be controlled by using the physical forces, like impulses, etc.

You can also read the current .rigidbody.linearVelocity and .angularVelocity before teleporting. You would teleport and then restore the velocities.

Doing it as you did, with child model using .setRotation() / .setPosition(), while the parent is controlled by physics forces is totally fine. Given you only rotate/move the visual model, and not the physics shape.

2 Likes

Hi, thanks so much for your detailed explanation! I really appreciate it. I’ve got things working pretty solidly at this point now.

For anyone reading this in the future, the weakness of handling this with a rotatable child visual model is that it doesn’t rotate in sync with the collider on its parent object, meaning the visual model clips through other objects sometimes. I more or less fixed this by having the parent collider entity snap to the visual model’s rotation using rigidbody.teleport at strategic times (in my case, on the first frame of the gamepad button press), and this looks good enough for me for now as a quick solution – but I’m guessing this is a case where reading the velocities, using teleport, and restoring the velocities might be better.

1 Like