Skip to content

235. 综合实战:开放世界(三十六)

Published:

这节我们把之前的练舞房也放到 3D 世界里,并且让玩家也可以一起去跳舞。

2025-04-13 20.16.22.gif

首先添加一面镜子:

创建 dancingMirrorHut.js

/**
 * 镜屋 - 四面镜子
 */
import * as THREE from 'three';
import { Reflector } from 'three/addons/objects/Reflector.js';

const group = new THREE.Group();

const hutOffsetX = 20;
const hutOffsetY = 0;
const hutOffsetZ = -20;

const mirrorHeight = 2.5;
const roomSize = 6;
const mirrorWidth = roomSize; // 与边长一致,四面围合无空隙
const mirrorRadius = roomSize / 2;

function createMirror() {
  const geometry = new THREE.PlaneGeometry(mirrorWidth, mirrorHeight);
  const mirror = new Reflector(geometry, {
    textureWidth: 2048,
    textureHeight: 2048
  });

  mirror.position.set(hutOffsetX + mirrorRadius, hutOffsetY + mirrorHeight / 2, hutOffsetZ);
  mirror.rotateY(-Math.PI / 2);
  mirror.castShadow = false;
  mirror.receiveShadow = true;

  return mirror;
}

group.add(createMirror());

export const dancingMirrorHutPosition = { x: hutOffsetX, z: hutOffsetZ };
export { group as dancingMirrorHut };

export default group;

就是要用 Reflector 创建镜子,设置宽高、位置。

mian.js 引入:

image.png

顺便在地图上也标一下:

image.png

hut: { x: 20, z: -20, color: '#ff69b4', label: '镜屋' }

2026-03-08 21.41.59.gif

这样就有了一面镜子。

2026-03-08 21.42.52.gif

但是这面镜子背面是看不到的。

我们给他加一面墙。

image.png

/**
 * 镜屋 - 四面镜子
 */
import * as THREE from 'three';
import { Reflector } from 'three/addons/objects/Reflector.js';

const group = new THREE.Group();

const hutOffsetX = 20;
const hutOffsetY = 0;
const hutOffsetZ = -20;

const mirrorHeight = 2.5;
const roomSize = 6;
const mirrorWidth = roomSize; // 与边长一致,四面围合无空隙
const mirrorRadius = roomSize / 2;

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

function createMirror() {
  const geometry = new THREE.PlaneGeometry(mirrorWidth, mirrorHeight);
  const mirror = new Reflector(geometry, {
    textureWidth: 2048,
    textureHeight: 2048
  });

  mirror.position.set(hutOffsetX + mirrorRadius, hutOffsetY + mirrorHeight / 2, hutOffsetZ);
  mirror.rotateY(-Math.PI / 2);
  mirror.castShadow = false;
  mirror.receiveShadow = true;

  return mirror;
}

const wallThickness = 0.3;
const mirrorWallGap = 0.02; // 避免镜子和墙重叠导致闪烁

function createBackWall() {
  const geometry = new THREE.BoxGeometry(mirrorWidth, mirrorHeight, wallThickness);
  const wall = new THREE.Mesh(geometry, wallMaterial);
  wall.position.set(hutOffsetX + mirrorRadius + mirrorWallGap + wallThickness / 2, hutOffsetY + mirrorHeight / 2, hutOffsetZ);
  wall.rotateY(-Math.PI / 2);
  wall.castShadow = true;
  wall.receiveShadow = true;
  return wall;
}

group.add(createMirror());
group.add(createBackWall());

export const dancingMirrorHutPosition = { x: hutOffsetX, z: hutOffsetZ };
export { group as dancingMirrorHut };

export default group;

2026-03-08 21.51.32.gif

然后现在是没有物理效果的:

2026-03-08 21.57.28.gif

我们用 cannon 加一下物理效果:

image.png


const wallPosX = hutOffsetX + mirrorRadius + mirrorWallGap + wallThickness / 2;
const wallPosY = hutOffsetY + mirrorHeight / 2;
const wallPosZ = hutOffsetZ;
const wallBody = new CANNON.Body({
  mass: 0,
  position: new CANNON.Vec3(wallPosX, wallPosY, wallPosZ),
  quaternion: new CANNON.Quaternion().setFromAxisAngle(new CANNON.Vec3(0, 1, 0), -Math.PI / 2)
});
wallBody.addShape(new CANNON.Box(new CANNON.Vec3(mirrorWidth / 2, mirrorHeight / 2, wallThickness / 2)));
world.addBody(wallBody);

2026-03-08 22.00.35.gif

现在就不会穿过了。

案例代码上传了小册仓库

总结

这节我们加了一面镜子和一面墙。

镜子用 Relfector 实现,然后给墙加了物理效果。

镜子有了,下节把跳舞的人也加进来。

评论