高级WebGL游戏架构师,负责3D物理沙盒
来自 Wikiprompt,自由的提示词百科全书
高级WebGL游戏架构师,负责3D物理沙盒 一个基于Three.js和Cannon.js的物理沙盒提示,支持基于冲量的交互、真实阻尼以及模块化几何体支持。
提示词内容收藏
🌐
作为高级WebGL游戏架构师,我将设计一个基于Three.js和Cannon.js的高性能3D物理沙盒逻辑。以下是核心JavaScript实现及冲量向量计算的数学解释。
```javascript
// 核心模块:物理世界与场景初始化
class PhysicsSandbox {
constructor(container) {
// Three.js 场景
this.scene = new THREE.Scene();
this.camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
this.renderer = new THREE.WebGLRenderer({ antialias: true });
this.renderer.setSize(window.innerWidth, window.innerHeight);
container.appendChild(this.renderer.domElement);
// Cannon.js 物理世界
this.world = new CANNON.World();
this.world.gravity.set(0, -9.82, 0);
this.world.broadphase = new CANNON.SAPBroadphase(this.world);
this.world.defaultContactMaterial.friction = 0.3;
this.world.defaultContactMaterial.restitution = 0.7;
// 边界容器(3D盒体)
this.bounds = { width: 20, height: 15, depth: 20 };
this.createBoundary();
// 对象存储
this.meshes = [];
this.bodies = [];
// 交互系统
this.setupInteraction();
// 动画循环
this.animate();
}
// 创建边界墙
createBoundary() {
const wallMat = new THREE.MeshStandardMaterial({ color: 0x888888, transparent: true, opacity: 0.3 });
const wallGeo = new THREE.BoxGeometry(1, 1, 1);
// 六个面(底部、顶部、前后左右)
const walls = [
{ pos: [0, -this.bounds.height/2, 0], size: [this.bounds.width, 1, this.bounds.depth] },
{ pos: [0, this.bounds.height/2, 0], size: [this.bounds.width, 1, this.bounds.depth] },
{ pos: [0, 0, -this.bounds.depth/2], size: [this.bounds.width, this.bounds.height, 1] },
{ pos: [0, 0, this.bounds.depth/2], size: [this.bounds.width, this.bounds.height, 1] },
{ pos: [-this.bounds.width/2, 0, 0], size: [1, this.bounds.height, this.bounds.depth] },
{ pos: [this.bounds.width/2, 0, 0], size: [1, this.bounds.height, this.bounds.depth] }
];
walls.forEach(w => {
const mesh = new THREE.Mesh(wallGeo, wallMat);
mesh.scale.set(...w.size);
mesh.position.set(...w.pos);
this.scene.add(mesh);
// 物理墙(静态刚体)
const body = new CANNON.Body({ mass: 0 });
body.addShape(new CANNON.Box(new CANNON.Vec3(w.size[0]/2, w.size[1]/2, w.size[2]/2)));
body.position.set(...w.pos);
this.world.addBody(body);
});
}
// 添加不同几何体
addObject(type, position) {
let mesh, body, shape;
const material = new THREE.MeshStandardMaterial({
color: Math.random() * 0xffffff,
roughness: 0.4,
metalness: 0.2
});
switch(type) {
case 'sphere':
const radius = 0.5 + Math.random() * 0.5;
mesh = new THREE.Mesh(new THREE.SphereGeometry(radius, 16, 16), material);
shape = new CANNON.Sphere(radius);
break;
case 'box':
const size = 0.5 + Math.random() * 0.5;
mesh = new THREE.Mesh(new THREE.BoxGeometry(size, size, size), material);
shape = new CANNON.Box(new CANNON.Vec3(size/2, size/2, size/2));
break;
case 'convex':
// 凸包示例(八面体)
const geo = new THREE.OctahedronGeometry(0.7);
mesh = new THREE.Mesh(geo, material);
const vertices = geo.attributes.position.array;
const points = [];
for (let i = 0; i < vertices.length; i += 3) {
points.push(new CANNON.Vec3(vertices[i], vertices[i+1], vertices[i+2]));
}
shape = new CANNON.ConvexPolyhedron({ vertices: points });
break;
}
// 物理体配置
body = new CANNON.Body({
mass: 1,
shape: shape,
position: new CANNON.Vec3(position.x, position.y, position.z),
linearDamping: 0.1, // 线性阻尼
angularDamping: 0.2, // 角阻尼
material: new CANNON.Material({ friction: 0.3, restitution: 0.7 })
});
this.world.addBody(body);
this.scene.add(mesh);
this.meshes.push(mesh);
this.bodies.push(body);
}
// 交互系统:点击/触摸施加冲量
setupInteraction() {
const raycaster = new THREE.Raycaster();
const mouse = new THREE.Vector2();
const onPointerDown = (event) => {
// 归一化设备坐标
mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
mouse.y = -(event.clientY / window.innerHeight) * 2 + 1;
raycaster.setFromCamera(mouse, this.camera);
const intersects = raycaster.intersectObjects(this.meshes);
if (intersects.length > 0) {
const hit = intersects[0];
const meshIndex = this.meshes.indexOf(hit.object);
const body = this.bodies[meshIndex];
// 计算冲量向量
const impulse = this.calculateImpulse(hit.point, body);
body.applyImpulse(impulse.vector, impulse.point);
}
};
window.addEventListener('pointerdown', onPointerDown);
}
// 冲量向量计算(数学核心)
calculateImpulse(hitPoint, body) {
// 1. 从相机位置到点击点的方向向量
const cameraPos = this.camera.position;
const direction = new THREE.Vector3()
.subVectors(hitPoint, cameraPos)
.normalize();
// 2. 冲量强度(可配置,此处为固定值)
const impulseStrength = 15;
// 3. 计算冲量向量(世界空间)
const impulseVector = direction.multiplyScalar(impulseStrength);
// 4. 转换为Cannon.js向量
const impulse = new CANNON.Vec3(impulseVector.x, impulseVector.y, impulseVector.z);
// 5. 计算作用点(物体质心到点击点的偏移)
const point = new CANNON.Vec3(hitPoint.x, hitPoint.y, hitPoint.z);
return { vector: impulse, point: point };
}
// 动画循环:同步物理与渲染
animate() {
requestAnimationFrame(() => this.animate());
// 固定时间步长物理更新
this.world.step(1/60);
// 同步刚体位置到网格
for (let i = 0; i < this.bodies.length; i++) {
this.meshes[i].position.copy(this.bodies[i].position);
this.meshes[i].quaternion.copy(this.bodies[i].quaternion);
}
this.renderer.render(this.scene, this.camera);
}
}
// 初始化
const sandbox = new PhysicsSandbox(document.getElementById('container'));
sandbox.addObject('sphere', { x: 0, y: 5, z: 0 });
sandbox.addObject('box', { x: 2, y: 5, z: 0 });
sandbox.addObject('convex', { x: -2, y: 5, z: 0 });
```
## 冲量向量计算的数学实现
### 1. 方向向量推导
设相机位置为 \( C = (c_x, c_y, c_z) \),点击点(射线与物体交点)为 \( P = (p_x, p_y, p_z) \)。
方向向量:
\[
\vec{d} = \frac{P - C}{\|P - C\|}
\]
### 2. 冲量向量
冲量强度 \( I_0 \)(标量)乘以方向向量:
\[
\vec{I} = I_0 \cdot \vec{d}
\]
### 3. 物理应用
在Cannon.js中,`applyImpulse` 方法使用:
\[
\Delta v = \frac{\vec{I}}{m}
\]
\[
\Delta \omega = J^{-1} (\vec{r} \times \vec{I})
\]
其中:
- \( m \) 是物体质量
- \( J \) 是惯性张量
- \( \vec{r} = P - X \) 是从质心 \( X \) 到作用点 \( P \) 的偏移向量
### 4. 能量损失模拟
- **摩擦**:接触面切向力,通过 `friction` 参数控制
- **恢复系数**:碰撞前后法向速度比,`restitution = 0.7` 表示70%速度保留
- **线性阻尼**:\( F_{damping} = -c \cdot v \),指数衰减速度
- **角阻尼**:类似线性阻尼,但作用于角速度
### 5. 性能优化
- 使用 `SAPBroadphase` 宽阶段碰撞检测
- 固定时间步长 `1/60` 保证物理稳定性
- 直接复制位置和四元数,避免额外计算
此架构支持轻松扩展新几何体(只需在 `addObject` 中添加case),并保持物理与渲染的同步。
用法
此提示词专为 coding 设计。复制上方内容并粘贴到你常用的 AI 工具中。
为获得最佳效果,可将占位符(方括号或大写字母标示)替换为你的具体需求。
讨论
0 条评论