优化版 3D 红衣军 - Three.js
by TurboKoala83293 lines12.2 KB🛠️ Three.js (3D graphics)
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>优化版 3D 红衣军 - Three.js</title>
<style>
body { margin: 0; overflow: hidden; background-color: #87CEEB; }
canvas { display: block; }
/* 模拟游戏UI */
.ui { position: absolute; top: 20px; left: 20px; color: white; font-family: sans-serif; pointer-events: none; }
.crosshair { position: absolute; top: 50%; left: 50%; width: 20px; height: 20px; border: 2px solid rgba(255,255,255,0.5); border-radius: 50%; transform: translate(-50%, -50%); pointer-events: none; }
</style>
</head>
<body>
<div class="ui">优化版 3D 建模演示</div>
<div class="crosshair"></div>
<!-- 使用较新的 Three.js 版本以支持 CapsuleGeometry (胶囊体) -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r160/three.min.js"></script>
<script>
// 1. 初始化场景、相机、渲染器
const scene = new THREE.Scene();
scene.background = new THREE.Color(0x9ac8e8); // 更自然的天蓝色
scene.fog = new THREE.Fog(0x9ac8e8, 15, 60); // 远处雾化,增加深度感
const camera = new THREE.PerspectiveCamera(65, window.innerWidth / window.innerHeight, 0.1, 1000);
camera.position.set(0, 1.6, 3.5);
camera.lookAt(0, 1.2, 0);
const renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.shadowMap.enabled = true;
renderer.shadowMap.type = THREE.PCFSoftShadowMap; // 柔和阴影
renderer.outputColorSpace = THREE.SRGBColorSpace; // 更好的色彩表现
document.body.appendChild(renderer.domElement);
// 2. 优化的光照系统
const hemiLight = new THREE.HemisphereLight(0xffffff, 0x444444, 0.6); // 天空与地面环境光
scene.add(hemiLight);
const dirLight = new THREE.DirectionalLight(0xfff5e6, 1.2); // 偏暖的阳光
dirLight.position.set(10, 20, 10);
dirLight.castShadow = true;
dirLight.shadow.mapSize.width = 2048;
dirLight.shadow.mapSize.height = 2048;
dirLight.shadow.camera.near = 1;
dirLight.shadow.camera.far = 50;
dirLight.shadow.camera.left = -10;
dirLight.shadow.camera.right = 10;
dirLight.shadow.camera.top = 10;
dirLight.shadow.camera.bottom = -10;
scene.add(dirLight);
// 3. 优化地形 (有起伏的山丘)
const terrainGeo = new THREE.PlaneGeometry(80, 80, 64, 64);
const pos = terrainGeo.attributes.position;
for (let i = 0; i < pos.count; i++) {
const x = pos.getX(i);
const y = pos.getY(i);
// 使用正弦波模拟地形起伏
const z = Math.sin(x * 0.1) * Math.cos(y * 0.1) * 1.5 + Math.sin(x * 0.5) * 0.2;
pos.setZ(i, z);
}
terrainGeo.computeVertexNormals();
const terrainMat = new THREE.MeshStandardMaterial({ color: 0x4a7035, roughness: 0.9 });
const terrain = new THREE.Mesh(terrainGeo, terrainMat);
terrain.rotation.x = -Math.PI / 2;
terrain.receiveShadow = true;
scene.add(terrain);
// 4. 优化草地 (使用 InstancedMesh 渲染数千根草)
const grassGeo = new THREE.PlaneGeometry(0.1, 0.4);
// 双面渲染,避免旋转时看不见
const grassMat = new THREE.MeshStandardMaterial({ color: 0x5c8f43, side: THREE.DoubleSide, roughness: 0.8 });
const grassCount = 3000;
const grassMesh = new THREE.InstancedMesh(grassGeo, grassMat, grassCount);
grassMesh.receiveShadow = true;
grassMesh.castShadow = true;
const dummy = new THREE.Object3D();
for (let i = 0; i < grassCount; i++) {
const x = (Math.random() - 0.5) * 30;
const z = (Math.random() - 0.5) * 30;
// 计算地形高度
const y = Math.sin(x * 0.1) * Math.cos(z * 0.1) * 1.5 + Math.sin(x * 0.5) * 0.2;
dummy.position.set(x, y + 0.2, z);
dummy.rotation.y = Math.random() * Math.PI;
dummy.rotation.x = (Math.random() - 0.5) * 0.2;
dummy.updateMatrix();
grassMesh.setMatrixAt(i, dummy.matrix);
}
scene.add(grassMesh);
// 5. 优化红衣军建模
const soldier = new THREE.Group();
// 调整士兵站立在起伏地形上的高度
soldier.position.set(0, 0.3, 0);
// 材质定义 (使用更细腻的质感)
const matRedCoat = new THREE.MeshStandardMaterial({ color: 0xb30000, roughness: 0.7 }); // 红衣
const matWhitePants = new THREE.MeshStandardMaterial({ color: 0xe6e6e6, roughness: 0.8 }); // 白裤
const matBlackBoots = new THREE.MeshStandardMaterial({ color: 0x1a1a1a, roughness: 0.9 }); // 黑靴
const matSkin = new THREE.MeshStandardMaterial({ color: 0xf1c27d, roughness: 0.5 }); // 皮肤
const matBlackHat = new THREE.MeshStandardMaterial({ color: 0x111111, roughness: 0.8 }); // 帽子
const matGold = new THREE.MeshStandardMaterial({ color: 0xd4af37, metalness: 0.8, roughness: 0.3 }); // 金色装饰
const matWood = new THREE.MeshStandardMaterial({ color: 0x4a2e15, roughness: 0.9 }); // 枪托木头
const matMetal = new THREE.MeshStandardMaterial({ color: 0x888888, metalness: 0.9, roughness: 0.2 }); // 枪管金属
// 躯干 (使用胶囊体,更贴近人体曲线)
const torso = new THREE.Mesh(new THREE.CapsuleGeometry(0.25, 0.5, 4, 16), matRedCoat);
torso.position.y = 1.15;
torso.castShadow = true;
soldier.add(torso);
// 军装下摆 (用圆柱体模拟长外套的下半部分)
const coatSkirt = new THREE.Mesh(new THREE.CylinderGeometry(0.28, 0.32, 0.4, 16), matRedCoat);
coatSkirt.position.y = 0.75;
coatSkirt.castShadow = true;
soldier.add(coatSkirt);
// 白色交叉背带 (使用两个细长的扁长方体)
const beltGeo = new THREE.BoxGeometry(0.06, 0.8, 0.02);
const belt1 = new THREE.Mesh(beltGeo, matWhitePants);
belt1.position.set(0, 1.15, 0.26);
belt1.rotation.z = Math.PI / 4;
soldier.add(belt1);
const belt2 = new THREE.Mesh(beltGeo, matWhitePants);
belt2.position.set(0, 1.15, 0.26);
belt2.rotation.z = -Math.PI / 4;
soldier.add(belt2);
// 头部和五官
const head = new THREE.Mesh(new THREE.SphereGeometry(0.2, 32, 32), matSkin);
head.position.y = 1.65;
head.castShadow = true;
soldier.add(head);
// 鼻子 (小圆锥体)
const nose = new THREE.Mesh(new THREE.ConeGeometry(0.04, 0.1, 8), matSkin);
nose.position.set(0, 1.63, 0.2);
nose.rotation.x = Math.PI / 2;
soldier.add(nose);
// 18世纪三角帽 (用三个倾斜的平面组合)
const hatGroup = new THREE.Group();
const brimGeo = new THREE.CylinderGeometry(0.32, 0.32, 0.04, 3);
const brim = new THREE.Mesh(brimGeo, matBlackHat);
brim.rotation.y = Math.PI / 6;
hatGroup.add(brim);
const crown = new THREE.Mesh(new THREE.SphereGeometry(0.2, 16, 8, 0, Math.PI * 2, 0, Math.PI / 2), matBlackHat);
crown.position.y = 0.06;
hatGroup.add(crown);
// 帽徽
const badge = new THREE.Mesh(new THREE.BoxGeometry(0.08, 0.08, 0.02), matGold);
badge.position.set(0, 0.08, 0.28);
hatGroup.add(badge);
hatGroup.position.y = 1.82;
hatGroup.castShadow = true;
soldier.add(hatGroup);
// 四肢 (使用胶囊体)
const armGeo = new THREE.CapsuleGeometry(0.08, 0.4, 4, 12);
const legGeo = new THREE.CapsuleGeometry(0.1, 0.45, 4, 12);
// 左臂 (持枪姿势)
const leftArm = new THREE.Mesh(armGeo, matRedCoat);
leftArm.position.set(-0.35, 1.2, 0.15);
leftArm.rotation.set(-1.2, 0, 0.2); // 抬高并向前伸
leftArm.castShadow = true;
soldier.add(leftArm);
// 右臂 (托枪姿势)
const rightArm = new THREE.Mesh(armGeo, matRedCoat);
rightArm.position.set(0.35, 1.1, -0.1);
rightArm.rotation.set(-1.5, 0, -0.1);
rightArm.castShadow = true;
soldier.add(rightArm);
// 左腿
const leftLeg = new THREE.Mesh(legGeo, matWhitePants);
leftLeg.position.set(-0.15, 0.55, 0);
leftLeg.castShadow = true;
soldier.add(leftLeg);
// 右腿
const rightLeg = new THREE.Mesh(legGeo, matWhitePants);
rightLeg.position.set(0.15, 0.55, 0);
rightLeg.castShadow = true;
soldier.add(rightLeg);
// 靴子
const bootGeo = new THREE.BoxGeometry(0.18, 0.2, 0.35);
const leftBoot = new THREE.Mesh(bootGeo, matBlackBoots);
leftBoot.position.set(-0.15, 0.1, 0.05);
leftBoot.castShadow = true;
soldier.add(leftBoot);
const rightBoot = new THREE.Mesh(bootGeo, matBlackBoots);
rightBoot.position.set(0.15, 0.1, 0.05);
rightBoot.castShadow = true;
soldier.add(rightBoot);
// 枪械细节优化 (枪管 + 枪托 + 扳机)
const gunGroup = new THREE.Group();
const barrel = new THREE.Mesh(new THREE.CylinderGeometry(0.025, 0.025, 1.1, 12), matMetal);
barrel.rotation.x = Math.PI / 2;
barrel.position.z = 0.4;
gunGroup.add(barrel);
const stock = new THREE.Mesh(new THREE.BoxGeometry(0.08, 0.12, 0.6), matWood);
stock.position.z = -0.25;
gunGroup.add(stock);
const triggerGuard = new THREE.Mesh(new THREE.TorusGeometry(0.04, 0.01, 8, 16), matMetal);
triggerGuard.rotation.x = Math.PI / 2;
triggerGuard.position.set(0, -0.06, -0.1);
gunGroup.add(triggerGuard);
// 将枪调整到与左臂对齐
gunGroup.position.set(-0.1, 1.25, 0.3);
gunGroup.rotation.set(-0.1, 0.1, 0);
gunGroup.castShadow = true;
soldier.add(gunGroup);
scene.add(soldier);
// 6. 第一人称视角武器与手部优化
const fpGroup = new THREE.Group();
// 玩家第一人称手部 (红衣袖子)
const fpArm = new THREE.Mesh(new THREE.CapsuleGeometry(0.06, 0.3, 4, 12), matRedCoat);
fpArm.position.set(0.3, -0.3, -0.5);
fpArm.rotation.set(-1.5, 0, 0.5);
fpGroup.add(fpArm);
const fpHand = new THREE.Mesh(new THREE.SphereGeometry(0.06, 16, 16), matSkin);
fpHand.position.set(0.3, -0.15, -0.8);
fpGroup.add(fpHand);
// 玩家第一人称枪械
const fpBarrel = new THREE.Mesh(new THREE.CylinderGeometry(0.02, 0.02, 1.2, 12), matMetal);
fpBarrel.rotation.x = Math.PI / 2;
fpBarrel.position.set(0.25, -0.15, -1.0);
fpGroup.add(fpBarrel);
const fpStock = new THREE.Mesh(new THREE.BoxGeometry(0.08, 0.1, 0.5), matWood);
fpStock.position.set(0.25, -0.18, -0.4);
fpGroup.add(fpStock);
// 将第一人称武器绑定到相机
camera.add(fpGroup);
scene.add(camera);
// 7. 动画循环 (增加呼吸和草叶微动)
let clock = new THREE.Clock();
function animate() {
requestAnimationFrame(animate);
const time = clock.getElapsedTime();
const delta = clock.getDelta();
// 士兵呼吸动画 (微微起伏)
soldier.position.y = 0.3 + Math.sin(time * 2) * 0.01;
torso.scale.set(1 + Math.sin(time * 2) * 0.01, 1, 1 + Math.sin(time * 2) * 0.01);
// 枪口微动
gunGroup.rotation.x = -0.1 + Math.sin(time * 3) * 0.01;
// 草地风吹动效 (通过整体轻微旋转很难,这里调整整体草的渲染不现实,可略过,但可以做相机轻微晃动)
camera.position.y = 1.6 + Math.sin(time * 1.5) * 0.005;
renderer.render(scene, camera);
}
// 8. 响应窗口大小变化
window.addEventListener('resize', () => {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
});
animate();
</script>
</body>
</html>Game Source: 优化版 3D 红衣军 - Three.js
Creator: TurboKoala83
Libraries: three
Complexity: complex (293 lines, 12.2 KB)
The full source code is displayed above on this page.
Remix Instructions
To remix this game, copy the source code above and modify it. Add a ARCADELAB header at the top with "remix_of: 3d-three-js-turbokoala83-mujg7pgh" to link back to the original. Then publish at arcadelab.ai/publish.