Skip to content

214. 综合实战:开放世界(十五)

Published:

上节实现了开飞机:

2026-01-03 00.27.25.gif

2026-01-03 00.42.18.gif

这节继续完善这个开放世界。

现在开放世界里并没有建筑,我们加一个房屋,可以开门走进去那种。

屋内有各种家具,都加上物理效果。

其实我们之前画过房子:

2025-03-24 23.50.00.gif

只不过当时只是 ui,并没有物理效果。

现在学了 cannon 之后,可以加上物理碰撞效果了。

首先,我们把这个房子画一下:

创建 src/house.js

import * as THREE from 'three';

const group = new THREE.Group();

const houseOffsetX = -20;
const houseOffsetY = 0;
const houseOffsetZ = -20;

const roomWidth = 6;
const roomDepth = 8;
const wallHeight = 3;
const wallThickness = 0.25;
const doorOpeningWidth = 1.2;

const wallMaterial = new THREE.MeshPhongMaterial({ color: 0xd4c5a9 });

// === 前墙(有门)===
// 左边墙
const leftWallWidth = (roomWidth - doorOpeningWidth) / 2;
const leftWallGeo = new THREE.BoxGeometry(leftWallWidth, wallHeight, wallThickness);
const leftWallMesh = new THREE.Mesh(leftWallGeo, wallMaterial);
leftWallMesh.position.set(
  houseOffsetX + (-roomWidth / 2 + leftWallWidth / 2),
  houseOffsetY + wallHeight / 2,
  houseOffsetZ + 0
);
leftWallMesh.castShadow = true;
leftWallMesh.receiveShadow = true;
group.add(leftWallMesh);

// 右边墙
const rightWallMesh = new THREE.Mesh(leftWallGeo, wallMaterial);
rightWallMesh.position.set(
  houseOffsetX + (roomWidth / 2 - leftWallWidth / 2),
  houseOffsetY + wallHeight / 2,
  houseOffsetZ + 0
);
rightWallMesh.castShadow = true;
rightWallMesh.receiveShadow = true;
group.add(rightWallMesh);

// 门框上方
const topFrameHeight = wallHeight - 2;
const topFrameGeo = new THREE.BoxGeometry(doorOpeningWidth, topFrameHeight, wallThickness);
const topFrameMesh = new THREE.Mesh(topFrameGeo, wallMaterial);
topFrameMesh.position.set(
  houseOffsetX + 0,
  houseOffsetY + (wallHeight - topFrameHeight / 2),
  houseOffsetZ + 0
);
topFrameMesh.castShadow = true;
topFrameMesh.receiveShadow = true;
group.add(topFrameMesh);

// === 后墙 ===
const backWallGeo = new THREE.BoxGeometry(roomWidth, wallHeight, wallThickness);
const backWallMesh = new THREE.Mesh(backWallGeo, wallMaterial);
backWallMesh.position.set(
  houseOffsetX + 0,
  houseOffsetY + wallHeight / 2,
  houseOffsetZ + (-roomDepth)
);
backWallMesh.castShadow = true;
backWallMesh.receiveShadow = true;
group.add(backWallMesh);

// === 左侧墙 ===
const sideWallGeo = new THREE.BoxGeometry(wallThickness, wallHeight, roomDepth);
const leftSideWallMesh = new THREE.Mesh(sideWallGeo, wallMaterial);
leftSideWallMesh.position.set(
  houseOffsetX + (-roomWidth / 2),
  houseOffsetY + wallHeight / 2,
  houseOffsetZ + (-roomDepth / 2)
);
leftSideWallMesh.castShadow = true;
leftSideWallMesh.receiveShadow = true;
group.add(leftSideWallMesh);

// === 右侧墙 ===
const rightSideWallMesh = new THREE.Mesh(sideWallGeo, wallMaterial);
rightSideWallMesh.position.set(
  houseOffsetX + (roomWidth / 2),
  houseOffsetY + wallHeight / 2,
  houseOffsetZ + (-roomDepth / 2)
);
rightSideWallMesh.castShadow = true;
rightSideWallMesh.receiveShadow = true;
group.add(rightSideWallMesh);

// === 屋顶 ===
const roofGeo = new THREE.BoxGeometry(roomWidth, 0.15, roomDepth);
const roofMat = new THREE.MeshPhongMaterial({ color: 0x8b7355 });
const roofMesh = new THREE.Mesh(roofGeo, roofMat);
roofMesh.position.set(
  houseOffsetX + 0,
  houseOffsetY + wallHeight,
  houseOffsetZ + (-roomDepth / 2)
);
roofMesh.receiveShadow = true;
roofMesh.castShadow = true;
group.add(roofMesh);

export default group;

用 BoxGeometry 来画各面墙:

image.png

image.png

image.png

屋顶也是:

image.png

前面的墙我们之前都是 ExtrudeGeometry + Shape + holes 来实现,现在简化了一下,用三个 BoxGeometry 来拼:

image.png

在 main.js 引入下:

image.png

看下效果:

2026-01-03 20.38.10.gif

这样,房子就画好了。

只不过现在人能穿过墙壁,还没加上物理效果,下节用 cannon 加一下物理刚体。

案例代码上传了小册仓库

总结

这节我们给开放世界加上了一个房子。

用 BoxGeometry 画的墙壁和屋顶。

下节加上 cannon 物理效果。

评论