[SOLVED] Multiplayer rotation with PlayCanvas and Photon

@Tirk182 I can try that and see if I can debug the code? About my previous question, is it necessary to add any new scripts or add more to the firstPersonMovement.js so that the player model moves in response to the camera?

@Tirk182
For the console.log in player.js, I always get returned a Vec3 with the same value, x = 0, y = -0, z = 0.
This is when logging : this.entity.getEulerAngles(). I haven’t coded it, as I don’t know how to make current player model rotate when I rotate the camera.

Are you using dynamic rigidbodies? Is the rotation of the player itself or the connected players not working?

I just checked the Photon tutorial and I see they are not using a rigidbody and camera for the player at all. That’s means you need to change the setup and scripts to make it work with your current project. @Tirk182 and @Alexis_Shaju are you aware of that?

@Albertos I believe I used from the beginning collisions and rigidbody’s for my firstPersonMovement script and it hasn’t affected Photon yet. However I don’t know how to code the rotation. I want the player who uses the screen basically, I want his body to rotate in the direction he rotates his camera, staying of course attached to the ground.

If you use a dynamic rigidbody (and you do), you cannot use setPosition() and setEulerAngles(). It looks like it works, but the rigidbody will not follow. You need to use the teleport() method to move and rotate your character. I assume this is also used in the movement script of your player.

https://developer.playcanvas.com/api/pc.RigidBodyComponent.html#teleport

But then what are you teleporting? The player itself? Or the rotation of the player? At the moment, it seems that the player moves, and enemy’s seem to be moving too to the player, when they press the respective keys, setPosition apparently is updating their position, but maybe not I don’t know.

You mentioned your own project, would I have to implement a script or add code like your lookAtMouse script into my code, for the player to follow the camera?

The visual entities are moving, but rigidbodies of the entities are not moving. That means there is collision on a position where the actual player not is anymore.

No. I assume the local player is already moving and rotating using the correct teleport() method. Can you check this is your player movement script please?

@Albertos
At the moment, there is no teleport function at all in my movement script, and the player itself is not rotated, I only have a setEulerAngles for the camera, to prevent it from doing a 360, as we want it to represent the fps player.
Here is the code we are using:

let FirstPersonMovement = pc.createScript('firstPersonMovement');

FirstPersonMovement.attributes.add('camera', {
    type: 'entity',
    description: 'Optional, assign a camera entity, otherwise one is created'
});

FirstPersonMovement.attributes.add('player', {
    type: 'entity',
});

FirstPersonMovement.attributes.add('power', {
    type: 'number',
    default: 2500,
    description: 'Adjusts the speed of player movement'
});

FirstPersonMovement.attributes.add('lookSpeed', {
    type: 'number',
    default: 0.25,
    description: 'Adjusts the sensitivity of looking'
});


let jumpForce = 1000;

let grounded = false;

// initialize code called once per entity
FirstPersonMovement.prototype.initialize = function() {
 
    this.app.keyboard.on(pc.EVENT_KEYDOWN, this.onKeyDown, this);
    this.app.keyboard.on(pc.EVENT_KEYUP, this.onKeyUp, this);
    this.entity.collision.on('collisionstart', this.onCollisionStart, this);

    this.force = new pc.Vec3();
    this.eulers = new pc.Vec3();
    
    let app = this.app;

    // Listen for mouse move events
    app.mouse.on("mousemove", this._onMouseMove, this);

    // when the mouse is clicked hide the cursor
    app.mouse.on("mousedown", function () {
        app.mouse.enablePointerLock();
    }, this);

    // Check for required components
    if (!this.entity.collision) {
        console.error("First Person Movement script needs to have a 'collision' component");
    }

    if (!this.entity.rigidbody || this.entity.rigidbody.type !== pc.BODYTYPE_DYNAMIC) {
        console.error("First Person Movement script needs to have a DYNAMIC 'rigidbody' component");
    }

    this.camera = this.app.root.findByName("Camera");
};

