【问题标题】:Java Script overwriting array indices after splicing拼接后Javascript覆盖数组索引
【发布时间】:2018-11-16 17:51:27
【问题描述】:

至少我认为,我遇到了一个问题,我似乎在拼接后覆盖了数组索引。它是一个使用 Phaser 2 构建的小游戏。它本质上只是一个小的多人跳跃游戏,以获得一些客户端/服务器架构的经验。正在使用 socket.io 和 express。我的问题似乎出在服务器上,当客户端断开连接时,当它从玩家列表中删除时,仍在游戏中的其他玩家似乎覆盖了断开连接的玩家的索引。为了调试这个,我主要使用控制台日志,使用 for 循环遍历列表并打印出玩家的套接字 ID。因此,例如,如果我的套接字 ID 为 1 的玩家 1 加入,然后具有套接字 ID 2 的玩家 2 加入,然后玩家 2 离开,for 循环将打印出 1, 1。如果具有套接字 ID 3 的新玩家 3 在玩家之后加入2 离开了,打印出玩家的 ID 会打印出 1、1、3。起初我认为问题是在 onNewPlayer(data) 函数中,我有一个别名问题,因为我在两个中使用了 var currentInfo不同的地方,所以我将第二个对象更改为 var info。这似乎是某种混叠问题,还是我应该在其他地方搜索这个问题?如果需要,我可以提供额外的代码,到目前为止,我们所有用于玩家创建和移动的回调都运行良好。谢谢。

下面是相关的服务器端代码

var players[];
//When a new player is made, save it
function onNewPlayer(data) {
  var newPlayer = new Player(data.x, data.y, this.id);

  var currentInfo = {
    x: newPlayer.x,
    y: newPlayer.y,
    id: newPlayer.id,
  };

  for(i = 0; i < players.length; i++) {
    //broadcast the new player out to all the other players in the list  
    this.broadcast.emit("newEnemy", currentInfo);
  }

  //check for if there are already players,
  //if so, send the player's who are already in the game to the new player
  if(players.length > 0) {
    for(i = 0; i < players.length; i++) {
        var info = {
            x: players[i].x,
            y: players[i].y,
            id: players[i].id,
        };
        this.emit("newEnemy", info);
    }
  }

  players.push(newPlayer);
  for(i = 0; i < players.length; i++) {
    console.log(players[i].id);
  }
}

function onDisconnect(){
    console.log("User " + this.id + " disconnected");
    //find the user in the list of players and remove them, then tell the client
    for(i = 0; i < players.length; i++) {
        if(players[i].id === this.id) {
            console.log("removing this player " + this.id);
            //TODO trying a different broadcast
            this.broadcast.emit("playerDisconnect", this.id);
            console.log(players[i].id);
            players.splice(i, 1);
        }
    }
}

下面是相关的客户端代码

//We've lost connection with the server!
function onSocketDisconnect() {
    console.log("Lost connection with server!");
};

//When the server notifies the client an enemy has disconnected,
//search for it in the enemies list and stop rendering it
function onEnemyDisconnect(data) {
    //TODO
    for(i = 0; i < enemies.length; i++) {
        if(enemies[i].id == data) {
            //TODO
            console.log("destroying");
            enemies[i].destroy();
            enemies.splice(i, 1);
        }
    }
}

【问题讨论】:

    标签: javascript express socket.io alias phaser-framework


    【解决方案1】:

    您正在使用for 循环正向迭代数组并使用.splice() 从数组中删除元素。这将无法正常工作,因为当您调用 .splice() 从数组中删除一个项目时,它会将后面的元素从数组中向下复制一个位置。但是,您的 for 循环索引指向数组中的下一个元素。最终结果是您跳过了数组中的迭代项。

    有多种可能的解决方案。

    1. 您可以向后而不是向前迭代数组。当以相反的顺序迭代时,你还没有迭代的元素不受你的.splice()的影响,它工作得很好。

    2. 您可以在 for 循环中停止修改数组。也许您收集了一组要删除的索引,然后将它们从后向前删除。

    3. 您可以使用.filter() 创建一个新数组,它是原始数组的子集,完成.filter() 操作后,只需将新数组分配给您的变量并从那时起使用它。

    4. 您可以按原样保持迭代,但在调用.splice() 后将for 循环索引减一。

    这是一个反向数组迭代的例子:

    // When the server notifies the client an enemy has disconnected,
    // search for it in the enemies list and stop rendering it
    function onEnemyDisconnect(data) {
        // use reverse iteration to avoid skipping elements when calling .splice()
        for (i = enemies.length - 1; i >= 0; i--)
            if(enemies[i].id == data) {
                console.log("destroying");
                enemies[i].destroy();
                enemies.splice(i, 1);
            }
        }
    }
    

    这是一个.filter() 的示例,假设您可以分配给enemies,并且新数组将永久取代它:

    // When the server notifies the client an enemy has disconnected,
    // search for it in the enemies list and stop rendering it
    function onEnemyDisconnect(data) {
        enemies = enemies.filter(item => {
            if (item.id === data) {
                console.log("destroying");
                item.destroy();
                return false;    // don't keep this one
            }
            return true;
        });
    }
    

    【讨论】:

    • 非常感谢!我永远不会发现。对 JS 来说仍然很新,来自 C/C++。在 for 循环结束后,我最终只是复制了索引并删除了所需的对象。
    • @user406955 - 如果只有一个匹配项,您可以在执行.splice() 之后循环breakfor,应该没有问题。这些解决方案适用于您可以拥有多个匹配项的情况。
    • 我应该一次只需要删除一个玩家,我认为使用回调它应该只为断开连接的单个套接字调用它吗?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-01-27
    • 2015-05-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多