140 lines
5.1 KiB
JavaScript
140 lines
5.1 KiB
JavaScript
import * as THREE from 'three';
|
|
import { state } from '../state.js';
|
|
import { degToRad } from '../utils.js';
|
|
|
|
const FLIES_COUNT = 2;
|
|
|
|
// --- Configuration ---
|
|
const FLIGHT_HEIGHT_MIN = 0.5; // Min height for flying
|
|
const FLIGHT_HEIGHT_MAX = 2;//state.roomHeight * 0.9; // Max height for flying
|
|
const FLY_FLIGHT_SPEED_FACTOR = 0.01; // How quickly 't' increases per frame
|
|
const FLY_WAIT_BASE = 1000;
|
|
const FLY_LAND_CHANCE = 0.3;
|
|
|
|
export class FliesEffect {
|
|
constructor(scene) {
|
|
this.flies = [];
|
|
this._setupFlies(scene);
|
|
}
|
|
|
|
_randomFlyTarget() {
|
|
return new THREE.Vector3(
|
|
(Math.random() - 0.5) * (state.roomSize - 1),
|
|
FLIGHT_HEIGHT_MIN + Math.random() * (FLIGHT_HEIGHT_MAX - FLIGHT_HEIGHT_MIN),
|
|
(Math.random() - 0.5) * (state.roomSize - 1)
|
|
);
|
|
}
|
|
|
|
_createFlyMesh() {
|
|
const flyGroup = new THREE.Group();
|
|
const flyMaterial = new THREE.MeshPhongMaterial({ color: 0x111111, shininess: 50 });
|
|
const bodyGeometry = new THREE.ConeGeometry(0.01, 0.02, 3);
|
|
const body = new THREE.Mesh(bodyGeometry, flyMaterial);
|
|
body.rotation.x = degToRad(90);
|
|
body.castShadow = true;
|
|
body.receiveShadow = true;
|
|
flyGroup.add(body);
|
|
|
|
flyGroup.userData = {
|
|
state: 'flying',
|
|
landTimer: 0,
|
|
t: 0,
|
|
speed: FLY_FLIGHT_SPEED_FACTOR + Math.random() * 0.01,
|
|
curve: null,
|
|
landCheckTimer: 0,
|
|
oscillationTime: Math.random() * 100,
|
|
};
|
|
|
|
flyGroup.position.copy(this._randomFlyTarget());
|
|
return flyGroup;
|
|
}
|
|
|
|
_createFlyCurve(fly, endPoint) {
|
|
const startPoint = fly.position.clone();
|
|
const midPoint = new THREE.Vector3().lerpVectors(startPoint, endPoint, 0.5);
|
|
const offsetMagnitude = startPoint.distanceTo(endPoint) * 0.5;
|
|
const offsetAngle = Math.random() * Math.PI * 2;
|
|
|
|
const controlPoint = new THREE.Vector3(
|
|
midPoint.x + Math.cos(offsetAngle) * offsetMagnitude * 0.5,
|
|
midPoint.y + Math.random() * 0.5 + 0.5,
|
|
midPoint.z + Math.sin(offsetAngle) * offsetMagnitude * 0.5
|
|
);
|
|
|
|
fly.userData.curve = new THREE.QuadraticBezierCurve3(startPoint, controlPoint, endPoint);
|
|
fly.userData.t = 0;
|
|
fly.userData.landCheckTimer = 50 + Math.random() * 50;
|
|
}
|
|
|
|
_setupFlies(scene) {
|
|
for (let i = 0; i < FLIES_COUNT; i++) {
|
|
const fly = this._createFlyMesh();
|
|
scene.add(fly);
|
|
this.flies.push(fly);
|
|
}
|
|
}
|
|
|
|
update() {
|
|
this.flies.forEach(fly => {
|
|
const data = fly.userData;
|
|
|
|
if (data.state === 'flying' || data.state === 'landing') {
|
|
if (!data.curve) {
|
|
const newTargetPos = this._randomFlyTarget();
|
|
this._createFlyCurve(fly, newTargetPos);
|
|
data.t = 0;
|
|
}
|
|
|
|
data.t += data.speed;
|
|
data.landCheckTimer--;
|
|
|
|
if (data.t >= 1) {
|
|
if (data.state === 'landing') {
|
|
data.state = 'landed';
|
|
data.landTimer = FLY_WAIT_BASE + Math.random() * 1000;
|
|
data.t = 0;
|
|
return;
|
|
}
|
|
|
|
if (data.landCheckTimer <= 0 && Math.random() > FLY_LAND_CHANCE) {
|
|
state.raycaster.set(fly.position, new THREE.Vector3(0, -1, 0));
|
|
const intersects = state.raycaster.intersectObjects(state.landingSurfaces, false);
|
|
|
|
if (intersects.length > 0) {
|
|
const intersect = intersects[0];
|
|
data.state = 'landing';
|
|
let newTargetPos = new THREE.Vector3(
|
|
intersect.point.x,
|
|
intersect.point.y + 0.05,
|
|
intersect.point.z
|
|
);
|
|
this._createFlyCurve(fly, newTargetPos);
|
|
data.t = 0;
|
|
}
|
|
}
|
|
|
|
if (data.state !== 'landing') {
|
|
const newTargetPos = this._randomFlyTarget();
|
|
this._createFlyCurve(fly, newTargetPos);
|
|
data.t = 0;
|
|
}
|
|
}
|
|
|
|
fly.position.copy(data.curve.getPoint(Math.min(data.t, 1)));
|
|
const tangent = data.curve.getTangent(Math.min(data.t, 1)).normalize();
|
|
fly.rotation.y = Math.atan2(tangent.x, tangent.z);
|
|
data.oscillationTime += 0.1;
|
|
fly.position.y += Math.sin(data.oscillationTime * 4) * 0.01;
|
|
|
|
} else if (data.state === 'landed') {
|
|
data.landTimer--;
|
|
if (data.landTimer <= 0) {
|
|
data.state = 'flying';
|
|
const newTargetPos = this._randomFlyTarget();
|
|
this._createFlyCurve(fly, newTargetPos);
|
|
data.t = 0;
|
|
}
|
|
}
|
|
});
|
|
}
|
|
} |