// update code called every frame
FirstPersonMovement.prototype.update = function(dt) {
    // If a camera isn't assigned from the Editor, create one
    if (!this.camera) {
        this._createCamera();
    }

    let force = this.force;
    let app = this.app;

    // Get camera directions to determine movement directions
    let forward = this.camera.forward;
    let right = this.camera.right;

    let pos = new pc.Vec3(0, 0, 0);

    // movement
    let x = 0;
    let y = 0;
    let z = 0;

    let rotY = 0;

    // Use W-A-S-D keys to move player
    // Check for key presses

    if (app.keyboard.isPressed(pc.KEY_A)) {
        x -= right.x;
        z -= right.z;
        pos.x = -dt;
    }

    if (app.keyboard.isPressed(pc.KEY_D)) {
        x += right.x;
        z += right.z;
        pos.x = dt;
    }

    if (app.keyboard.isPressed(pc.KEY_W)) {
        x += forward.x;
        z += forward.z;
        pos.z = -dt;
    }

    if (app.keyboard.isPressed(pc.KEY_S)) {
        x -= forward.x;
        z -= forward.z;
        pos.z = dt;
    }

    if (this.app.keyboard.isPressed(pc.KEY_SHIFT)){
        this.power = 1000;
    }
    else
    {
        this.power = 2500;
    }

    if (this.app.keyboard.isPressed(pc.KEY_SPACE) && grounded === true) {
        this.entity.rigidbody.applyImpulse(0, jumpForce, 0); // Apply Impulse
        grounded = false; // Set onGround variable to false
    }

    if(this.app.keyboard.isPressed(pc.KEY_SPACE)){
        pos.y = dt;
    }

    if (!pos.equals(new pc.Vec3(0, 0, 0))) {
        this.entity.translate(pos);

        // Added
        const { x, y, z } = this.entity.getPosition();
        this.app.fire("loadbalancing:sendPlayerPosition", { x, y, z });
    }
    
    // use direction from keypresses to apply a force to the character
    if (x !== 0 && z !== 0) {
        force.set(x, 0, z).normalize().scale(this.power);
        this.entity.rigidbody.applyForce(force);
    }

    // update camera angle from mouse events // math.clamp pre7ents looking further than +-60%
    this.eulers.y = pc.math.clamp(this.eulers.y, -70, 70);
    this.camera.setLocalEulerAngles(this.eulers.y, this.eulers.x, 0);
};

FirstPersonMovement.prototype._onMouseMove = function (e) {
    // If pointer is disabled
    // If the left mouse button is down update the camera from mouse movement
    if (pc.Mouse.isPointerLocked() || e.buttons[0]) {
        this.eulers.x -= this.lookSpeed * e.dx;
        this.eulers.y -= this.lookSpeed * e.dy;
    }
};

FirstPersonMovement.prototype.onCollisionStart = function(result) {
    // Ground Check
    if (result.other.tags.has('ground')) {
        grounded = true;
    }    
};

FirstPersonMovement.prototype._createCamera = function () {
    // If user hasn't assigned a camera, create a new one
    this.camera = new pc.Entity();
    this.camera.setName("First Person Camera");
    this.camera.addComponent("camera");
    this.entity.addChild(this.camera);
    this.camera.translateLocal(0, 0.5, 0);
};

I see, you use applyForce() and that is also for dynamic rigidbodies.

https://developer.playcanvas.com/en/tutorials/Using-forces-on-rigid-bodies/

Maybe you can use the rotation of the camera to get the correct rotation for the connected players, but I’m not sure about that.

@Albertos So does that mean, the body is indeed being moved, by this applyForce to the rigidbody?
And now I only need to write a teleport function for a rotation?
I can try work on that and see where that leads. If this works, I think I should be able to reproduce it into photon.

