场景系统
场景(Scene)是 Three.js 的核心概念之一,它是所有 3D 对象的容器。理解场景系统对于构建复杂的 3D 应用至关重要。
系统架构概述
code
┌─────────────────────────────────────────────────────────────────┐
│ 场景系统架构 │
├─────────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────┐ ┌──────────┐ ┌───────────────┐ │
│ │ Scene │────▶│ Object3D │────▶│ 子类对象 │ │
│ └─────────┘ └──────────┘ ├───────────────┤ │
│ │ │ Mesh │ │
│ │ 包含 │ Group │ │
│ ▼ │ Light │ │
│ ┌─────────┐ │ Camera │ │
│ │ 背景属性 │ │ Line │ │
│ │ 雾效果 │ │ Points │ │
│ │ 环境贴图 │ │ Sprite │ │
│ └─────────┘ └───────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────┘Scene 基础
创建场景
javascript
// 创建空场景
const scene = new THREE.Scene()
// 场景创建后可立即添加对象
scene.add(new THREE.AmbientLight(0xffffff, 0.5))Scene 属性配置
| 属性 | 类型 | 说明 | 默认值 |
|---|---|---|---|
background | Color/Texture/CubeTexture | 场景背景 | null |
environment | Texture | 环境贴图,用于反射 | null |
fog | Fog/FogExp2 | 雾效果 | null |
children | Array | 子对象数组 | [] |
autoUpdate | Boolean | 自动更新矩阵 | true |
backgroundBlurriness | Number | 背景模糊程度 | 0 |
backgroundIntensity | Number | 背景强度 | 1 |
背景设置
javascript
// 使用纯色背景
scene.background = new THREE.Color(0x333333)
// 使用渐变背景(通过着色器或 CSS)
scene.background = new THREE.Color(0x87ceeb) // 天蓝色
// 使用天空盒(立方体贴图)
scene.background = new THREE.CubeTextureLoader().load([
'px.jpg', 'nx.jpg', // 正X、负X
'py.jpg', 'ny.jpg', // 正Y、负Y
'pz.jpg', 'nz.jpg' // 正Z、负Z
])
// 使用等距矩形投影贴图(全景图)
scene.background = new THREE.TextureLoader().load('equirectangular.jpg')
// 设置背景模糊(需要 HDR 环境)
scene.backgroundBlurriness = 0.5
scene.backgroundIntensity = 1.0环境贴图
javascript
// 设置环境贴图(用于 PBR 材质反射)
const envMap = new THREE.CubeTextureLoader().load([
'px.jpg', 'nx.jpg',
'py.jpg', 'ny.jpg',
'pz.jpg', 'nz.jpg'
])
scene.environment = envMap
// 使用 PMREMGenerator 生成优化的环境贴图
const pmremGenerator = new THREE.PMREMGenerator(renderer)
const envMapTexture = pmremGenerator.fromScene(scene).texture
scene.environment = envMapTexture雾效果
| 雾类型 | 构造函数 | 特点 | 适用场景 |
|---|---|---|---|
Fog | new THREE.Fog(color, near, far) | 线性雾,距离越远越浓 | 室内场景 |
FogExp2 | new THREE.FogExp2(color, density) | 指数雾,更自然 | 开放场景 |
javascript
// 线性雾:near 处开始,far 处完全透明
scene.fog = new THREE.Fog(0xcccccc, 10, 50)
// 参数说明:
// - color: 雾的颜色
// - near: 开始产生雾效果的近距离
// - far: 完全被雾遮蔽的远距离
// 指数雾:密度控制雾的浓度
scene.fog = new THREE.FogExp2(0xcccccc, 0.02)
// 参数说明:
// - color: 雾的颜色
// - density: 雾的密度,值越大雾越浓场景图(Scene Graph)
树形结构
Three.js 使用树形结构组织场景中的对象,形成父子层级关系:
code
Scene (根节点)
├── Group "buildings" [建筑组]
│ ├── Mesh "building_1" [建筑1]
│ │ └── Mesh "door" [门]
│ └── Mesh "building_2" [建筑2]
├── Group "lights" [灯光组]
│ ├── AmbientLight [环境光]
│ ├── DirectionalLight [方向光]
│ └── PointLight [点光源]
├── Mesh "ground" [地面]
└── Camera [相机]Object3D 基类
所有场景中的对象都继承自 THREE.Object3D,它提供了基础的变换和层级管理功能。
Object3D 核心属性
| 属性 | 类型 | 说明 |
|---|---|---|
position | Vector3 | 本地位置坐标 |
rotation | Euler | 旋转角度(弧度) |
quaternion | Quaternion | 四元数旋转 |
scale | Vector3 | 缩放比例 |
visible | Boolean | 是否可见 |
castShadow | Boolean | 是否投射阴影 |
receiveShadow | Boolean | 是否接收阴影 |
frustumCulled | Boolean | 是否进行视锥体裁剪 |
matrix | Matrix4 | 本地变换矩阵 |
matrixWorld | Matrix4 | 世界变换矩阵 |
userData | Object | 自定义数据存储 |
javascript
const object = new THREE.Object3D()
// 位置操作
object.position.set(5, 3, 2)
object.position.x = 10
object.translateX(1)
object.translateY(2)
object.translateZ(3)
// 旋转操作
object.rotation.set(Math.PI / 4, 0, 0) // 绕X轴旋转45度
object.rotation.x = Math.PI / 2
object.rotateX(Math.PI / 4)
object.rotateY(Math.PI / 6)
object.rotateZ(Math.PI / 3)
// 缩放操作
object.scale.set(2, 2, 2)
object.scale.x = 1.5
// 自定义数据
object.userData = {
id: 'player_001',
type: 'character',
health: 100,
level: 5
}Object3D 核心方法
| 方法 | 参数 | 返回值 | 说明 |
|---|---|---|---|
add(object) | Object3D | - | 添加子对象 |
remove(object) | Object3D | - | 移除子对象 |
traverse(callback) | Function | - | 遍历所有后代 |
traverseVisible(callback) | Function | - | 遍历可见后代 |
traverseAncestors(callback) | Function | - | 遍历所有祖先 |
getObjectById(id) | Number | Object3D | 通过ID查找 |
getObjectByName(name) | String | Object3D | 通过名称查找 |
getObjectByProperty(name, value) | String, Any | Object3D | 通过属性查找 |
getWorldPosition(target) | Vector3 | Vector3 | 获取世界坐标 |
getWorldDirection(target) | Vector3 | Vector3 | 获取世界方向 |
getWorldQuaternion(target) | Quaternion | Quaternion | 获取世界四元数 |
getWorldScale(target) | Vector3 | Vector3 | 获取世界缩放 |
localToWorld(vector) | Vector3 | Vector3 | 本地转世界坐标 |
worldToLocal(vector) | Vector3 | Vector3 | 世界转本地坐标 |
lookAt(x, y, z) | Number/Vector3 | - | 朝向指定位置 |
updateMatrix() | - | - | 更新本地矩阵 |
updateMatrixWorld(force) | Boolean | - | 更新世界矩阵 |
dispose() | - | - | 释放资源 |
javascript
// 添加和移除对象
object.add(child)
object.remove(child)
// 遍历所有后代
object.traverse((child) => {
console.log(child.type, child.name)
})
// 查找对象
const found = object.getObjectByName('player')
const byId = object.getObjectById(1)
// 获取世界信息
const worldPos = new THREE.Vector3()
object.getWorldPosition(worldPos)
const worldDir = new THREE.Vector3()
object.getWorldDirection(worldDir)Group(组)
Group 用于组织多个对象,方便统一管理和操作。
javascript
// 创建组
const group = new THREE.Group()
group.name = 'buildings' // 命名便于查找
// 创建并添加对象到组
const cube1 = new THREE.Mesh(geometry1, material1)
cube1.name = 'cube_1'
const cube2 = new THREE.Mesh(geometry2, material2)
cube2.name = 'cube_2'
group.add(cube1)
group.add(cube2)
// 将组添加到场景
scene.add(group)
// 统一操作组(影响所有子对象)
group.position.set(0, 2, 0) // 整体移动
group.rotation.y = Math.PI / 4 // 整体旋转
group.scale.set(2, 2, 2) // 整体缩放
// 移动组(相对于当前位置)
group.translateX(5)
// 控制整体可见性
group.visible = false // 隐藏所有子对象层级关系示例
javascript
// 创建太阳系层级结构
const solarSystem = new THREE.Group()
solarSystem.name = 'solarSystem'
// 地球轨道组
const earthOrbit = new THREE.Group()
earthOrbit.name = 'earthOrbit'
// 月球轨道组(相对于地球)
const moonOrbit = new THREE.Group()
moonOrbit.name = 'moonOrbit'
// 创建天体
const sun = new THREE.Mesh(sunGeometry, sunMaterial)
sun.name = 'sun'
const earth = new THREE.Mesh(earthGeometry, earthMaterial)
earth.name = 'earth'
const moon = new THREE.Mesh(moonGeometry, moonMaterial)
moon.name = 'moon'
// 设置初始位置
earth.position.set(10, 0, 0) // 地球距太阳10单位
moon.position.set(2, 0, 0) // 月球距地球2单位
// 构建层级结构
moonOrbit.add(moon)
earthOrbit.add(earth)
earthOrbit.add(moonOrbit)
solarSystem.add(sun)
solarSystem.add(earthOrbit)
scene.add(solarSystem)
// 动画循环中更新轨道
function animate() {
requestAnimationFrame(animate)
earthOrbit.rotation.y += 0.01 // 地球公转
moonOrbit.rotation.y += 0.05 // 月球公转
earth.rotation.y += 0.02 // 地球自转
renderer.render(scene, camera)
}坐标系统
Three.js 使用右手坐标系:X轴指向右,Y轴指向上,Z轴指向观察者。
code
Y
│
│
│
└─────── X
╱
╱
Z世界坐标
场景的全局坐标系统,所有对象的最终渲染位置由其世界坐标决定。
javascript
const object = new THREE.Object3D()
object.position.set(5, 3, 2) // 设置本地坐标
// 获取世界坐标(考虑所有父级的变换)
const worldPosition = new THREE.Vector3()
object.getWorldPosition(worldPosition)
console.log('世界坐标:', worldPosition)本地坐标
相对于父对象的坐标,子对象的变换会继承父对象的变换。
javascript
// 父对象在世界原点
const parent = new THREE.Object3D()
parent.position.set(10, 0, 0) // 父对象在世界坐标 (10, 0, 0)
// 子对象相对于父对象
const child = new THREE.Object3D()
child.position.set(5, 0, 0) // 本地坐标 (5, 0, 0)
parent.add(child)
scene.add(parent)
// child 的世界坐标 = 父坐标 + 本地坐标 = (15, 0, 0)
const worldPos = new THREE.Vector3()
child.getWorldPosition(worldPos)
console.log(worldPos) // Vector3 {x: 15, y: 0, z: 0}坐标转换
javascript
// 本地坐标转世界坐标
const worldPos = new THREE.Vector3(5, 0, 0)
object.localToWorld(worldPos)
// 世界坐标转本地坐标
const localPos = new THREE.Vector3(15, 0, 0)
object.worldToLocal(localPos)
// 获取世界方向(对象前方)
const worldDirection = new THREE.Vector3()
object.getWorldDirection(worldDirection)
// 更新矩阵后获取精确的世界信息
object.updateMatrixWorld(true)
const matrix = object.matrixWorld场景操作
添加对象
javascript
// 添加单个对象
scene.add(mesh)
// 添加多个对象(一次性添加)
scene.add(mesh1, mesh2, mesh3)
// 使用 Group 批量管理
const group = new THREE.Group()
group.add(mesh1, mesh2, mesh3)
scene.add(group)
// 检查对象是否在场景中
console.log(scene.children.includes(mesh))移除对象
javascript
// 移除单个对象
scene.remove(mesh)
// 移除并释放资源
function removeObject(object) {
scene.remove(object)
if (object.geometry) {
object.geometry.dispose()
}
if (object.material) {
if (Array.isArray(object.material)) {
object.material.forEach(mat => {
if (mat.map) mat.map.dispose()
mat.dispose()
})
} else {
if (object.material.map) object.material.map.dispose()
object.material.dispose()
}
}
}
// 移除所有子对象
while (scene.children.length > 0) {
const child = scene.children[0]
scene.remove(child)
}
// 清空场景但保留相机和灯光
scene.children.forEach((child) => {
if (child.type !== 'Camera' && child.type !== 'AmbientLight') {
scene.remove(child)
}
})查找对象
javascript
// 通过 ID 查找(每个对象有唯一的 id)
const object = scene.getObjectById(1)
// 通过名称查找(需要预先设置 name 属性)
const player = scene.getObjectByName('player')
// 通过自定义属性查找
const enemy = scene.getObjectByProperty('type', 'enemy')
// 遍历查找特定类型的对象
scene.traverse((child) => {
if (child.isMesh) {
console.log('网格对象:', child.name)
}
if (child.isLight) {
console.log('灯光对象:', child.type)
}
})
// 使用 userData 查找
scene.traverse((child) => {
if (child.userData.type === 'enemy') {
console.log('敌人:', child.userData.health)
}
})遍历场景
javascript
// 遍历所有后代(包括自身)
scene.traverse((child) => {
console.log(child.type, child.name)
// 对特定类型操作
if (child.isMesh) {
child.material.wireframe = true
}
})
// 只遍历可见对象
scene.traverseVisible((child) => {
console.log('可见:', child.type)
})
// 遍历祖先(向上查找)
object.traverseAncestors((parent) => {
console.log('父级:', parent.name)
})场景优化
对象池
复用对象,减少创建和销毁开销,适用于频繁创建销毁的场景(如子弹、粒子)。
javascript
class ObjectPool {
constructor(createFn, initialSize = 10, maxSize = 100) {
this.createFn = createFn
this.pool = []
this.active = []
this.maxSize = maxSize
// 预创建对象
for (let i = 0; i < initialSize; i++) {
this.pool.push(this.createFn())
}
}
// 获取对象
get() {
let object
if (this.pool.length > 0) {
object = this.pool.pop()
} else if (this.active.length < this.maxSize) {
object = this.createFn()
} else {
console.warn('对象池已满')
return null
}
object.visible = true
this.active.push(object)
return object
}
// 释放对象
release(object) {
const index = this.active.indexOf(object)
if (index > -1) {
this.active.splice(index, 1)
object.visible = false
this.pool.push(object)
}
}
// 释放所有对象
releaseAll() {
while (this.active.length > 0) {
this.release(this.active[0])
}
}
}
// 使用对象池管理子弹
const bulletPool = new ObjectPool(() => {
const geometry = new THREE.SphereGeometry(0.1)
const material = new THREE.MeshBasicMaterial({ color: 0xff0000 })
return new THREE.Mesh(geometry, material)
}, 20, 100)
// 发射子弹
const bullet = bulletPool.get()
if (bullet) {
bullet.position.copy(player.position)
scene.add(bullet)
}
// 子弹消失时释放
bulletPool.release(bullet)
scene.remove(bullet)LOD(Level of Detail)
根据距离显示不同精度的模型,优化远距离对象的渲染性能。
javascript
const lod = new THREE.LOD()
// 创建不同精度的模型
const highDetail = new THREE.Mesh(
new THREE.SphereGeometry(1, 32, 32),
material
)
const mediumDetail = new THREE.Mesh(
new THREE.SphereGeometry(1, 16, 16),
material
)
const lowDetail = new THREE.Mesh(
new THREE.SphereGeometry(1, 8, 8),
material
)
// 设置不同距离的显示等级
lod.addLevel(highDetail, 0) // 0-10 单位显示高精度
lod.addLevel(mediumDetail, 10) // 10-50 单位显示中精度
lod.addLevel(lowDetail, 50) // 50+ 单位显示低精度
// 更新 LOD(需要相机位置)
function animate() {
lod.update(camera)
renderer.render(scene, camera)
}
scene.add(lod)实例化渲染
渲染大量相同几何体的对象时,大幅提升性能。
javascript
// 创建实例化网格
const geometry = new THREE.BoxGeometry(1, 1, 1)
const material = new THREE.MeshStandardMaterial({ color: 0x00ff00 })
const count = 1000
const mesh = new THREE.InstancedMesh(geometry, material, count)
// 设置每个实例的变换矩阵
const dummy = new THREE.Object3D()
const colors = []
for (let i = 0; i < count; i++) {
// 设置位置
dummy.position.set(
Math.random() * 100 - 50,
Math.random() * 100 - 50,
Math.random() * 100 - 50
)
// 设置旋转
dummy.rotation.set(
Math.random() * Math.PI,
Math.random() * Math.PI,
Math.random() * Math.PI
)
// 设置缩放
dummy.scale.setScalar(Math.random() * 0.5 + 0.5)
// 更新矩阵
dummy.updateMatrix()
mesh.setMatrixAt(i, dummy.matrix)
// 设置颜色(需要开启 vertexColors)
colors.push(Math.random(), Math.random(), Math.random())
}
// 应用颜色
mesh.instanceMatrix.needsUpdate = true
scene.add(mesh)场景序列化
导出场景
javascript
import { GLTFExporter } from 'three/examples/jsm/exporters/GLTFExporter.js'
function exportScene(scene, filename = 'scene.gltf') {
const exporter = new GLTFExporter()
exporter.parse(
scene,
(gltf) => {
const output = JSON.stringify(gltf, null, 2)
// 下载文件
const blob = new Blob([output], { type: 'application/json' })
const url = URL.createObjectURL(blob)
const link = document.createElement('a')
link.href = url
link.download = filename
link.click()
URL.revokeObjectURL(url)
},
(error) => {
console.error('导出失败:', error)
},
{ binary: false } // false = GLTF, true = GLB
)
}导入场景
javascript
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader.js'
import { DRACOLoader } from 'three/examples/jsm/loaders/DRACOLoader.js'
const loader = new GLTFLoader()
// 可选:使用 Draco 压缩
const dracoLoader = new DRACOLoader()
dracoLoader.setDecoderPath('/draco/')
loader.setDRACOLoader(dracoLoader)
loader.load(
'scene.gltf',
(gltf) => {
const model = gltf.scene
// 处理模型
model.traverse((child) => {
if (child.isMesh) {
child.castShadow = true
child.receiveShadow = true
}
})
scene.add(model)
},
(progress) => {
console.log('加载进度:', (progress.loaded / progress.total * 100).toFixed(2) + '%')
},
(error) => {
console.error('加载失败:', error)
}
)场景管理
多场景管理
javascript
class SceneManager {
constructor() {
this.scenes = new Map()
this.currentScene = null
this.clock = new THREE.Clock()
}
create(name, options = {}) {
const scene = new THREE.Scene()
// 配置场景
if (options.background !== undefined) {
scene.background = new THREE.Color(options.background)
}
if (options.fog) {
if (options.fog.type === 'exp2') {
scene.fog = new THREE.FogExp2(options.fog.color, options.fog.density)
} else {
scene.fog = new THREE.Fog(options.fog.color, options.fog.near, options.fog.far)
}
}
if (options.environment) {
scene.environment = options.environment
}
// 存储场景配置
const sceneData = {
scene,
name,
init: options.init || (() => {}),
update: options.update || (() => {}),
destroy: options.destroy || (() => {}),
isInitialized: false
}
this.scenes.set(name, sceneData)
return sceneData
}
switch(name) {
const sceneData = this.scenes.get(name)
if (!sceneData) {
console.error(`场景 "${name}" 不存在`)
return false
}
// 卸载当前场景
if (this.currentScene && this.currentScene !== sceneData) {
this.currentScene.destroy()
this.currentScene.isInitialized = false
}
// 初始化新场景
if (!sceneData.isInitialized) {
sceneData.init(sceneData.scene)
sceneData.isInitialized = true
}
this.currentScene = sceneData
return true
}
update() {
if (this.currentScene) {
const delta = this.clock.getDelta()
const elapsed = this.clock.getElapsedTime()
this.currentScene.update(delta, elapsed)
}
}
remove(name) {
const sceneData = this.scenes.get(name)
if (sceneData) {
sceneData.destroy()
this.scenes.delete(name)
}
}
}
// 使用场景管理器
const sceneManager = new SceneManager()
// 创建菜单场景
sceneManager.create('menu', {
background: 0x333333,
init: (scene) => {
// 初始化菜单对象
scene.add(createMenuUI())
},
update: (delta) => {
// 菜单动画
}
})
// 创建游戏场景
sceneManager.create('game', {
background: 0x87ceeb,
fog: { type: 'linear', color: 0xcccccc, near: 10, far: 100 },
init: (scene) => {
// 初始化游戏对象
scene.add(createPlayer())
scene.add(createEnemies())
},
update: (delta) => {
// 游戏逻辑更新
},
destroy: () => {
// 清理游戏资源
}
})
// 切换场景
sceneManager.switch('game')场景状态保存
javascript
class SceneState {
constructor() {
this.states = new Map()
}
save(name, scene) {
const state = {
timestamp: Date.now(),
objects: []
}
scene.traverse((child) => {
if (child.isMesh || child.isGroup) {
state.objects.push({
uuid: child.uuid,
name: child.name,
type: child.type,
position: child.position.toArray(),
rotation: [child.rotation.x, child.rotation.y, child.rotation.z],
scale: child.scale.toArray(),
visible: child.visible
})
}
})
this.states.set(name, state)
return state
}
restore(name, scene) {
const state = this.states.get(name)
if (!state) {
console.error(`状态 "${name}" 不存在`)
return false
}
state.objects.forEach((objState) => {
const object = scene.getObjectByProperty('uuid', objState.uuid)
if (object) {
object.position.fromArray(objState.position)
object.rotation.set(objState.rotation[0], objState.rotation[1], objState.rotation[2])
object.scale.fromArray(objState.scale)
object.visible = objState.visible
}
})
return true
}
delete(name) {
this.states.delete(name)
}
}
// 使用状态保存
const stateManager = new SceneState()
// 保存当前状态
stateManager.save('checkpoint_1', scene)
// 恢复状态
stateManager.restore('checkpoint_1', scene)常见问题
Q1: 场景中的对象不显示?
诊断步骤:
javascript
// 1. 检查对象是否已添加到场景
console.log('对象在场景中:', scene.children.includes(object))
// 2. 检查对象位置
console.log('对象位置:', object.position)
console.log('对象世界位置:', object.getWorldPosition(new THREE.Vector3()))
// 3. 检查对象可见性
console.log('对象可见:', object.visible)
// 4. 检查相机视锥体
const frustum = new THREE.Frustum()
const matrix = new THREE.Matrix4().multiplyMatrices(
camera.projectionMatrix,
camera.matrixWorldInverse
)
frustum.setFromProjectionMatrix(matrix)
console.log('对象在视锥体中:', frustum.intersectsObject(object))
// 5. 检查材质和几何体
console.log('材质:', object.material)
console.log('几何体:', object.geometry)Q2: 如何正确清空场景?
javascript
function clearScene(scene) {
// 遍历并释放所有资源
scene.traverse((child) => {
// 清理几何体
if (child.geometry) {
child.geometry.dispose()
}
// 清理材质
if (child.material) {
const materials = Array.isArray(child.material) ? child.material : [child.material]
materials.forEach((mat) => {
// 清理材质中的纹理
if (mat.map) mat.map.dispose()
if (mat.normalMap) mat.normalMap.dispose()
if (mat.roughnessMap) mat.roughnessMap.dispose()
if (mat.metalnessMap) mat.metalnessMap.dispose()
if (mat.aoMap) mat.aoMap.dispose()
if (mat.emissiveMap) mat.emissiveMap.dispose()
mat.dispose()
})
}
})
// 移除所有子对象
while (scene.children.length > 0) {
scene.remove(scene.children[0])
}
}Q3: 如何优化大型场景?
优化策略:
javascript
// 1. 使用 LOD 技术
const lod = new THREE.LOD()
lod.addLevel(highDetailMesh, 0)
lod.addLevel(lowDetailMesh, 50)
scene.add(lod)
// 2. 视锥体裁剪(Three.js 自动处理)
object.frustumCulled = true // 默认开启
// 3. 实例化渲染大量相同对象
const instancedMesh = new THREE.InstancedMesh(geometry, material, count)
scene.add(instancedMesh)
// 4. 对象池复用
const pool = new ObjectPool(createFunction)
// 5. 分块加载(按需加载场景区域)
function loadChunk(x, z) {
// 只加载玩家附近的区块
}
// 6. 降低绘制调用
// - 合并几何体
// - 使用实例化
// - 减少材质数量
// 7. 使用 Octree 或 BVH 加速碰撞检测
// 适用于大型场景的对象查询Q4: 子对象的世界坐标计算错误?
javascript
// 问题:获取的世界坐标不正确
// 原因:矩阵未更新
// 解决方案:强制更新矩阵
object.updateMatrixWorld(true)
// 然后获取世界坐标
const worldPos = new THREE.Vector3()
object.getWorldPosition(worldPos)
// 或在渲染器中设置自动更新
renderer.autoUpdateObjects = true // 默认为 true最佳实践
1. 使用 Group 组织对象
javascript
// 按功能分组
const buildings = new THREE.Group()
buildings.name = 'buildings'
const vehicles = new THREE.Group()
vehicles.name = 'vehicles'
const characters = new THREE.Group()
characters.name = 'characters'
scene.add(buildings, vehicles, characters)
// 方便统一操作
buildings.visible = false // 隐藏所有建筑
characters.traverse(child => {
if (child.isMesh) {
child.castShadow = true
}
})2. 命名对象便于调试
javascript
const cube = new THREE.Mesh(geometry, material)
cube.name = 'player-cube'
scene.add(cube)
// 调试时查找
const player = scene.getObjectByName('player-cube')
// 在控制台中可见
console.log(scene)3. 使用 userData 存储自定义数据
javascript
const enemy = new THREE.Mesh(geometry, material)
enemy.userData = {
id: 'enemy_001',
type: 'enemy',
health: 100,
maxHealth: 100,
level: 5,
attack: 10,
defense: 5,
dropItems: ['sword', 'potion']
}
scene.add(enemy)
// 在游戏中使用
function damageEnemy(enemy, damage) {
enemy.userData.health -= damage
if (enemy.userData.health <= 0) {
// 掉落物品
dropItems(enemy.userData.dropItems)
// 移除敌人
scene.remove(enemy)
}
}4. 及时清理资源
javascript
// 创建清理函数
function disposeObject(object) {
// 清理几何体
if (object.geometry) {
object.geometry.dispose()
}
// 清理材质
if (object.material) {
const materials = Array.isArray(object.material) ? object.material : [object.material]
materials.forEach((mat) => {
// 清理所有纹理
for (const key in mat) {
if (mat[key] && typeof mat[key].dispose === 'function') {
mat[key].dispose()
}
}
mat.dispose()
})
}
}
// 使用
disposeObject(oldObject)
scene.remove(oldObject)