如何用dat.gui控制Three.js中TextGeometry的尺寸与颜色?
问题:dat.gui控制Three.js TextGeometry尺寸与颜色时场景崩溃
问题背景
想通过dat.gui可视化控制TextGeometry的尺寸和颜色,但无论把GUI代码放在textCreation函数内部还是外部,都会导致场景崩溃。已知修改参数时需要销毁旧网格并重建新网格,且textGeometry、textMesh、textMaterial、group均为全局变量,initText会随按键事件更新。
现有代码
文本创建函数
function textCreation() { const fontLoader = new FontLoader(); fontLoader.load( "node_modules/three/examples/fonts/droid/droid_sans_bold.typeface.json", (droidFont) => { textGeometry = new TextGeometry(initText, { //initText变量随每次按键更新 size: 5, height: 2, font: droidFont, parameters: { size: 5, height: 2, }, }); textMaterial = new THREE.MeshBasicMaterial({ color: 0xff0000 }); textMesh = new THREE.Mesh(textGeometry, textMaterial); group = new THREE.Group(); group.add(textMesh); scene.add(group); } ); }
尝试的dat.gui代码
let textFolder = gui.addFolder("Text Controls"); textFolder.add(textGeometry, "size", 10, 20).onChange(function () { textGeometry.setSize(textGeometry.size); // 可能需要销毁并移除场景中的旧网格,再用新参数创建新网格 }); textFolder.addColor(textGeometry, "color").onChange(function () { textGeometry.setColor(textGeometry.color); });
问题原因
- 异步加载时机问题:
FontLoader.load是异步操作,初始化GUI时textGeometry还未被创建,直接访问会触发undefined报错。 - 错误的API调用:
TextGeometry没有setSize、setColor方法,调用这些不存在的方法会直接导致崩溃。 - 未正确清理旧资源:修改参数时没有销毁旧的几何体、材质和网格,会导致内存泄漏和场景中存在多个文本网格。
- 控制器绑定对象错误:直接绑定未初始化的
textGeometry,而非一个稳定的配置对象,会导致GUI无法正常工作。
解决方案
步骤1:创建全局配置对象
定义一个存储文本参数的全局对象,GUI直接绑定这个对象,避免依赖未初始化的Three.js对象:
// 全局配置对象,存储文本的可配置参数 const textConfig = { size: 5, height: 2, color: 0xff0000, font: null // 存储加载完成的字体 };
步骤2:重构文本创建与更新逻辑
拆分出updateText函数,负责销毁旧资源、用新参数重建文本;textCreation只负责加载字体并初始化第一次文本:
// 加载字体并初始化文本 function textCreation() { const fontLoader = new FontLoader(); fontLoader.load( "node_modules/three/examples/fonts/droid/droid_sans_bold.typeface.json", (droidFont) => { textConfig.font = droidFont; // 字体加载完成后初始化文本和GUI updateText(); initGUI(); } ); } // 更新文本:销毁旧资源,用新参数重建 function updateText() { // 清理旧资源 if (textMesh) { // 从组中移除网格 group.remove(textMesh); // 销毁几何体和材质,释放内存 textGeometry.dispose(); textMaterial.dispose(); // 销毁网格 textMesh = null; } // 创建新的文本几何体、材质和网格 textGeometry = new TextGeometry(initText, { size: textConfig.size, height: textConfig.height, font: textConfig.font }); textMaterial = new THREE.MeshBasicMaterial({ color: textConfig.color }); textMesh = new THREE.Mesh(textGeometry, textMaterial); // 如果组不存在则创建,否则直接添加新网格 if (!group) { group = new THREE.Group(); scene.add(group); } group.add(textMesh); }
步骤3:正确初始化dat.gui
在字体加载完成后初始化GUI,绑定textConfig对象,并在参数变化时调用updateText:
function initGUI() { const gui = new dat.GUI(); const textFolder = gui.addFolder("Text Controls"); // 绑定尺寸参数,变化时更新文本 textFolder.add(textConfig, "size", 1, 20).step(0.5).onChange(updateText); // 绑定厚度参数 textFolder.add(textConfig, "height", 0.1, 10).step(0.1).onChange(updateText); // 绑定颜色参数 textFolder.addColor(textConfig, "color").onChange(updateText); textFolder.open(); }
步骤4:按键事件联动
如果需要在按键更新initText时也更新文本,直接在按键事件中调用updateText即可:
// 示例按键事件 document.addEventListener('keydown', (e) => { // 根据按键更新initText,比如追加字符 initText += e.key; // 调用updateText重建文本 updateText(); });
关键注意事项
- 资源销毁:每次更新必须调用
dispose()销毁旧的几何体和材质,避免内存泄漏。 - 异步顺序:必须等待字体加载完成后再初始化GUI和创建文本,否则会因
textConfig.font为null报错。 - 配置对象:始终用独立的配置对象存储参数,而非直接绑定Three.js的实例属性,避免因实例未初始化导致的错误。
内容的提问来源于stack exchange,提问作者rju7n
相关产品推荐
相关产品推荐

