javascriptphaser-framework

Why does the client stop seeing the movement of other players in an online game after switching the scene in Phaser?


If you do not try to move to another location, the player sees the other player and his movement, otherwise the player sees only the player, but not his movement. I tried to clear the groups, but it didn't help. I want you to see the movement of the other player when you switch the scene, if there is one there. I use phaser 3, mysql2.

Client code

const socket = io();

class BaseScene extends Phaser.Scene {
    constructor(key) {
        super({ key });
        this.spawned = false;
        this.otherPlayers = {};
        this.nicknames = {};
    }

    create() {
        const self = this;
        this.cursors = this.input.keyboard.createCursorKeys();
        this.otherPlayers = this.physics.add.group();

    }

    update() {
        if (this.player && this.player.body) {
            let moved = false;

            if (this.cursors.left.isDown) {
                this.player.setVelocityX(-160);
                moved = true;
            } else if (this.cursors.right.isDown) {
                this.player.setVelocityX(160);
                moved = true;
            } else {
                this.player.setVelocityX(0);
            }

            if (this.cursors.up.isDown) {
                this.player.setVelocityY(-160);
                moved = true;
            } else if (this.cursors.down.isDown) {
                this.player.setVelocityY(160);
                moved = true;
            } else {
                this.player.setVelocityY(0);
            }

            if (moved) {
                socket.emit('playerMovement', { x: this.player.x, y: this.player.y });
            }
        }
    }
}
class FirstScene extends BaseScene {
    constructor() {
        super('first');
    }

    create() {
        super.create();
        this.add.text(10, 10, 'LOCATION 1');

        socket.emit('createPlayer');

        socket.on('playerCreated', (playerData) => {
            addPlayer(this, playerData);
            this.trigger = this.add.rectangle(400, -12, 100, 100, 0xffffff).setDisplaySize(50, 50);

            this.physics.add.existing(this.trigger, true);
            this.physics.add.collider(this.player, this.trigger, (player, trigger) => {
                socket.emit('TriggerFirstScene', player);
            }); 
            });

        socket.on('playerMoved', (playerData) => {
            playerMoved(this, playerData);
        });

        socket.on('newPlayer', (players) => {
            addOtherPlayer(this, players);
        });


        socket.on('TriggerFirstScene', (playerData) => {
            this.scene.switch('second');
        }); 

    }

    update() {
        super.update();
     }
}
class SecondScene extends BaseScene {
    constructor() {
        super('second');
    }

    create() {
        super.create();
        this.add.text(10, 10, 'LOCATION 2');

        socket.emit('createPlayer2');

        socket.on('playerCreated2', playerData => {
            addPlayer(this, playerData);
            this.trigger = this.add.rectangle(400, -12, 100, 100, 0xffffff).setDisplaySize(50, 50);

            this.physics.add.existing(this.trigger, true);
            this.physics.add.collider(this.player, this.trigger, (player, trigger) => {
                socket.emit('TriggerSecondScene', this.player);
            });

            socket.on('TriggerSecondScene', (playerData) => {
                this.scene.switch('first');
            });
        });

        socket.on('playerMoved2', (playerData) => {
            playerMoved(this, playerData);
        });

        socket.on('newPlayer2', (players) => {
            addOtherPlayer(this, players);
        });

    }

    update() {
        super.update();
        // other none duplicate code
        // ...
    }
}

const config = {
    type: Phaser.AUTO,
    width: 800,
    height: 700,
    physics: {
        default: 'arcade',
        arcade: {
            gravity: { y: 0 }
        }
    },
    scene: [FirstScene, SecondScene]
};

const game = new Phaser.Game(config);

function addPlayer(self, playerData) {
    self.player = self.physics.add.image(playerData.x, playerData.y, 'player').setDisplaySize(100, 100);
    self.player.setCollideWorldBounds(true);
    self.player.playerId = playerData.playerId;
}

function addOtherPlayer(self, players) {
    players.forEach((playerData) => {
        const existingPlayer = self.otherPlayers.getChildren().find((otherPlayer) => otherPlayer.playerId === playerData.id);

        if (!existingPlayer) {
            this.otherPlayer = self.physics.add.sprite(playerData.x_position, playerData.y_position, 'player').setDisplaySize(100, 100);
            this.otherPlayer.playerId = playerData.id;
            self.otherPlayers.add(otherPlayer);
        }
    });
}

function playerMoved(self, playerData) {
    self.otherPlayers.getChildren().forEach((otherPlayer) => {
        if (playerData.playerId === otherPlayer.playerId) {
            otherPlayer.setPosition(playerData.x, playerData.y);
        }
    });
}

Server code

