131 lines
4.9 KiB
JavaScript
131 lines
4.9 KiB
JavaScript
import * as THREE from 'three';
|
|
import { state } from '../state.js';
|
|
|
|
const SPIDER_COUNT = 5;
|
|
const SPIDER_SPEED = 0.0001;
|
|
const SPIDER_TURN_SPEED = 0.02;
|
|
const SPIDER_WAIT_MIN = 200; // frames
|
|
const SPIDER_WAIT_MAX = 500; // frames
|
|
|
|
export class SpiderEffect {
|
|
constructor(scene) {
|
|
this.spiders = [];
|
|
this._setupSpiders(scene);
|
|
}
|
|
|
|
_getRandomPointOnWall(wall) {
|
|
const position = new THREE.Vector3();
|
|
const width = wall.geometry.parameters.width;
|
|
const height = wall.geometry.parameters.height;
|
|
|
|
position.x = (Math.random() - 0.5) * width;
|
|
position.y = (Math.random() - 0.5) * height;
|
|
position.z = 0; // Local z is 0 for a plane
|
|
|
|
// Convert local position to world position
|
|
return wall.localToWorld(position);
|
|
}
|
|
|
|
_createSpiderMesh() {
|
|
const spiderGroup = new THREE.Group();
|
|
const spiderMaterial = new THREE.MeshPhongMaterial({ color: 0x919191, shininess: 50 });
|
|
|
|
// Body
|
|
const bodyGeometry = new THREE.SphereGeometry(0.01, 6, 5);
|
|
const body = new THREE.Mesh(bodyGeometry, spiderMaterial);
|
|
body.scale.z = 0.6; // Flatten the sphere
|
|
body.castShadow = true;
|
|
spiderGroup.add(body);
|
|
|
|
// Head
|
|
const headGeometry = new THREE.SphereGeometry(0.005, 5, 4);
|
|
const head = new THREE.Mesh(headGeometry, spiderMaterial);
|
|
head.position.y = 0.015;
|
|
head.castShadow = true;
|
|
spiderGroup.add(head);
|
|
|
|
spiderGroup.userData = {
|
|
state: 'crawling', // 'crawling', 'waiting'
|
|
waitTimer: 0,
|
|
t: 0,
|
|
curve: null,
|
|
currentWall: null,
|
|
};
|
|
|
|
return spiderGroup;
|
|
}
|
|
|
|
_findNewTarget(spider) {
|
|
if (!spider.userData.currentWall) {
|
|
// First time, pick a random wall
|
|
const walls = state.crawlSurfaces;
|
|
if (walls.length === 0) return;
|
|
spider.userData.currentWall = walls[Math.floor(Math.random() * walls.length)];
|
|
spider.position.copy(this._getRandomPointOnWall(spider.userData.currentWall));
|
|
}
|
|
|
|
const startPoint = spider.position.clone();
|
|
const endPoint = this._getRandomPointOnWall(spider.userData.currentWall);
|
|
|
|
// Create a curved path on the wall
|
|
const midPoint = new THREE.Vector3().lerpVectors(startPoint, endPoint, 0.5);
|
|
const direction = new THREE.Vector3().subVectors(endPoint, startPoint).normalize();
|
|
const wallNormal = spider.userData.currentWall.getWorldDirection(new THREE.Vector3()).negate();
|
|
|
|
// Get a perpendicular vector on the plane of the wall
|
|
const perpendicular = new THREE.Vector3().crossVectors(direction, wallNormal).normalize();
|
|
const offsetMagnitude = startPoint.distanceTo(endPoint) * (Math.random() * 0.4 - 0.2); // Random offset left or right
|
|
|
|
const controlPoint = midPoint.clone().add(perpendicular.multiplyScalar(offsetMagnitude));
|
|
|
|
spider.userData.curve = new THREE.QuadraticBezierCurve3(startPoint, controlPoint, endPoint);
|
|
spider.userData.t = 0;
|
|
spider.userData.state = 'crawling';
|
|
}
|
|
|
|
_setupSpiders(scene) {
|
|
for (let i = 0; i < SPIDER_COUNT; i++) {
|
|
const spider = this._createSpiderMesh();
|
|
scene.add(spider);
|
|
this.spiders.push(spider);
|
|
this._findNewTarget(spider); // Initial placement
|
|
}
|
|
}
|
|
|
|
update() {
|
|
this.spiders.forEach(spider => {
|
|
const data = spider.userData;
|
|
|
|
if (data.state === 'crawling') {
|
|
if (!data.curve) {
|
|
this._findNewTarget(spider);
|
|
return;
|
|
}
|
|
|
|
data.t += SPIDER_SPEED;
|
|
if (data.t >= 1) {
|
|
spider.position.copy(data.curve.v2);
|
|
data.state = 'waiting';
|
|
data.waitTimer = SPIDER_WAIT_MIN + Math.random() * (SPIDER_WAIT_MAX - SPIDER_WAIT_MIN);
|
|
} else {
|
|
spider.position.copy(data.curve.getPoint(data.t));
|
|
|
|
// Smoothly turn the spider towards the tangent of the curve
|
|
const tangent = data.curve.getTangent(data.t);
|
|
const up = data.currentWall.getWorldDirection(new THREE.Vector3()).negate();
|
|
|
|
const targetQuaternion = new THREE.Quaternion().setFromUnitVectors(new THREE.Vector3(0, 1, 0), tangent).multiply(
|
|
new THREE.Quaternion().setFromUnitVectors(new THREE.Vector3(0, 0, 1), up)
|
|
);
|
|
|
|
spider.quaternion.slerp(targetQuaternion, SPIDER_TURN_SPEED);
|
|
}
|
|
} else if (data.state === 'waiting') {
|
|
data.waitTimer--;
|
|
if (data.waitTimer <= 0) {
|
|
this._findNewTarget(spider);
|
|
}
|
|
}
|
|
});
|
|
}
|
|
} |