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

使用THREE.Raycaster点击网格始终返回ID8的问题求助

问题原因:闭包导致的变量绑定陷阱

你遇到的问题是典型的JavaScript闭包特性带来的坑——在循环中给每个网格的callback赋值时,你引用的是同一个idNumberForTest变量,而非循环执行到当前步骤时的数值。当整个循环结束后,idNumberForTest已经累加到了8(4行×2列刚好8个网格),所以不管点击哪个网格,回调函数触发时都会读取这个最终值。

解决方案1:使用let创建块级作用域(推荐)

在循环内部用let声明一个局部变量,把当前的ID值存进去。这样每次循环都会生成一个独立的作用域,回调函数会绑定到当前循环的ID值,而非共享的全局变量:

for(i=1; i<=countInColumns; i++ ) {
    var geometry = new THREE.BoxGeometry(maxTileSizeWidth, maxTileSizeHeight, 1 );
    var tileX = new THREE.Mesh( geometry, materialsTiles );
    tileX.position.set(actualCoordinatesX,actualCoordinatesY,0);
    actualCoordinatesX = actualCoordinatesX + maxTileSizeWidth + distanceBetween;
    idNumberForTest=idNumberForTest + 1;
    // 用let保存当前循环的ID,避免闭包共享变量
    let currentTileId = idNumberForTest;
    tileX.callback = function(){callFromTitleWithId(currentTileId+'')};
    arrayTiles.push(tileX);
    sceneWorld.add( tileX );
}

解决方案2:使用立即执行函数(IIFE)兼容旧环境

如果你的运行环境不支持ES6的let,可以用立即执行函数创建独立作用域来捕获当前ID:

for(i=1; i<=countInColumns; i++ ) {
    var geometry = new THREE.BoxGeometry(maxTileSizeWidth, maxTileSizeHeight, 1 );
    var tileX = new THREE.Mesh( geometry, materialsTiles );
    tileX.position.set(actualCoordinatesX,actualCoordinatesY,0);
    actualCoordinatesX = actualCoordinatesX + maxTileSizeWidth + distanceBetween;
    idNumberForTest=idNumberForTest + 1;
    // 用IIFE把当前ID作为参数传入,形成独立作用域
    (function(tileId){
        tileX.callback = function(){callFromTitleWithId(tileId+'')};
    })(idNumberForTest);
    arrayTiles.push(tileX);
    sceneWorld.add( tileX );
}

解决方案3:直接将ID绑定到Mesh对象(更直观)

Three.js推荐用userData属性存储自定义数据,我们可以把ID直接存在网格对象上,回调时读取这个属性:

for(i=1; i<=countInColumns; i++ ) {
    var geometry = new THREE.BoxGeometry(maxTileSizeWidth, maxTileSizeHeight, 1 );
    var tileX = new THREE.Mesh( geometry, materialsTiles );
    tileX.position.set(actualCoordinatesX,actualCoordinatesY,0);
    actualCoordinatesX = actualCoordinatesX + maxTileSizeWidth + distanceBetween;
    idNumberForTest=idNumberForTest + 1;
    // 用Three.js官方推荐的userData存储自定义ID
    tileX.userData.tileId = idNumberForTest;
    tileX.callback = function(){callFromTitleWithId(this.userData.tileId+'')};
    arrayTiles.push(tileX);
    sceneWorld.add( tileX );
}

额外小提示

  • 你的idNumberForTest初始值是0,第一个网格的ID会是1,如果需要从0开始可以调整初始值
  • 使用userData存储自定义数据可以避免和Three.js内置属性冲突,是更规范的写法

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.06 10:02:36