Three.js调用同一函数创建2个球体后如何区分并控制指定球体移动
问题根源
- 你代码里的
center、mesh_felii变量没有加let/const声明,默认成为全局变量,第二次调用createSphere函数时会直接覆盖第一次的center值,所以你目前只能操作到最后一次创建的球体。 - 你传入的
sphere_name参数没有被实际使用,没有给不同球体设置唯一标识用来区分。 - 你当前生成的球体分片网格都是直接添加到场景中,没有挂载到
center下,就算你能拿到之前的center实例,移动center时分片也不会跟着移动,这是隐藏的逻辑问题。
解决方法
第一步:修改函数逻辑,存储球体实例
先在全局作用域声明一个用来存储所有球体实例的映射表,同时修改createSphere函数,给变量加声明、把分片挂载到center下、用sphere_name作为唯一标识存储实例:
// 全局作用域声明,用来存储所有球体实例 const spheres = {} // 注意:你原代码里的全局变量materialeDeAratat如果要多次调用函数,建议按需重置,这里先保留原逻辑 function createSphere( sphere_name, radius, wSegments, hSegments, surface_map, xPos, yPos, zPos ) { let geometry1 = new THREE.SphereGeometry(radius, wSegments, hSegments); let material1 = new THREE.MeshLambertMaterial({ map: surface_map }); // 加let声明,避免成为全局变量 let center = new THREE.Mesh(geometry1, material1); center.position.set(xPos, yPos, zPos); // 给实例设置name属性,方便后续查找 center.name = sphere_name; let materialsToMap = 1; for (let i = 10; i < 630; i += 62.8) { for (let k = 70; k < 215; k += 45) { let j = i / 100; let l = k / 100; let geometry = new THREE.SphereGeometry(radius, 16, 16, j, 0.62, l, 0.44); let material = new THREE.MeshLambertMaterial({ map: materiale[materialsToMap], transparent: true, opacity: 0.8, }); // 加let声明,避免全局污染 let mesh_felii = new THREE.Mesh(geometry, material); // 子元素相对于父级center定位,直接设为0即可 mesh_felii.position.set(0,0,0); mesh_felii.userData = linkuri[materialeDeAratat]; mesh_felii.layers.set(1); // 挂载到center下,移动center时所有分片会同步移动 center.add(mesh_felii); materialeDeAratat++; if (materialeDeAratat >= materiale.length) materialeDeAratat = 1; } } // 把当前球体实例存入全局映射表 spheres[sphere_name] = center; scene.add(center); return center; }
第二步:调用函数时传入唯一名称
创建球体时给每个球体传入唯一的sphere_name:
// 示例:创建两个球体,分别命名为sphere1和sphere2 createSphere('sphere1', 5, 32, 32, surfaceMap1, -10, 0, 0); createSphere('sphere2', 5, 32, 32, surfaceMap2, 10, 0, 0);
第三步:通过名称指定要移动的球体
滑块触发事件时,直接通过名称从映射表中取对应实例修改位置即可:
// 假设控制第一个球体的滑块id为sphere1Slider document.querySelector('#sphere1Slider').addEventListener('input', e => { // 直接通过名称获取对应球体,修改x坐标 spheres.sphere1.position.x = Number(e.target.value); }) // 控制第二个球体就改为spheres.sphere2.position.x
替代方案
如果你不想用全局映射表,也可以直接用Three.js内置的getObjectByName方法从场景中查找对应实例:
const targetSphere = scene.getObjectByName('sphere1'); if (targetSphere) { targetSphere.position.x = 滑块值; }
内容的提问来源于stack exchange,提问作者Dragos
相关产品推荐
相关产品推荐

