引言
在 Three.js 的三维场景中,很多时候我们需要为玩家提供一个 2D 小地图,以便他们能够快速了解自己的位置和周围环境。本教程将演示如何在 Three.js 中实现一个 2D 小地图,它会随着玩家的移动而平滑调整,确保玩家始终保持在小地图的可见范围内。
需求分析
- 小地图是一张大尺寸 2D 图片,代表整个场景。
- 小地图窗口 只显示大地图的一部分,相当于视口。
- 玩家在小地图中移动,实时反映 3D 场景中的位置。
- 当玩家接近边界时,小地图平滑移动,确保玩家始终在视口范围内。
代码实现
1. HTML 结构
<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Three.js 2D 小地图</title>
<style>
body { margin: 0; overflow: hidden; position: relative; }
canvas { display: block; }
/* 小地图窗口 */
#miniMap {
position: absolute;
bottom: 10px;
right: 10px;
width: 200px;
height: 200px;
border: 2px solid white;
border-radius: 10px;
overflow: hidden;
}
/* 小地图背景 */
#mapContainer {
position: relative;
width: 800px;
height: 800px;
background: url('mini-map.png') no-repeat;
background-size: cover;
}
/* 玩家标记 */
#playerDot {
position: absolute;
width: 10px;
height: 10px;
background: red;
border-radius: 50%;
transform: translate(-50%, -50%);
}
</style>
</head>
<body>
<script type="module">
import * as THREE from 'https://cdn.jsdelivr.net/npm/three@latest/build/three.module.js';
// 创建 Three.js 场景
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
camera.position.set(0, 10, 20);
camera.lookAt(0, 0, 0);
// 渲染器
const renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
// 创建地面
const groundGeometry = new THREE.PlaneGeometry(20, 20);
const groundMaterial = new THREE.MeshBasicMaterial({ color: 0xaaaaaa, side: THREE.DoubleSide });
const ground = new THREE.Mesh(groundGeometry, groundMaterial);
ground.rotation.x = -Math.PI / 2;
scene.add(ground);
// 玩家(立方体)
const playerGeometry = new THREE.BoxGeometry(1, 1, 1);
const playerMaterial = new THREE.MeshBasicMaterial({ color: 0xff0000 });
const player = new THREE.Mesh(playerGeometry, playerMaterial);
scene.add(player);
// 小地图窗口
const miniMap = document.createElement('div');
miniMap.id = 'miniMap';
document.body.appendChild(miniMap);
// 小地图背景
const mapContainer = document.createElement('div');
mapContainer.id = 'mapContainer';
miniMap.appendChild(mapContainer);
// 玩家标记
const playerDot = document.createElement('div');
playerDot.id = 'playerDot';
mapContainer.appendChild(playerDot);
// 小地图参数
const worldSize = 20;
const mapSize = 800;
const viewportSize = 200;
const halfViewport = viewportSize / 2;
function animate() {
requestAnimationFrame(animate);
player.position.x = Math.sin(Date.now() * 0.001) * 9;
player.position.z = Math.cos(Date.now() * 0.001) * 9;
updateMiniMap(player.position);
renderer.render(scene, camera);
}
function updateMiniMap(playerPosition) {
const minX = -worldSize / 2, maxX = worldSize / 2;
const minZ = -worldSize / 2, maxZ = worldSize / 2;
const mapX = ((playerPosition.x - minX) / (maxX - minX)) * mapSize;
const mapY = ((playerPosition.z - minZ) / (maxZ - minZ)) * mapSize;
playerDot.style.left = `${mapX}px`;
playerDot.style.top = `${mapY}px`;
let offsetX = mapX - halfViewport;
let offsetY = mapY - halfViewport;
offsetX = Math.max(0, Math.min(offsetX, mapSize - viewportSize));
offsetY = Math.max(0, Math.min(offsetY, mapSize - viewportSize));
mapContainer.style.transform = `translate(${-offsetX}px, ${-offsetY}px)`;
}
animate();
</script>
</body>
</html>
代码解析
-
Three.js 场景:创建了一个带有地面的 3D 场景,并添加了一个代表玩家的立方体。
-
小地图:使用
div
元素创建了一个#miniMap
窗口,内含#mapContainer
作为整个大地图。 -
玩家标记:
#playerDot
代表玩家在小地图中的位置。 -
更新小地图:
- 计算玩家在小地图的坐标。
- 确保玩家始终可见:如果玩家接近小地图边界,则平滑移动
#mapContainer
。
总结
通过本教程,实现了一个 Three.js 3D 场景与 2D 小地图联动的效果。小地图能够动态跟随玩家移动,并在接近边缘时平滑调整,使玩家始终可见。你可以进一步扩展该功能,例如:
- 小地图旋转 适配相机角度。
- 缩放小地图,适应不同的场景需求。
- 显示多个标记点(NPC、任务目标等)。
🚀