io.on('connection', (socket) => {
    console.log('A user connected');

    if (socket.handshake.session && socket.handshake.session.user) {
        socket.on('createPlayer', async () => {
            try {
                const userId = socket.handshake.session.user.id;
                socket.emit('currentUserId', userId);

                const [rows] = await pool.query('SELECT  `id`, `nickname`, `login`, `password`, `age`, `x_position`, `y_position`, `clan`, `post`, `location`, `created_at` FROM `users` WHER WHERE id = ?', [userId]);

                if (rows.length > 0) {
                    let playerData = {
                        playerId: userId,
                        x: rows[0].x_position,
                        y: rows[0].y_position,
                        nickname: rows[0].nickname,
                    };

                    socket.emit('playerCreated', playerData);

                    const [rows2] = await pool.query('SELECT  `id`, `nickname`, `age`, `x_position`, `y_position`, `location`, `created_at` FROM users WHERE id != ? AND location = ?', [userId, 1]);

                    socket.emit('newPlayer', rows2)
                } else {
                }
            } catch (error) {
                console.error(error);
                
            }
        });

        socket.on('createPlayer2', async () => {
            try {
                const userId = socket.handshake.session.user.id;
                socket.emit('currentUserId', userId);

                const [rows] = await pool.query('SELECT  `id`, `nickname`, `age`, `x_position`, `y_position`, `location`, `created_at` FROM `users` WHERE id = ? AND location = ?', [userId, 2]);

                if (rows.length > 0) {
                    let playerData = {
                        playerId: userId,
                        x: rows[0].x_position,
                        y: rows[0].y_position,
                        nickname: rows[0].nickname,
                    };

                    socket.emit('playerCreated2', playerData);

                    const [rows2] = await pool.query('SELECT  `id`, `nickname`, `age`, `x_position`, `y_position`, `location`, `created_at` FROM users WHERE id != ? AND location = ? ', [userId, 2]);
                    socket.emit('newPlayer2', rows2)
                } else {
                   
                }
            } catch (error) {
                console.error(error);
            }
        });

        socket.on('playerMovement', async (data) => {
            try {
                const userId = socket.handshake.session.user.id;
                const { x, y } = data;

                await pool.query('UPDATE users SET x_position = ?, y_position = ? WHERE id = ?', [x, y, userId]);

                socket.broadcast.emit('playerMoved', { playerId: userId, x, y });
            } catch (error) {
                console.error(error);
            }
        });

app.get('/game.html', async (req, res) => {
    res.render('game', { user: req.session.user });
});

server.listen(3000, (err) => {
    if (err) {
        console.error('Error starting the server:', err);
    } else {
        console.log('Server is running on http://localhost:3000');
    }
});


Solution

  • Well honestly I don't really understand, what you want to achieve with your code, it is far to complicated.

    THAT said there are some issue here:

    1. If you switch from FirstScene to SecondScene, the socket events playerMoved might trigger (I'm not 100% sure if phaser will not put them to sleep or not), BUT they are triggering on the FirstScene, since playerMoved(this, playerData); is scoped in the FirstScene.
    2. In the SecondScene you are only querying the players in the location 2, and I can't see any code "moving" the player to that new location.

    A code structure that would be more readable / structured / easy to debug:

    (Simply have one Scene, that manages the other scenes)

    document.body.style = 'margin:0;';
    console.clear();
    
    class FirstScene extends Phaser.Scene {
        constructor(){
            super('FirstScene');
        }
        
        create(){
            this.add.text(10, 10, 'In FirstScene, Click to change').setOrigin(0);
            this.message = this.add.text(10, 30, 'Wating for data...').setOrigin(0);
            this.events.on('mgr-update', data => this.message.setText(`Data: ${data}`));
            this.input.on('pointerdown', () => this.events.emit('change-scene'));
        }
    }
    
    class SecondScene extends Phaser.Scene {
        constructor(){
            super('SecondScene');
        }
        
        create(){
            this.add.text(10, 10, 'In SecondScene, Click to change').setOrigin(0);
            this.message = this.add.text(10, 30, 'Wating for data...').setOrigin(0);
            this.events.on('mgr-update', data => this.message.setText(`Data: ${data}`));
            this.input.on('pointerdown', () => this.events.emit('change-scene'));
        }
    }
    
    class ManagerScene extends Phaser.Scene {
        constructor(){
            super('ManagerScene');
        }
        
        create(){
            let counter = 0;
            this.currentScene = 'FirstScene';
            this.scene.launch(this.currentScene);
    
            this.scene.get('FirstScene').events.on('change-scene', () => {
                this.currentScene = 'SecondScene';
                this.scene.launch('SecondScene');
                this.scene.sleep('FirstScene');
            });
            
            this.scene.get('SecondScene').events.on('change-scene', () => {
                this.currentScene = 'FirstScene';
                this.scene.launch('FirstScene');
                this.scene.sleep('SecondScene');
            });
            
            // for-the-demo sends every second the counter-value
            setInterval( () =>  this.scene.get(this.currentScene).events.emit('mgr-update', counter++), 500);
        }
    }
    
    var config = {
        type: Phaser.AUTO,
        width: 536,
        height: 183,
        scene: [ManagerScene, SecondScene, FirstScene, ],
    }; 
    
    new Phaser.Game(config);
    <script src="//cdn.jsdelivr.net/npm/phaser/dist/phaser.min.js"></script>

    Basically: in the manager Scene you would put the whole socket logic, and it would pass the data to the corresponding Scenes. This would keep the complexity and the count of eventlistener low.