Rigidbody.teleport() has weird behavior

I’m replicating dynamic entities by guid values on my multiplayer game, it’s successfully syncing entity state for the most part considering I used realtime multiplayer example as a start reference.

This line causes the target entity to have a stutter/glitch like movement during update to server yet this doesn’t occur with player movements.

clientNetEntity.rigidbody.teleport(entityData.objPosX, entityData.objPosY, entityData.objPosZ, entityData.objRotX, entityData.objRotY, entityData.objRotZ);

network.js - Entity Replication Code

// Called every frame in Network.prototype.update()
Network.prototype.updateNetEntityPosition = function () {
    if (this.initialized) {
        var gameObjects = this.game.findByTag('netDynamic');
        var gameObjLength = gameObjects.length;
        
        for (var i = 0; i < gameObjLength; i++) {
            var obj = gameObjects[i]; // Not passed to Server
            var objName = obj.name; // Not passed to Server
            var objGuid = obj._guid;
            var objPos = obj.getPosition(); 
            var objRot = obj.getRotation();
            var objAngl = objRot.getEulerAngles();
                            
            //console.log(objName + ': ' + objGuid + '| pos: ' + objPos);
            
            var clientNetEntity = this.game.findByGuid(objGuid); // Locate entity in scene
            
            // Update per frame if entity's velocity changed
            if (clientNetEntity.enabled && clientNetEntity.rigidbody.linearVelocity.lengthSq() > 0.01) {
                
                // Prevents newly connected clients from updating entity positions for already connected clients (Dynamic objects fall due to mass and gravity)
                if (this.shouldEmitUpdate) {
                    //console.log('Alert Network', objName, 'moved to', objPos);
                    
                    Network.socket.emit('netEntityPositionUpdate', {objGuid: objGuid, objPosX: objPos.x, objPosY: objPos.y, objPosZ: objPos.z, objRotX: objAngl.x, objRotY: objAngl.y, objRotZ: objAngl.z}); // Network emits signal to moveNetEntity() next
                }
            }
        }
    }
};

Network.prototype.moveNetEntity = function (entityData) {
    if (this.initialized) {
        if (this.netEntities !== null && this.netEntities[entityData.objGuid]) {
            var clientNetEntity = this.game.findByGuid(entityData.objGuid); // Locate entity in scene
            var netEntityPos = new pc.Vec3(entityData.objPosX, entityData.objPosY, entityData.objPosZ);
                        
            if (clientNetEntity.enabled && clientNetEntity.getPosition() !== netEntityPos) {
                //console.log('Network updated position:', clientNetEntity.name);
                
                //clientNetEntity.setPosition(entityData.objPosX, entityData.objPosY, entityData.objPosZ, entityData.objRotX, entityData.objRotY, entityData.objRotZ);
                clientNetEntity.rigidbody.teleport(entityData.objPosX, entityData.objPosY, entityData.objPosZ, entityData.objRotX, entityData.objRotY, entityData.objRotZ);
            }
        }
    }
};

A temporary yet not suggested fix has been to use .setPosition on the dynamic entity as it does smoothly move the entity for all clients however sometimes it can be inaccurate/take time to update to proper position.

Below is the code that replicates player position state, it smoothly moves all clients with rigidbody.teleport yet on entities such code isn’t effective.

network.js - Player Replication Code

// update code called every frame
Network.prototype.update = function(dt) {
    this.updatePosition();
    
    if (this.netEntities !== null) {
        this.updateNetEntityPosition();
    }
};

Network.prototype.updatePosition = function () {
    if (this.initialized) {    
        var pos = this.player.getPosition();
        var rot = this.player.getRotation();
        var angl = rot.getEulerAngles();
        
        if (this.player.getPosition().y < -7) {
            this.player.rigidbody.teleport(0, 5, 0, angl.x, angl.y, angl.z);
        }
                
        Network.socket.emit('positionUpdate', {id: Network.id, x: pos.x, y: pos.y, z: pos.z, rotX: angl.x, rotY: angl.y, rotZ: angl.z}); // Network emits signal to movePlayer() next
    }
};

Network.prototype.movePlayer = function (data) {
    if (this.initialized && this.players[data.id]) {
        this.players[data.id].entity.rigidbody.teleport(data.x, data.y, data.z, data.rotX, data.rotY, data.rotZ);
        
        //console.log('moveAndOrientate:', data.x, data.y, data.z, 'rotX:', data.rotX, 'rotY:', data.rotY, 'rotZ:', data.rotZ);
        //console.log('Moving:', this.players[data.id].entity.name);
        //console.log(this.players[data.id].entity.children[1]);
    }
};

The entities in editor properties such as Linear/Angular Damping/Factors are matching so I don’t understand why this is occurring. I also prevent updates to the server unless the entity rigidbody velocity changes. Does anyone know why this is happening for tracked entities and not players?

Can you post a video of the behaviour please?

It could be an update order issue, a network rate (how often it gets updates), etc. A video should hopefully help

1 Like

Here’s the Gif

@yaustar Hmm you’re probably right but it shouldn’t be Network related as an entity only updates the server on velocity change. The player positions updates every frame however and operates smoothly which you can see in the provided gif

Is the cube a dynamic rigidbody?

Yes both cubes have dynamic bodies so players can move them around, which is why I implemented them over the network so each client can just replicate the physics based on the entity position values passed from server.

In which case, my guess is that what you are seeing is the cube being moved by the local physics simulation and then being teleported by the network code. As the two aren’t in sync, it looks glitchy.

Networked physics is very hard to do to the point where networked games don’t use full physics for gameplay elements.

Eg Counterstrike is mostly nav mesh casting and very basic physics that can be done deterministically on the server and clients. Rollback is used to ensure its in sync on the client to make up for latency.

Some videos on the topic:

https://www.google.com/url?sa=t&source=web&rct=j&url=https://m.youtube.com/watch%3Fv%3D7jb0FOcImdg&ved=2ahUKEwjxsajBysPwAhVCA2MBHVA_C6MQwqsBegQIIxAE&usg=AOvVaw12-KNjnqrfbGbQLZuYi5N-

https://www.google.com/url?sa=t&source=web&rct=j&url=https://m.youtube.com/watch%3Fv%3DW3aieHjyNvw&ved=2ahUKEwje8tC2ysPwAhUL8hQKHfaSDrEQwqsBegQIBhAE&usg=AOvVaw3fX08hqDJzSEPzRsBDNXfM&cshid=1620803295144

2 Likes

I guess it’s just gonna take further hacking and prioritizing what can be implemented. Fairly dull videos, though the input is appreciated.

As a hack, I wonder if you can make it so that networked sync entities don’t collide with each other (eg make them kinematic)? That way, they won’t move according to the current clients physics simulation and collision?

1 Like

@yaustar I like how you think!

I started implementing it like such considering it’s a sandbox game and having physics on many items hassles the FPS a bit, making them static is a simple hack until I come back to it.
Thanks!