feat: Implement procedural textures and enhanced furniture generation with collision avoidance.

This commit is contained in:
2026-01-03 09:21:52 +00:00
parent 2eac31aae9
commit 94b6d7ac80
2 changed files with 432 additions and 40 deletions

View File

@@ -87,19 +87,21 @@ export class Player {
// 2. Lights // 2. Lights
// Main SpotLight (The beam) // 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.angle = Math.PI / 6;
this.flashlight.penumbra = 0.3; this.flashlight.penumbra = 0.5; // Softer edges
this.flashlight.decay = 1.5; this.flashlight.decay = 2.0; // Faster falloff
this.flashlight.distance = 60; this.flashlight.distance = 50;
this.flashlight.position.set(0, 0, -0.1); // At tip 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);
this.flashlightGroup.add(this.flashlight.target); this.flashlightGroup.add(this.flashlight.target);
// PointLight (The glow around the player) // 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.bulbLight.position.set(0, 0, -0.15); // Slightly ahead of tip
this.flashlightGroup.add(this.bulbLight); this.flashlightGroup.add(this.bulbLight);
} }
@@ -271,7 +273,7 @@ export class Player {
this.flashlight.angle = Math.max(0.1, this.flashlight.angle - angleSpeed); this.flashlight.angle = Math.max(0.1, this.flashlight.angle - angleSpeed);
} }
if (this.adjustBright) { 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); this.flashlight.angle = Math.min(Math.PI / 2, this.flashlight.angle + angleSpeed);
} }
// Sync bulb light // Sync bulb light

View File

@@ -4,34 +4,54 @@ export class World {
constructor(scene) { constructor(scene) {
this.scene = scene; this.scene = scene;
this.colliders = []; this.colliders = [];
this.staticObstacles = []; // Track pillars etc for spawning
this.dustParticles = null; this.dustParticles = null;
} }
load() { 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 // Standard lighting for horror
const ambientLight = new THREE.AmbientLight(0x404040, 0.2); // Very dim ambient const ambientLight = new THREE.AmbientLight(0x404040, 0.2); // Very dim ambient
this.scene.add(ambientLight); this.scene.add(ambientLight);
// Floor // Floor (Dirty Concrete)
const floorGeo = new THREE.PlaneGeometry(30, 30); const floorGeo = new THREE.PlaneGeometry(60, 60);
const floorMat = new THREE.MeshStandardMaterial({ const floorMat = new THREE.MeshStandardMaterial({
color: 0x222222, map: this.texConcrete,
roughness: 0.9 roughness: 0.9,
metalness: 0.1
}); });
const floor = new THREE.Mesh(floorGeo, floorMat); const floor = new THREE.Mesh(floorGeo, floorMat);
floor.rotation.x = -Math.PI / 2; floor.rotation.x = -Math.PI / 2;
this.scene.add(floor); this.scene.add(floor);
// Simple walls // Ceiling (Dirty Concrete, Darker)
this.createWall(0, 2.5, -15, 30, 5); // Back const ceilingGeo = new THREE.PlaneGeometry(60, 60);
this.createWall(0, 2.5, 15, 30, 5); // Front const ceilingMat = new THREE.MeshStandardMaterial({
this.createWall(-15, 2.5, 0, 30, 5, true); // Left map: this.texConcrete,
this.createWall(15, 2.5, 0, 30, 5, true); // Right 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 // Simple walls (Expanded to 60x60 with Moldy Plaster)
for (let i = 0; i < 8; i++) { this.createWall(0, 2.5, -30, 60, 5); // Back
const px = (Math.random() - 0.5) * 20; this.createWall(0, 2.5, 30, 60, 5); // Front
const pz = (Math.random() - 0.5) * 20; 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); this.createPillar(px, 2.5, pz);
} }
@@ -44,9 +64,397 @@ export class World {
this.scene.add(target); this.scene.add(target);
this.colliders.push(target); this.colliders.push(target);
this.createFurniture(45);
this.createDust(); 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() { createDust() {
// Create 800 dust particles (Reduced) // Create 800 dust particles (Reduced)
const count = 800; const count = 800;
@@ -56,9 +464,9 @@ export class World {
const velocities = []; const velocities = [];
for (let i = 0; i < count; i++) { 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 + 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] = 0; // Start invisible (black)
colors[i * 3 + 1] = 0; colors[i * 3 + 1] = 0;
@@ -88,24 +496,6 @@ export class World {
this.scene.add(this.dustParticles); 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) { update(dt, player) {
if (!this.dustParticles) return; if (!this.dustParticles) return;