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

Three.js中sin()/cos()动画导致网格重叠问题求解

Three.js立方体运动重叠问题解决建议

我是JavaScript和Three.js新手,很高兴认识大家!

这段代码用于创建4个立方体和1个箭头,初始效果为四个立方体均匀分布在圆周路径上,箭头位于中心区域。

// Scene
const scene = new THREE.Scene();

// Groups
const cubes = new THREE.Group();

// Ambient Light
const light = new THREE.AmbientLight(0xffffff, 1);
scene.add(light)

// Reference Circle or Path
const circle = new THREE.CircleGeometry(5, 100)
const stroke = new THREE.LineBasicMaterial({color : 'white'})
const ref = new THREE.Line(circle, stroke)
scene.add(ref)

// Mesh 
const geometry = new THREE.BoxGeometry(1, 1, 1)
const material = new THREE.MeshBasicMaterial({ color: 'red' })
const mesh = new THREE.Mesh(geometry, material)

// Mesh 2  
const geometry2 = new THREE.BoxGeometry(1, 1, 1)
const material2 = new THREE.MeshBasicMaterial({ color: 'blue' })
const mesh2 = new THREE.Mesh(geometry2, material2)

// Mesh 3 
const geometry3 = new THREE.BoxGeometry(1, 1, 1)
const material3 = new THREE.MeshBasicMaterial({ color: 'green' })
const mesh3 = new THREE.Mesh(geometry3, material3)

// Mesh 4 
const geometry4 = new THREE.BoxGeometry(1, 1, 1)
const material4 = new THREE.MeshBasicMaterial({ color: 'purple' })
const mesh4 = new THREE.Mesh(geometry4, material4)

// Mesh 5 Arrow
const geometry5 = new THREE.ConeGeometry(1, 4, 3)
const material5 = new THREE.MeshBasicMaterial({ color: 'yellow' })
const arrow = new THREE.Mesh(geometry5, material5)

scene.add(mesh, mesh2, mesh3, mesh4, arrow)

const axesHelper = new THREE.AxesHelper();
scene.add(axesHelper)

// Camera
const aspect = {
    width: window.innerWidth,
    height: window.innerHeight
}

const camera = new THREE.PerspectiveCamera(75, aspect.width / aspect.height)
camera.position.x = -0.15;
camera.position.z = 10;

scene.add(camera)

// Renderer 
const canvas = document.querySelector('.draw')
const renderer = new THREE.WebGLRenderer({ canvas });
renderer.setSize(aspect.width, aspect.height)
renderer.render(scene, camera)

const clock = new THREE.Clock();

const animate = () => {
    const time = clock.getElapsedTime()
    const a = time;
    let px;
    let pz;
    let r = 5;

    px = r * Math.cos(a);
    pz = r * Math.sin(a);

    mesh.position.set(px, pz, 0);
    mesh.rotation.x = time * Math.PI * 0.1;
    mesh.rotation.y = time * Math.PI * 0.3;

    mesh2.position.set(px, pz, 0);
    mesh2.rotation.x = time * Math.PI * 0.1;
    mesh2.rotation.y = time * Math.PI * 0.3;
    
    mesh3.position.set(px2, pz, 0); // 此处px2未定义,属于代码bug
    mesh3.rotation.x = time * Math.PI * 0.1;
    mesh3.rotation.y = time * Math.PI * 0.3;

    mesh4.position.set(px, pz, 0);
    mesh4.rotation.x = time * Math.PI * 0.1;
    mesh4.rotation.y = time * Math.PI * 0.3;

    arrow.rotation.y = time * Math.PI * 0.7;

    renderer.render(scene, camera)
    window.requestAnimationFrame(animate)
}
animate();

我遇到的问题是,当animate()循环启动时,所有几何体都重叠在一起,四个立方体和箭头挤在同一位置。请问有什么解决重叠的建议吗?比如添加某种延迟,但我不知道如何实现。我曾考虑为每个立方体创建4个循环,但程序运行效果不符合预期。请问有什么方法可以为每个网格添加延迟以避免重叠吗?


问题分析

当前代码中所有立方体使用相同的时间变量time计算位置,导致它们的坐标完全一致,自然会重叠。此外代码中mesh3用到的px2未定义,属于语法错误,需要先修正。

解决方法

方法1:角度偏移法(推荐)

让每个立方体在圆周上的运动角度相差固定值,这样它们会始终均匀分布在圆周上,不会重叠。四个立方体刚好可以各差90°(即Math.PI/2弧度):

修改animate函数如下:

const animate = () => {
    const time = clock.getElapsedTime()
    const r = 5;
    // 四个立方体的角度偏移量,依次相差90°
    const angleOffsets = [0, Math.PI/2, Math.PI, 3*Math.PI/2];
    // 将立方体存入数组,循环处理减少重复代码
    const cubes = [mesh, mesh2, mesh3, mesh4];

    cubes.forEach((cube, index) => {
        // 每个立方体的运动角度 = 当前时间 + 偏移量
        const currentAngle = time + angleOffsets[index];
        const px = r * Math.cos(currentAngle);
        const pz = r * Math.sin(currentAngle);
        cube.position.set(px, pz, 0);
        cube.rotation.x = time * Math.PI * 0.1;
        cube.rotation.y = time * Math.PI * 0.3;
    });

    arrow.rotation.y = time * Math.PI * 0.7;

    renderer.render(scene, camera)
    window.requestAnimationFrame(animate)
}

方法2:延迟启动法

如果需要让立方体依次启动,可以给每个立方体的运动时间减去不同的延迟值,避免初始时重叠:

const animate = () => {
    const time = clock.getElapsedTime()
    const r = 5;
    // 每个立方体的延迟时间,单位秒
    const delays = [0, 0.5, 1, 1.5];
    const cubes = [mesh, mesh2, mesh3, mesh4];

    cubes.forEach((cube, index) => {
        // 确保时间不为负,避免初始位置异常
        const delayedTime = Math.max(0, time - delays[index]);
        const currentAngle = delayedTime;
        const px = r * Math.cos(currentAngle);
        const pz = r * Math.sin(currentAngle);
        cube.position.set(px, pz, 0);
        cube.rotation.x = delayedTime * Math.PI * 0.1;
        cube.rotation.y = delayedTime * Math.PI * 0.3;
    });

    arrow.rotation.y = time * Math.PI * 0.7;

    renderer.render(scene, camera)
    window.requestAnimationFrame(animate)
}

额外优化建议

  • 复用几何体和材质:四个立方体的几何体和材质参数完全一致,可以只创建一份,然后实例化多个Mesh,减少内存占用:
    const geometry = new THREE.BoxGeometry(1, 1, 1);
    const cubes = [
        new THREE.Mesh(geometry, new THREE.MeshBasicMaterial({ color: 'red' })),
        new THREE.Mesh(geometry, new THREE.MeshBasicMaterial({ color: 'blue' })),
        new THREE.Mesh(geometry, new THREE.MeshBasicMaterial({ color: 'green' })),
        new THREE.Mesh(geometry, new THREE.MeshBasicMaterial({ color: 'purple' }))
    ];
    cubes.forEach(cube => scene.add(cube));
    

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.19 05:10:54