You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Phaser 3场景切换后客户端无法看到其他玩家移动的问题

问题根源与修复方案

核心问题

  1. 服务端未按场景广播移动事件:当前服务端所有玩家移动都广播playerMoved,但第二场景客户端监听的是playerMoved2,完全收不到移动消息。
  2. 场景切换未更新用户位置信息:玩家切换场景时,服务端没有更新数据库中用户的location字段,导致后续无法正确识别玩家所在场景。
  3. 客户端函数变量错误:addOtherPlayer函数中使用全局this定义otherPlayer,导致添加其他玩家时出现作用域错误。
  4. 场景切换未清理旧玩家数据:切换场景后,旧场景的其他玩家没有被移除,可能导致混淆,但这不是移动不显示的直接原因。

具体修复步骤

1. 服务端修复:按场景分组广播移动事件

修改点:

  • 新增场景切换的事件处理,更新用户的location字段
  • 修改playerMovement事件,根据用户当前所在场景,广播对应场景的移动事件
io.on('connection', (socket) => {
    console.log('A user connected');

    if (socket.handshake.session && socket.handshake.session.user) {
        const userId = socket.handshake.session.user.id;

        // 处理从场景1切换到场景2
        socket.on('switchToScene2', async () => {
            try {
                await pool.query('UPDATE users SET location = 2 WHERE id = ?', [userId]);
                socket.emit('TriggerFirstScene');
                // 可选:通知场景2其他玩家有新用户加入
                const [userRow] = await pool.query('SELECT id, x_position, y_position FROM users WHERE id = ?', [userId]);
                socket.broadcast.emit('newPlayer2', [userRow[0]]);
            } catch (error) {
                console.error(error);
            }
        });

        // 处理从场景2切换到场景1
        socket.on('switchToScene1', async () => {
            try {
                await pool.query('UPDATE users SET location = 1 WHERE id = ?', [userId]);
                socket.emit('TriggerSecondScene');
                const [userRow] = await pool.query('SELECT id, x_position, y_position FROM users WHERE id = ?', [userId]);
                socket.broadcast.emit('newPlayer', [userRow[0]]);
            } catch (error) {
                console.error(error);
            }
        });

        // 修改playerMovement事件,按场景广播
        socket.on('playerMovement', async (data) => {
            try {
                const { x, y } = data;
                await pool.query('UPDATE users SET x_position = ?, y_position = ? WHERE id = ?', [x, y, userId]);
                
                // 查询用户当前场景
                const [rows] = await pool.query('SELECT location FROM users WHERE id = ?', [userId]);
                const location = rows[0].location;
                
                // 按场景广播对应移动事件
                if (location === 1) {
                    socket.broadcast.emit('playerMoved', { playerId: userId, x, y });
                } else if (location === 2) {
                    socket.broadcast.emit('playerMoved2', { playerId: userId, x, y });
                }
            } catch (error) {
                console.error(error);
            }
        });

        // 保留原有的createPlayer、createPlayer2等代码...
    }
});

2. 客户端修复:修正场景切换逻辑与变量错误

修改点:

  • 修复addOtherPlayer函数的变量作用域问题
  • 修正场景切换时的socket事件发送,调用服务端新增的切换事件
// 修复addOtherPlayer函数的变量作用域错误
function addOtherPlayer(self, players) {
    players.forEach((playerData) => {
        const existingPlayer = self.otherPlayers.getChildren().find((otherPlayer) => otherPlayer.playerId === playerData.id);

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

// 修改FirstScene的场景切换触发逻辑
class FirstScene extends BaseScene {
    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, () => {
                socket.emit('switchToScene2');
            }); 
        });

        // 保留原有的playerMoved、newPlayer、TriggerFirstScene监听...
    }
}

// 修改SecondScene的场景切换触发逻辑
class SecondScene extends BaseScene {
    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, () => {
                socket.emit('switchToScene1');
            });
        });

        // 保留原有的playerMoved2、newPlayer2监听...
    }
}

3. 可选优化:场景切换时清理旧玩家

在BaseScene的create方法中添加清理逻辑,避免跨场景玩家残留:

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();
        // 清理之前场景的其他玩家
        if (this.otherPlayers && this.otherPlayers.clear) {
            this.otherPlayers.clear(true, true);
        }
        this.otherPlayers = this.physics.add.group();
    }

    // 保留原有的update方法...
}

修复后逻辑说明

  1. 玩家切换场景时,客户端通知服务端更新数据库中的location字段
  2. 服务端处理玩家移动时,先查询玩家当前场景,再广播对应场景的移动事件
  3. 客户端对应场景监听各自的移动事件,确保只接收当前场景玩家的移动消息
  4. 修复变量作用域错误,保证其他玩家能正确添加到场景中

内容的提问来源于stack exchange,提问作者Iya

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.30 20:15:53