Three.js中满足条件语句后如何继续动画并实现墙体无限上移循环
嘿,我帮你拆解下问题的根源,再给你两个能完美实现无限循环滚动的方案~
为什么你的动画会“停止”?
你代码里的位置更新逻辑是硬伤:你每次都用全局递增的time变量直接计算网格的y位置,而不是在上一帧的位置基础上逐步累加偏移量。
看这段关键代码:
Wall.position.y = -15 + time / 200;
time是从页面加载开始就一直涨的毫秒数,当你触发if条件把Wall.position.y设为0后,下一帧的time已经比之前大了几十毫秒,算出来的-15 + time/200会直接跳到一个远大于5的数值,瞬间又触发重置逻辑。这个过程快到肉眼根本看不到循环,看起来就像动画卡在高处不动了。
另外还有个小细节:Three.js里旋转用的是弧度不是角度,你写的Wall.rotation.x = 180其实是180弧度,相当于让网格转了28圈多,这也会导致初始位置不符合你的预期,应该用Math.PI(等于180度)。
正确的实现方式
下面给你两种实用的方案,按需选择:
方案1:复用单个网格,平滑循环
这种方式最简单,只要把位置更新改成基于当前位置的增量累加,移出视野后重置到初始位置即可:
import * as THREE from 'three'; const renderer = new THREE.WebGLRenderer(); renderer.setSize(window.innerWidth, window.innerHeight); document.body.appendChild(renderer.domElement); const scene = new THREE.Scene(); const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000); camera.position.set(0, 0, 10); // 创建网格,用Math.PI代替180(弧度转角度) const Wall = new THREE.GridHelper(10, 5); Wall.rotation.x = Math.PI; scene.add(Wall); // 配置参数,方便调整 const moveSpeed = 0.1; // 每帧移动的距离 const startY = -10; // 初始位置(视野下方) const resetY = 10; // 移出视野的阈值 // 初始化位置 Wall.position.y = startY; function animate() { // 核心:在上一帧位置基础上累加速度,实现连续移动 Wall.position.y += moveSpeed; // 完全移出视野后,重置到初始位置 if (Wall.position.y > resetY) { Wall.position.y = startY; } renderer.render(scene, camera); } renderer.setAnimationLoop(animate);
方案2:多网格循环,更自然的无限滚动
如果不想看到单个网格突然“跳”回底部的突兀感,可以创建多个网格,当某个网格移出视野时,把它偷偷移到队列最底部,视觉上就是无限滚动了:
import * as THREE from 'three'; const renderer = new THREE.WebGLRenderer(); renderer.setSize(window.innerWidth, window.innerHeight); document.body.appendChild(renderer.domElement); const scene = new THREE.Scene(); const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000); camera.position.set(0, 0, 10); // 配置参数 const gridSize = 10; const moveSpeed = 0.1; const gridCount = 3; // 3个网格足够覆盖视野,避免空白 const grids = []; // 初始化多个网格,垂直排列 for (let i = 0; i < gridCount; i++) { const grid = new THREE.GridHelper(gridSize, 5); grid.rotation.x = Math.PI; // 每个网格间隔gridSize,从视野下方开始排列 grid.position.y = -gridSize * 1.5 + i * gridSize; scene.add(grid); grids.push(grid); } function animate() { grids.forEach(grid => { grid.position.y += moveSpeed; // 当网格完全移出顶部视野,移到最底部 if (grid.position.y > gridSize * 1.5) { // 找到当前最靠下的网格 const lowestGrid = grids.reduce((prev, curr) => prev.position.y < curr.position.y ? prev : curr); // 把当前网格移到它下方一个网格的距离 grid.position.y = lowestGrid.position.y - gridSize; } }); renderer.render(scene, camera); } renderer.setAnimationLoop(animate);
关于你尝试过的“创建新网格”方案
如果之前用创建新网格、删除旧网格的方式失败,大概率是没正确清理资源:删除旧网格时,除了调用scene.remove(oldGrid),还要释放它的几何体和材质内存(oldGrid.geometry.dispose()、oldGrid.material.dispose()),不然会导致内存泄漏。不过复用现有网格的方式性能更好,优先推荐上面的两种方案。
内容的提问来源于stack exchange,提问作者Seelum

