Socket.IO中如何清除玩家移动的setInterval避免重复执行?
问题解决:为特定玩家清除移动定时器,避免速度翻倍
问题根源
你的代码核心问题是:每次收到moveRequest时都会创建新的setInterval,但旧的定时器只有在玩家到达原目标时才会被清除。如果玩家中途点击新网格,旧定时器仍在运行,多个定时器同时修改玩家坐标,导致移动速度翻倍。之前绑定定时器到玩家对象时出现栈溢出,大概率是因为没有先清理旧定时器,导致重复创建的定时器引发循环引用或递归调用。
解决方案
给每个玩家对象添加moveInterval属性存储当前定时器ID,每次处理新的移动请求前先清除旧定时器,确保同一时间只有一个定时器在控制玩家移动。
修改后的代码
// 初始化玩家对象时添加moveInterval属性 players[socket.id] = { x: 0, z: 0, xTo: 0, zTo: 0, isMoving: false, playerId: socket.id, name: name, room: 'lobby', moveInterval: null // 新增:存储当前移动定时器ID }; // 处理客户端移动请求 socket.on('moveRequest', function(pos) { const player = players[socket.id]; // 先清除玩家当前的移动定时器(如果存在) if (player.moveInterval) { clearInterval(player.moveInterval); player.moveInterval = null; } // 设置目标坐标 player.xTo = pos[0]; player.zTo = pos[2]; // 目标与当前位置相同时直接返回 if (player.x === player.xTo) return; // 计算移动方向 const xDiff = player.xTo - player.x; let xDirection = xDiff > 0 ? 'right' : 'left'; // 创建新的移动定时器,并赋值给玩家的moveInterval属性 player.moveInterval = setInterval(function() { let shouldStop = false; if (xDirection === 'right' && player.x < player.xTo) { player.x += 0.03; } else if (xDirection === 'left' && player.x > player.xTo) { player.x -= 0.03; } else { // 到达目标,停止移动 shouldStop = true; xDirection = 'none'; player.x = player.xTo; // 修正坐标到精确目标位置 } // 同步位置到房间内所有玩家 io.to('game').emit('playerMove', player); // 停止定时器并清理 if (shouldStop) { clearInterval(player.moveInterval); player.moveInterval = null; } }, 1000 / 60); });
关键改动说明
- 新增
moveInterval属性:每个玩家对象保存自己的定时器ID,方便后续精准清除 - 前置清除逻辑:每次处理新移动请求时,先检查并清除旧定时器,从根源避免多个定时器同时运行
- 精确修正坐标:到达目标时直接将玩家坐标设置为目标值,避免因步长问题导致的坐标偏差
- 定时器清理:到达目标后不仅清除定时器,还将
moveInterval置为null,避免残留无效引用
内容的提问来源于stack exchange,提问作者thedoomer1000
相关产品推荐
相关产品推荐