For the local player everything is good. For the connected players you need to use teleport() to apply the new position and rotation, just like I do in the example project I shared. You need to try to modify this for your Photon script, but the basics are the same.

Would this teleport be instead of the setPosition? setPosition seems to be working ok, but maybe it does not update the position. Teleport is giving me errors.

Yes, it does update the entity but not the rigidbody of the entity.

What you can do is create a different player entity for your connected players. This is also done in the example project I shared. You can give this entity a kinematic rigidbody so you can keep using setPosition(). I think this entity does not need a camera.

@Albertos I can try this.

@Tirk182 Would it be possible to recommend me a way to go about player rotations, as I need to handle it in a 3d environment. The more simpler the code the better. I had a project that was able to do it, for third person games, but I wish to be able to implement this for first person and I prefer to have my model layed out as it is. WIth the player template, having a camera.

@Alexis_Shaju I believe that some of the examples do cover the player rotation. One of the things to be aware of in the first person is that what really gets rotated is the camera attached to the instance itself. And not the actual rigid body. The action on the rigid body only moves in the x/z area. So from inside the first person character you move the camera when you move the mouse. But… you need to have the first person view see the other players which are actually clones which represent the players in the game room including their rotation. i.e. maybe see their eyes or head point in the direction the are going. There should be no changes to current operations but you must send the rotation threw the using the method I have described. I will try to send again another example towards the end of the day.

@Alexis_Shaju More information on how to handle player rotation in multiplayer. Remember that all of the actors in the game room are just clones and are getting updates from the multiplayer link. First is in player.js

        player: {
            name: this.app.loadBalancing.myActor().name,
            playerMat: this.app.loadBalancing.myActor().getCustomProperty('Mat'),
            playerRot: this.tmpRotx,
        }

Added another variable to pass which is rotation.

// Update player changes
Player.prototype.postUpdate = function () {
    const playerPos = this.entity.getPosition();
    const playerRot = this.entity.script.fpsController.eulers.x;

    // Handle changes in player direction
    if (!this.tmpVec.equals(playerPos)) {
        this.tmpVec = playerPos.clone();        
        this.raiseEvent();
    }

    // Handle Changes in player rotation
    if (this.tmpRotx != playerRot) {
        this.tmpRotx = this.entity.script.fpsController.eulers.x;
        this.raiseEvent();        
    }

};

This is the update function. What I use for rotation is the update inside of the first person controller script’s mouse rotation variable. This best represents in my case the movement of the players rotation.

Since the event is raised on change then in game-room.js I do the following.

GameRoom.prototype.updatePlayerPosition = function (content, actorNr) {

    const { x, y, z } = content.position;
    const playerData = content.player;
    const player = this.entity.findByName(actorNr);
   

    // Set Rotation
    player.setLocalEulerAngles(0,playerData.playerRot,0);

    // Set the position
    player.setPosition(x, y, z);

};

@Tirk182 This works like a charm, thank you so much for this. I understand the player.js and the GameRoom what you are doing.
The PostUpdate of the player.js is where I am a bit lost. If I am understanding correctly, you are getting the player rotation from the x value of the eulers, which is in the fps script. And this eulers is what belongs to the camera itself, so whenever the camera moves, no matter the y and z values of its movement, the enemy player is passed the x value of this camera movement, and it is reflected in the player moving?

Would this be the correct understanding or am I missing something?

@Alexis_Shaju you have it mostly correct. The capsule based FPS player that is the real thing that is moving uses physics. So it’s direction is controlled by applying force. Inside the player capsule is the camera which the mouse controls is rotation and the up/down angle. But… the camera rotates inside the player and does not physically rotate the rigid body itself. So the data we are sending from each player in player.js is x,y, and z position. And then we use the camera rotation to capture what the player is looking at. These are the important items we send.

In the game room we are just feeding the clones in the view of the actual player data from the other players in the room.

Thank you for this explanation. It made it a lot clearer.

1 Like