feat: Implement procedural textures and enhanced furniture generation with collision avoidance.
This commit is contained in:
@@ -87,19 +87,21 @@ export class Player {
|
||||
|
||||
// 2. Lights
|
||||
// Main SpotLight (The beam)
|
||||
this.flashlight = new THREE.SpotLight(0xffffff, 10);
|
||||
this.flashlight = new THREE.SpotLight(0xffffff, 3.0); // Reduced brightness
|
||||
this.flashlight.angle = Math.PI / 6;
|
||||
this.flashlight.penumbra = 0.3;
|
||||
this.flashlight.decay = 1.5;
|
||||
this.flashlight.distance = 60;
|
||||
this.flashlight.penumbra = 0.5; // Softer edges
|
||||
this.flashlight.decay = 2.0; // Faster falloff
|
||||
this.flashlight.distance = 50;
|
||||
this.flashlight.position.set(0, 0, -0.1); // At tip
|
||||
this.flashlight.target.position.set(0, 0, -10); // Aim forward
|
||||
// Aim inward to crosshair (Converge)
|
||||
// Group is at (0.3, -0.25), so target needs to be (-0.3, 0.25) to hit center 0,0 relative to camera
|
||||
this.flashlight.target.position.set(-0.3, 0.25, -20);
|
||||
|
||||
this.flashlightGroup.add(this.flashlight);
|
||||
this.flashlightGroup.add(this.flashlight.target);
|
||||
|
||||
// PointLight (The glow around the player)
|
||||
this.bulbLight = new THREE.PointLight(0xffffff, 2, 4); // Low range (4m)
|
||||
this.bulbLight = new THREE.PointLight(0xffffff, 0.5, 3); // Very dim local glow
|
||||
this.bulbLight.position.set(0, 0, -0.15); // Slightly ahead of tip
|
||||
this.flashlightGroup.add(this.bulbLight);
|
||||
}
|
||||
@@ -271,7 +273,7 @@ export class Player {
|
||||
this.flashlight.angle = Math.max(0.1, this.flashlight.angle - angleSpeed);
|
||||
}
|
||||
if (this.adjustBright) {
|
||||
this.flashlight.intensity = Math.min(50, this.flashlight.intensity + speed * 10);
|
||||
this.flashlight.intensity = Math.min(15, this.flashlight.intensity + speed * 10);
|
||||
this.flashlight.angle = Math.min(Math.PI / 2, this.flashlight.angle + angleSpeed);
|
||||
}
|
||||
// Sync bulb light
|
||||
|
||||
456
src/World.js
456
src/World.js
@@ -4,34 +4,54 @@ export class World {
|
||||
constructor(scene) {
|
||||
this.scene = scene;
|
||||
this.colliders = [];
|
||||
this.staticObstacles = []; // Track pillars etc for spawning
|
||||
this.dustParticles = null;
|
||||
}
|
||||
|
||||
load() {
|
||||
// Generate Textures
|
||||
this.texConcrete = this.createProceduralTexture('concrete');
|
||||
this.texWall = this.createProceduralTexture('wall');
|
||||
this.texWood = this.createProceduralTexture('wood');
|
||||
this.texFabric = this.createProceduralTexture('fabric');
|
||||
|
||||
// Standard lighting for horror
|
||||
const ambientLight = new THREE.AmbientLight(0x404040, 0.2); // Very dim ambient
|
||||
this.scene.add(ambientLight);
|
||||
|
||||
// Floor
|
||||
const floorGeo = new THREE.PlaneGeometry(30, 30);
|
||||
// Floor (Dirty Concrete)
|
||||
const floorGeo = new THREE.PlaneGeometry(60, 60);
|
||||
const floorMat = new THREE.MeshStandardMaterial({
|
||||
color: 0x222222,
|
||||
roughness: 0.9
|
||||
map: this.texConcrete,
|
||||
roughness: 0.9,
|
||||
metalness: 0.1
|
||||
});
|
||||
const floor = new THREE.Mesh(floorGeo, floorMat);
|
||||
floor.rotation.x = -Math.PI / 2;
|
||||
this.scene.add(floor);
|
||||
|
||||
// Simple walls
|
||||
this.createWall(0, 2.5, -15, 30, 5); // Back
|
||||
this.createWall(0, 2.5, 15, 30, 5); // Front
|
||||
this.createWall(-15, 2.5, 0, 30, 5, true); // Left
|
||||
this.createWall(15, 2.5, 0, 30, 5, true); // Right
|
||||
// Ceiling (Dirty Concrete, Darker)
|
||||
const ceilingGeo = new THREE.PlaneGeometry(60, 60);
|
||||
const ceilingMat = new THREE.MeshStandardMaterial({
|
||||
map: this.texConcrete,
|
||||
roughness: 1.0,
|
||||
metalness: 0
|
||||
});
|
||||
const ceiling = new THREE.Mesh(ceilingGeo, ceilingMat);
|
||||
ceiling.rotation.x = Math.PI / 2;
|
||||
ceiling.position.y = 5; // Top of walls
|
||||
this.scene.add(ceiling);
|
||||
|
||||
// Add some "horror" pillars
|
||||
for (let i = 0; i < 8; i++) {
|
||||
const px = (Math.random() - 0.5) * 20;
|
||||
const pz = (Math.random() - 0.5) * 20;
|
||||
// Simple walls (Expanded to 60x60 with Moldy Plaster)
|
||||
this.createWall(0, 2.5, -30, 60, 5); // Back
|
||||
this.createWall(0, 2.5, 30, 60, 5); // Front
|
||||
this.createWall(-30, 2.5, 0, 60, 5, true); // Left
|
||||
this.createWall(30, 2.5, 0, 60, 5, true); // Right
|
||||
|
||||
// Add some "horror" pillars (Spread out, Concrete)
|
||||
for (let i = 0; i < 12; i++) {
|
||||
const px = (Math.random() - 0.5) * 50;
|
||||
const pz = (Math.random() - 0.5) * 50;
|
||||
this.createPillar(px, 2.5, pz);
|
||||
}
|
||||
|
||||
@@ -44,9 +64,397 @@ export class World {
|
||||
this.scene.add(target);
|
||||
this.colliders.push(target);
|
||||
|
||||
this.createFurniture(45);
|
||||
this.createDust();
|
||||
}
|
||||
|
||||
createProceduralTexture(type) {
|
||||
const size = 512;
|
||||
const canvas = document.createElement('canvas');
|
||||
canvas.width = size;
|
||||
canvas.height = size;
|
||||
const ctx = canvas.getContext('2d');
|
||||
|
||||
// Base fill
|
||||
if (type === 'concrete') {
|
||||
ctx.fillStyle = '#333333';
|
||||
ctx.fillRect(0, 0, size, size);
|
||||
// Noise & Stains
|
||||
for (let i = 0; i < 2000; i++) {
|
||||
ctx.fillStyle = Math.random() > 0.5 ? '#222222' : '#444444';
|
||||
ctx.globalAlpha = 0.1;
|
||||
const x = Math.random() * size;
|
||||
const y = Math.random() * size;
|
||||
const r = Math.random() * 50;
|
||||
ctx.beginPath();
|
||||
ctx.arc(x, y, r, 0, Math.PI * 2);
|
||||
ctx.fill();
|
||||
}
|
||||
} else if (type === 'wall') {
|
||||
ctx.fillStyle = '#555555'; // Plaster
|
||||
ctx.fillRect(0, 0, size, size);
|
||||
// General Grime
|
||||
for (let i = 0; i < 1000; i++) {
|
||||
ctx.fillStyle = '#333333';
|
||||
ctx.globalAlpha = 0.05;
|
||||
ctx.fillRect(Math.random() * size, Math.random() * size, Math.random() * 100, Math.random() * 100);
|
||||
}
|
||||
// Mold at bottom
|
||||
const grad = ctx.createLinearGradient(0, size, 0, size * 0.5);
|
||||
grad.addColorStop(0, 'rgba(10, 30, 10, 0.8)'); // Black-Green
|
||||
grad.addColorStop(1, 'rgba(10, 30, 10, 0)');
|
||||
ctx.fillStyle = grad;
|
||||
ctx.globalAlpha = 1.0;
|
||||
ctx.fillRect(0, 0, size, size);
|
||||
} else if (type === 'wood') {
|
||||
ctx.fillStyle = '#3e2723';
|
||||
ctx.fillRect(0, 0, size, size);
|
||||
// Wood grain streaks
|
||||
for (let i = 0; i < 500; i++) {
|
||||
ctx.fillStyle = '#281815';
|
||||
ctx.globalAlpha = 0.3;
|
||||
const x = Math.random() * size;
|
||||
const w = Math.random() * 10 + 2;
|
||||
ctx.fillRect(x, 0, w, size);
|
||||
}
|
||||
} else if (type === 'fabric') {
|
||||
ctx.fillStyle = '#4e342e'; // Faded brown
|
||||
ctx.fillRect(0, 0, size, size);
|
||||
// Crosshatch noise
|
||||
ctx.strokeStyle = '#3e2723';
|
||||
ctx.globalAlpha = 0.2;
|
||||
for (let i = 0; i < 200; i++) {
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(0, Math.random() * size);
|
||||
ctx.lineTo(size, Math.random() * size);
|
||||
ctx.stroke();
|
||||
}
|
||||
}
|
||||
|
||||
const tex = new THREE.CanvasTexture(canvas);
|
||||
tex.wrapS = THREE.RepeatWrapping;
|
||||
tex.wrapT = THREE.RepeatWrapping;
|
||||
return tex;
|
||||
}
|
||||
|
||||
createWall(x, y, z, width, height, rotate = false) {
|
||||
const geo = new THREE.BoxGeometry(width, height, 0.5);
|
||||
const mat = new THREE.MeshStandardMaterial({
|
||||
map: this.texWall,
|
||||
roughness: 0.95
|
||||
});
|
||||
// Tile the texture
|
||||
mat.map.repeat.set(width / 5, height / 5);
|
||||
|
||||
const wall = new THREE.Mesh(geo, mat);
|
||||
wall.position.set(x, y, z);
|
||||
if (rotate) wall.rotation.y = Math.PI / 2;
|
||||
this.scene.add(wall);
|
||||
this.colliders.push(wall);
|
||||
}
|
||||
|
||||
createPillar(x, y, z) {
|
||||
const geo = new THREE.BoxGeometry(1, 5, 1);
|
||||
const mat = new THREE.MeshStandardMaterial({
|
||||
map: this.texConcrete, // Reuse concrete for pillars
|
||||
roughness: 0.9
|
||||
});
|
||||
const pillar = new THREE.Mesh(geo, mat);
|
||||
pillar.position.set(x, y, z);
|
||||
this.scene.add(pillar);
|
||||
this.colliders.push(pillar);
|
||||
this.staticObstacles.push(pillar); // Add to spawn blocker list
|
||||
}
|
||||
|
||||
createFurniture(count) {
|
||||
const props = [];
|
||||
|
||||
for (let i = 0; i < count; i++) {
|
||||
// Try 10 times to find a valid spot
|
||||
let px, pz, valid = false;
|
||||
for (let attempt = 0; attempt < 10; attempt++) {
|
||||
px = (Math.random() - 0.5) * 54; // Spread almost to walls (60 width)
|
||||
pz = (Math.random() - 0.5) * 54;
|
||||
|
||||
// Avoid center spawn
|
||||
if (Math.abs(px) < 6 && Math.abs(pz) < 6) continue;
|
||||
|
||||
// Check distance to other props
|
||||
let overlap = false;
|
||||
for (const p of props) {
|
||||
const dx = p.px - px;
|
||||
const dz = p.pz - pz;
|
||||
if (dx * dx + dz * dz < 16) { // Min distance 4 units
|
||||
overlap = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// ALSO Check distance to Pillars (Static Obstacles)
|
||||
if (!overlap) {
|
||||
for (const obs of this.staticObstacles) {
|
||||
const dx = obs.position.x - px;
|
||||
const dz = obs.position.z - pz;
|
||||
if (dx * dx + dz * dz < 9) { // 3m distance from pillars
|
||||
overlap = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!overlap) {
|
||||
valid = true;
|
||||
props.push({ px, pz });
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!valid) continue; // Skip if no spot found
|
||||
|
||||
const type = Math.floor(Math.random() * 10); // Increased variety to 10 types
|
||||
const rot = Math.random() * Math.PI * 2;
|
||||
|
||||
const group = new THREE.Group();
|
||||
group.position.set(px, 0, pz);
|
||||
group.rotation.y = rot;
|
||||
this.scene.add(group);
|
||||
|
||||
if (type === 0) this.spawnCouch(group);
|
||||
else if (type === 1) this.spawnTable(group);
|
||||
else if (type === 2) this.spawnChair(group);
|
||||
else if (type === 3) this.spawnLamp(group);
|
||||
else if (type === 4) this.spawnClock(group);
|
||||
else if (type === 5) this.spawnMannequin(group);
|
||||
else if (type === 6) this.spawnTV(group);
|
||||
else if (type === 7) this.spawnBed(group); // New
|
||||
else if (type === 8) this.spawnBookshelf(group); // New
|
||||
else if (type === 9) this.spawnDrawer(group); // New
|
||||
}
|
||||
}
|
||||
|
||||
spawnBed(group) {
|
||||
this.addColliderBox(group, 1.2, 0.6, 2.0);
|
||||
|
||||
const frameMat = new THREE.MeshStandardMaterial({ color: 0x3e2723, roughness: 0.9, metalness: 0.4 });
|
||||
const mattressMat = new THREE.MeshStandardMaterial({ map: this.texFabric, color: 0x888877, roughness: 1.0 });
|
||||
|
||||
const f1 = new THREE.Mesh(new THREE.BoxGeometry(1.2, 0.1, 2.0), frameMat); f1.position.y = 0.3; group.add(f1);
|
||||
const hbTop = new THREE.Mesh(new THREE.BoxGeometry(1.2, 0.05, 0.05), frameMat); hbTop.position.set(0, 0.9, -1.0); group.add(hbTop);
|
||||
const legBL = new THREE.Mesh(new THREE.BoxGeometry(0.1, 1.0, 0.1), frameMat); legBL.position.set(-0.55, 0.5, -1.0); group.add(legBL);
|
||||
const legBR = new THREE.Mesh(new THREE.BoxGeometry(0.1, 1.0, 0.1), frameMat); legBR.position.set(0.55, 0.5, -1.0); group.add(legBR);
|
||||
|
||||
const matMesh = new THREE.Mesh(new THREE.BoxGeometry(1.1, 0.25, 1.9), mattressMat); matMesh.position.y = 0.45; matMesh.rotation.z = (Math.random() - 0.5) * 0.05; group.add(matMesh);
|
||||
}
|
||||
|
||||
spawnBookshelf(group) {
|
||||
this.addColliderBox(group, 1.1, 2.0, 0.5);
|
||||
|
||||
const mat = new THREE.MeshStandardMaterial({ map: this.texWood, color: 0x4a3c31, roughness: 0.9 });
|
||||
|
||||
const left = new THREE.Mesh(new THREE.BoxGeometry(0.1, 2.0, 0.4), mat); left.position.set(-0.5, 1.0, 0); group.add(left);
|
||||
const right = new THREE.Mesh(new THREE.BoxGeometry(0.1, 2.0, 0.4), mat); right.position.set(0.5, 1.0, 0); group.add(right);
|
||||
const top = new THREE.Mesh(new THREE.BoxGeometry(1.1, 0.1, 0.4), mat); top.position.set(0, 2.0, 0); group.add(top);
|
||||
|
||||
for (let y = 0.5; y < 2.0; y += 0.5) {
|
||||
if (Math.random() < 0.2) continue;
|
||||
const shelf = new THREE.Mesh(new THREE.BoxGeometry(1.0, 0.05, 0.38), mat); shelf.position.set(0, y, 0); shelf.rotation.z = (Math.random() - 0.5) * 0.2; group.add(shelf);
|
||||
if (Math.random() > 0.5) {
|
||||
const book = new THREE.Mesh(new THREE.BoxGeometry(0.1, 0.25, 0.2), new THREE.MeshStandardMaterial({ color: Math.random() * 0xffffff }));
|
||||
book.position.set((Math.random() - 0.5) * 0.8, y + 0.15, 0); book.rotation.z = (Math.random() - 0.5) * 0.5; group.add(book);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
spawnDrawer(group) {
|
||||
this.addColliderBox(group, 0.9, 1.2, 0.5);
|
||||
|
||||
const mat = new THREE.MeshStandardMaterial({ map: this.texWood, color: 0x3d2b1f, roughness: 0.8 });
|
||||
|
||||
const body = new THREE.Mesh(new THREE.BoxGeometry(0.9, 1.2, 0.5), mat); body.position.y = 0.6; group.add(body);
|
||||
|
||||
[0.3, 0.6, 0.9].forEach(y => {
|
||||
if (Math.random() < 0.2) return;
|
||||
const d = new THREE.Mesh(new THREE.BoxGeometry(0.8, 0.25, 0.05), mat);
|
||||
const offset = Math.random() < 0.3 ? 0.2 : 0;
|
||||
d.position.set(0, y, 0.26 + offset);
|
||||
group.add(d);
|
||||
});
|
||||
}
|
||||
addColliderBox(group, w, h, d) {
|
||||
// Create invisible hitbox: slightly smaller than visual to forgive AABB rotation errors
|
||||
const geo = new THREE.BoxGeometry(w * 0.85, h, d * 0.85);
|
||||
const mat = new THREE.MeshBasicMaterial({ visible: false }); // Invisible
|
||||
const box = new THREE.Mesh(geo, mat);
|
||||
box.position.y = h / 2;
|
||||
group.add(box);
|
||||
this.colliders.push(box);
|
||||
}
|
||||
|
||||
spawnCouch(group) {
|
||||
this.addColliderBox(group, 2.2, 1.0, 0.8);
|
||||
|
||||
// Use worn fabric texture
|
||||
const mat = new THREE.MeshStandardMaterial({
|
||||
map: this.texFabric,
|
||||
roughness: 1.0,
|
||||
color: 0x666666
|
||||
});
|
||||
|
||||
// Base (Sagging)
|
||||
const base = new THREE.Mesh(new THREE.BoxGeometry(2.2, 0.45, 0.8), mat);
|
||||
base.position.y = 0.225;
|
||||
group.add(base);
|
||||
|
||||
// Back (Uneven)
|
||||
const back = new THREE.Mesh(new THREE.BoxGeometry(2.2, 0.65, 0.2), mat);
|
||||
back.position.set(0, 0.7, -0.3);
|
||||
back.rotation.x = -0.1;
|
||||
group.add(back);
|
||||
|
||||
// Arms (Worn)
|
||||
const armL = new THREE.Mesh(new THREE.BoxGeometry(0.3, 0.45, 0.85), mat);
|
||||
armL.position.set(-1.0, 0.5, 0);
|
||||
group.add(armL);
|
||||
|
||||
const armR = new THREE.Mesh(new THREE.BoxGeometry(0.3, 0.45, 0.85), mat);
|
||||
armR.position.set(1.0, 0.5, 0);
|
||||
group.add(armR);
|
||||
|
||||
// Cushions
|
||||
const cushionL = new THREE.Mesh(new THREE.BoxGeometry(0.8, 0.1, 0.6), mat);
|
||||
cushionL.position.set(-0.5, 0.5, 0.1);
|
||||
cushionL.rotation.y = 0.1; cushionL.rotation.z = 0.05;
|
||||
group.add(cushionL);
|
||||
|
||||
const cushionR = new THREE.Mesh(new THREE.BoxGeometry(0.8, 0.1, 0.6), mat);
|
||||
cushionR.position.set(0.5, 0.5, 0.1);
|
||||
cushionR.rotation.y = -0.05;
|
||||
group.add(cushionR);
|
||||
}
|
||||
|
||||
spawnTable(group) {
|
||||
this.addColliderBox(group, 1.5, 0.9, 1.0);
|
||||
|
||||
const mat = new THREE.MeshStandardMaterial({ map: this.texWood, roughness: 0.8, metalness: 0.1 });
|
||||
|
||||
// Top
|
||||
const top = new THREE.Mesh(new THREE.BoxGeometry(1.5, 0.1, 1.0), mat);
|
||||
top.position.y = 0.8;
|
||||
group.add(top);
|
||||
|
||||
// Legs
|
||||
const legGeo = new THREE.BoxGeometry(0.1, 0.8, 0.1);
|
||||
const positions = [[-0.6, -0.4], [0.6, -0.4], [-0.6, 0.4], [0.6, 0.4]];
|
||||
|
||||
positions.forEach((pos) => {
|
||||
const leg = new THREE.Mesh(legGeo, mat);
|
||||
leg.position.set(pos[0], 0.4, pos[1]);
|
||||
leg.rotation.z = (Math.random() - 0.5) * 0.1;
|
||||
leg.rotation.x = (Math.random() - 0.5) * 0.1;
|
||||
group.add(leg);
|
||||
});
|
||||
}
|
||||
|
||||
spawnChair(group) {
|
||||
this.addColliderBox(group, 0.5, 0.8, 0.5); // Simple box for chair
|
||||
|
||||
const mat = new THREE.MeshStandardMaterial({ map: this.texWood, roughness: 0.9 });
|
||||
|
||||
// Seat
|
||||
const seat = new THREE.Mesh(new THREE.BoxGeometry(0.5, 0.08, 0.5), mat);
|
||||
seat.position.y = 0.5;
|
||||
seat.rotation.z = 0.02;
|
||||
group.add(seat);
|
||||
|
||||
// Back
|
||||
const railL = new THREE.Mesh(new THREE.BoxGeometry(0.05, 0.5, 0.05), mat); railL.position.set(-0.2, 0.75, -0.22); group.add(railL);
|
||||
const railR = new THREE.Mesh(new THREE.BoxGeometry(0.05, 0.5, 0.05), mat); railR.position.set(0.2, 0.75, -0.22); group.add(railR);
|
||||
const topRail = new THREE.Mesh(new THREE.BoxGeometry(0.5, 0.1, 0.05), mat); topRail.position.set(0, 1.0, -0.22); group.add(topRail);
|
||||
|
||||
// Legs
|
||||
const legGeo = new THREE.BoxGeometry(0.05, 0.5, 0.05);
|
||||
[[-0.2, -0.2], [0.2, -0.2], [-0.2, 0.2], [0.2, 0.2]].forEach(pos => {
|
||||
const leg = new THREE.Mesh(legGeo, mat);
|
||||
leg.position.set(pos[0], 0.25, pos[1]);
|
||||
group.add(leg);
|
||||
});
|
||||
|
||||
if (Math.random() < 0.1) {
|
||||
group.rotation.x = Math.PI / 2;
|
||||
group.position.y = 0.2;
|
||||
}
|
||||
}
|
||||
|
||||
spawnLamp(group) {
|
||||
this.addColliderBox(group, 0.1, 2.0, 0.1);
|
||||
|
||||
const mat = new THREE.MeshStandardMaterial({ color: 0x3e2723, roughness: 0.9, metalness: 0.3 });
|
||||
const pole = new THREE.Mesh(new THREE.CylinderGeometry(0.05, 0.05, 2.0, 8), mat);
|
||||
pole.position.y = 1.0; pole.rotation.z = 0.1;
|
||||
group.add(pole); // No collider
|
||||
|
||||
const shade = new THREE.Mesh(new THREE.ConeGeometry(0.4, 0.5, 16, 1, true), new THREE.MeshStandardMaterial({ color: 0xd7ccc8, side: THREE.DoubleSide, roughness: 1.0 }));
|
||||
shade.position.set(-0.1, 1.9, 0); shade.rotation.z = 0.3;
|
||||
group.add(shade);
|
||||
}
|
||||
|
||||
spawnClock(group) {
|
||||
this.addColliderBox(group, 0.7, 2.2, 0.6);
|
||||
|
||||
const woodColor = 0x2a1a10;
|
||||
const mat = new THREE.MeshStandardMaterial({ color: woodColor, roughness: 0.6 });
|
||||
const base = new THREE.Mesh(new THREE.BoxGeometry(0.7, 0.4, 0.6), mat); base.position.y = 0.2; group.add(base);
|
||||
const body = new THREE.Mesh(new THREE.BoxGeometry(0.5, 1.4, 0.45), mat); body.position.y = 1.1; group.add(body);
|
||||
const headBox = new THREE.Mesh(new THREE.BoxGeometry(0.7, 0.7, 0.5), mat); headBox.position.y = 2.15; group.add(headBox);
|
||||
const top = new THREE.Mesh(new THREE.CylinderGeometry(0.05, 0.35, 0.2, 4), mat); top.rotation.y = Math.PI / 4; top.position.y = 2.6; group.add(top);
|
||||
const face = new THREE.Mesh(new THREE.CircleGeometry(0.25, 32), new THREE.MeshBasicMaterial({ color: 0xeeddcc })); face.position.set(0, 2.15, 0.26); group.add(face);
|
||||
const glass = new THREE.Mesh(new THREE.PlaneGeometry(0.3, 1.0), new THREE.MeshStandardMaterial({ color: 0x111111, roughness: 0.1, metalness: 0.8 })); glass.position.set(0, 1.1, 0.23); group.add(glass);
|
||||
const pendulum = new THREE.Mesh(new THREE.CylinderGeometry(0.1, 0.1, 0.05, 16), new THREE.MeshStandardMaterial({ color: 0xae8b0c, metalness: 0.8, roughness: 0.3 })); pendulum.rotation.x = Math.PI / 2; pendulum.position.set(0, 0.9, 0.24); group.add(pendulum);
|
||||
const rod = new THREE.Mesh(new THREE.BoxGeometry(0.02, 0.8, 0.02), new THREE.MeshStandardMaterial({ color: 0xae8b0c })); rod.position.set(0, 1.3, 0.24); group.add(rod);
|
||||
}
|
||||
|
||||
spawnMannequin(group) {
|
||||
this.addColliderBox(group, 0.5, 1.9, 0.4);
|
||||
|
||||
const mat = new THREE.MeshStandardMaterial({ color: 0xd0c0b0, roughness: 0.4 });
|
||||
const hips = new THREE.Mesh(new THREE.BoxGeometry(0.35, 0.2, 0.25), mat); hips.position.y = 0.95; group.add(hips);
|
||||
const legGeo = new THREE.CylinderGeometry(0.09, 0.07, 0.95, 8);
|
||||
const legL = new THREE.Mesh(legGeo, mat); legL.position.set(-0.12, 0.475, 0); group.add(legL);
|
||||
const legR = new THREE.Mesh(legGeo, mat); legR.position.set(0.12, 0.475, 0); group.add(legR);
|
||||
const torso = new THREE.Mesh(new THREE.BoxGeometry(0.38, 0.6, 0.24), mat); torso.position.y = 1.35; group.add(torso);
|
||||
const chest = new THREE.Mesh(new THREE.BoxGeometry(0.45, 0.2, 0.26), mat); chest.position.y = 1.6; group.add(chest);
|
||||
const armGeo = new THREE.CylinderGeometry(0.06, 0.05, 0.7, 8);
|
||||
const armL = new THREE.Mesh(armGeo, mat); armL.position.set(-0.28, 1.35, 0); armL.rotation.z = 0.1; group.add(armL);
|
||||
const armR = new THREE.Mesh(armGeo, mat); armR.position.set(0.28, 1.35, 0); armR.rotation.z = -0.1; group.add(armR);
|
||||
const neck = new THREE.Mesh(new THREE.CylinderGeometry(0.06, 0.07, 0.15), mat); neck.position.y = 1.75; group.add(neck);
|
||||
const head = new THREE.Mesh(new THREE.SphereGeometry(0.12, 16, 16), mat); head.position.y = 1.88; group.add(head);
|
||||
}
|
||||
|
||||
spawnTV(group) {
|
||||
this.addColliderBox(group, 0.8, 1.5, 0.6);
|
||||
|
||||
const standMat = new THREE.MeshStandardMaterial({ color: 0x4a3c31, roughness: 0.9 });
|
||||
const stand = new THREE.Mesh(new THREE.BoxGeometry(0.8, 0.6, 0.5), standMat); stand.position.y = 0.3; group.add(stand);
|
||||
|
||||
const tvMat = new THREE.MeshStandardMaterial({ color: 0x1a1a1a, roughness: 0.5 });
|
||||
const tv = new THREE.Mesh(new THREE.BoxGeometry(0.7, 0.55, 0.55), tvMat); tv.position.y = 0.875; group.add(tv);
|
||||
|
||||
const screenMat = new THREE.MeshStandardMaterial({ color: 0x111111, metalness: 0.6, roughness: 0.2 });
|
||||
const screen = new THREE.Mesh(new THREE.SphereGeometry(0.35, 32, 32, 0, Math.PI * 2, 0, 0.6), screenMat);
|
||||
screen.position.set(0, 0.9, 0.15); screen.scale.set(0.9, 0.7, 0.5); screen.rotation.x = -Math.PI / 2; group.add(screen);
|
||||
|
||||
const sidePanel = new THREE.Mesh(new THREE.BoxGeometry(0.15, 0.4, 0.05), new THREE.MeshStandardMaterial({ color: 0x333333 }));
|
||||
sidePanel.position.set(0.25, 0.875, 0.28); group.add(sidePanel);
|
||||
const knob1 = new THREE.Mesh(new THREE.CylinderGeometry(0.03, 0.03, 0.05), new THREE.MeshStandardMaterial({ color: 0x888888 }));
|
||||
knob1.rotation.x = Math.PI / 2; knob1.position.set(0.25, 0.95, 0.31); group.add(knob1);
|
||||
const knob2 = knob1.clone(); knob2.position.set(0.25, 0.8, 0.31); group.add(knob2);
|
||||
|
||||
const ant = new THREE.Mesh(new THREE.CylinderGeometry(0.005, 0.005, 0.6), new THREE.MeshStandardMaterial({ color: 0xaaaaaa }));
|
||||
ant.position.set(-0.1, 1.3, 0); ant.rotation.z = 0.4; group.add(ant);
|
||||
const ant2 = ant.clone(); ant2.position.set(0.1, 1.3, 0); ant2.rotation.z = -0.4; group.add(ant2);
|
||||
}
|
||||
|
||||
|
||||
createDust() {
|
||||
// Create 800 dust particles (Reduced)
|
||||
const count = 800;
|
||||
@@ -56,9 +464,9 @@ export class World {
|
||||
const velocities = [];
|
||||
|
||||
for (let i = 0; i < count; i++) {
|
||||
positions[i * 3] = (Math.random() - 0.5) * 40;
|
||||
positions[i * 3] = (Math.random() - 0.5) * 60;
|
||||
positions[i * 3 + 1] = Math.random() * 5;
|
||||
positions[i * 3 + 2] = (Math.random() - 0.5) * 40;
|
||||
positions[i * 3 + 2] = (Math.random() - 0.5) * 60;
|
||||
|
||||
colors[i * 3] = 0; // Start invisible (black)
|
||||
colors[i * 3 + 1] = 0;
|
||||
@@ -88,24 +496,6 @@ export class World {
|
||||
this.scene.add(this.dustParticles);
|
||||
}
|
||||
|
||||
createWall(x, y, z, width, height, rotate = false) {
|
||||
const geo = new THREE.BoxGeometry(width, height, 0.5);
|
||||
const mat = new THREE.MeshStandardMaterial({ color: 0x333333 });
|
||||
const wall = new THREE.Mesh(geo, mat);
|
||||
wall.position.set(x, y, z);
|
||||
if (rotate) wall.rotation.y = Math.PI / 2;
|
||||
this.scene.add(wall);
|
||||
this.colliders.push(wall);
|
||||
}
|
||||
|
||||
createPillar(x, y, z) {
|
||||
const geo = new THREE.BoxGeometry(1, 5, 1);
|
||||
const mat = new THREE.MeshStandardMaterial({ color: 0x444444 });
|
||||
const pillar = new THREE.Mesh(geo, mat);
|
||||
pillar.position.set(x, y, z);
|
||||
this.scene.add(pillar);
|
||||
this.colliders.push(pillar);
|
||||
}
|
||||
|
||||
update(dt, player) {
|
||||
if (!this.dustParticles) return;
|
||||
|
||||
Reference in New Issue
Block a user