Supabase实时日志与Presence事件不同步,玩家位置获取值为undefined
问题背景
我正在实现玩家位置广播功能,核心代码如下:
广播玩家位置
// 订阅操作用于客户端向服务器注册 this.channel.subscribe(async (status: any) => { if (status === 'SUBSCRIBED') { const status = await this.channel.track({ online_at: new Date().toISOString(), user_id: this.userId }) // 广播玩家位置 setInterval(() => { const player = this.player if (player) { const key = player.anims.currentAnim?.key; const velocityX = player.body.velocity.x; const velocityY = player.body.velocity.y; const positionX = player.x; const positionY = player.y; this.channel.send({ type: 'broadcast', event: 'player-pos', payload: { velocityX: velocityX, velocityY: velocityY, positionX: positionX, positionY: positionY, key: key }, }) } }, 100) } })
监听广播并保存位置
.on('broadcast', { event: 'player-pos' }, (payload: any) => { this.playerMap.forEach((obj => { if (this.userId !== obj.user_id) { // 创建玩家精灵并添加物理属性 this.otherPlayer?.setVelocity(payload.payload.velocityX, payload.payload.velocityY) if (payload.payload.key) { this.otherPlayer?.play(payload.payload.key, true) } if (obj.user_id && payload.payload.positionX && payload.payload.positionY) { this.playerMap.get(obj.user_id).positionX = payload.payload.positionX this.playerMap.get(obj.user_id).positionY = payload.payload.positionY } } })) })
Presence同步事件处理
.on('presence', { event: 'sync' }, async () => { const usersObj = await this.channel.presenceState() for (const userId in usersObj) { if (!this.playerMap.has(userId)) { this.playerMap.set(userId, usersObj[userId][0]) } } console.log('同步playerMap', this.playerMap, 'totalPlayers', this.totalPlayers, 'playerMap.size', this.playerMap.size) this.playerMap.forEach((obj => { // 需要判断this.totalPlayers < this.playerMap.size,因为presence至少会触发两次 // 第二次连接会创建重复的其他玩家 if (this.userId !== obj.user_id && this.totalPlayers < this.playerMap.size) { // 创建玩家精灵并添加物理属性 const otherPlayerObj = this.playerMap.get(obj.user_id) console.log('otherPlayerObj', otherPlayerObj, 'otherPlayerObj.positionX', otherPlayerObj.positionX) this.totalPlayers++ this.otherPlayer = new Player( this, otherPlayerObj.positionX, otherPlayerObj.positionY, 'up' ); } })) })
问题现象
打开两个客户端,先加载第一个客户端,再打开第二个客户端:
- 第二个客户端能正常看到第一个玩家的位置,控制台日志显示正常;
- 第一个客户端控制台显示
otherPlayerObj存在positionX属性,但获取otherPlayerObj.positionX时返回undefined。
请问这是什么原因导致的?我哪里操作有误?
原因分析及解决方案
核心问题
第一个客户端在Presence同步时拿到的用户数据没有positionX和positionY属性:
- 客户端调用
track方法时,只上传了online_at和user_id,没有包含初始位置信息; - 第二个客户端连接后触发Presence同步,第一个客户端拿到的是第二个客户端
track时上传的仅含基础字段的对象; - 控制台打印
otherPlayerObj时看到的positionX,是后续广播事件触发后才添加的属性,但你在Presence同步回调里立刻读取,此时属性还未被赋值,所以返回undefined。
具体修复方案
- 修改
track方法,上传初始位置
在首次向服务器注册时,就把玩家的初始位置一并传入,这样其他客户端同步时能直接拿到完整数据:
const status = await this.channel.track({ online_at: new Date().toISOString(), user_id: this.userId, positionX: this.player?.x || 0, // 新增初始X位置,加默认值兜底 positionY: this.player?.y || 0 // 新增初始Y位置,加默认值兜底 })
- 优化广播监听的位置更新逻辑
当前遍历整个playerMap的效率低且逻辑模糊,建议广播时带上user_id,直接定位目标玩家更新:
// 广播时新增user_id字段 this.channel.send({ type: 'broadcast', event: 'player-pos', payload: { user_id: this.userId, // 新增 velocityX: velocityX, velocityY: velocityY, positionX: positionX, positionY: positionY, key: key }, }) // 监听广播时精准更新 .on('broadcast', { event: 'player-pos' }, (payload: any) => { const { user_id, velocityX, velocityY, positionX, positionY, key } = payload.payload; if (user_id !== this.userId) { const targetPlayer = this.playerMap.get(user_id); if (targetPlayer) { targetPlayer.positionX = positionX; targetPlayer.positionY = positionY; } this.otherPlayer?.setVelocity(velocityX, velocityY); key && this.otherPlayer?.play(key, true); } })
- 给位置读取加兜底默认值
即使存在位置未同步的极端情况,也能避免创建玩家时出现异常:
this.otherPlayer = new Player( this, otherPlayerObj.positionX || 0, otherPlayerObj.positionY || 0, 'up' );
内容的提问来源于stack exchange,提问作者dev_el
相关产品推荐
相关产品推荐

