Feature: books start levitating
This commit is contained in:
parent
6e2a59ce77
commit
2e1e6bddfa
@ -88,6 +88,65 @@ function updateVcr() {
|
||||
}
|
||||
}
|
||||
|
||||
function updateBooks() {
|
||||
const LEVITATE_CHANCE = 0.001; // Chance for a resting book to start levitating per frame
|
||||
const LEVITATE_DURATION_MIN = 100; // frames
|
||||
const LEVITATE_DURATION_MAX = 300; // frames
|
||||
const LEVITATE_AMPLITUDE = 0.02; // Max vertical displacement
|
||||
const LEVITATE_SPEED_FACTOR = 0.03; // Speed of oscillation
|
||||
const START_RATE = 0.05; // How quickly a book starts to levitate
|
||||
const RETURN_RATE = 0.1; // How quickly a book returns to original position
|
||||
const START_DURATION = 120; // frames for the starting transition
|
||||
const levitation = state.bookLevitation;
|
||||
|
||||
// Manage the global levitation state
|
||||
if (levitation.state === 'resting') {
|
||||
if (Math.random() < LEVITATE_CHANCE) {
|
||||
levitation.state = 'starting';
|
||||
levitation.timer = START_DURATION;
|
||||
}
|
||||
} else if (levitation.state === 'starting') {
|
||||
levitation.timer--;
|
||||
if (levitation.timer <= 0) {
|
||||
levitation.state = 'levitating';
|
||||
levitation.timer = LEVITATE_DURATION_MIN + Math.random() * (LEVITATE_DURATION_MAX - LEVITATE_DURATION_MIN);
|
||||
}
|
||||
} else if (levitation.state === 'levitating') {
|
||||
levitation.timer--;
|
||||
if (levitation.timer <= 0) {
|
||||
levitation.state = 'returning';
|
||||
}
|
||||
}
|
||||
|
||||
// Animate books based on the global state
|
||||
let allBooksReturned = true;
|
||||
state.books.forEach(book => {
|
||||
const data = book.userData;
|
||||
|
||||
if (levitation.state === 'starting') {
|
||||
allBooksReturned = false;
|
||||
book.position.y = THREE.MathUtils.lerp(book.position.y, data.originalY + LEVITATE_AMPLITUDE/2, START_RATE);
|
||||
data.oscillationTime = 0;
|
||||
} else if (levitation.state === 'levitating') {
|
||||
allBooksReturned = false;
|
||||
data.oscillationTime += LEVITATE_SPEED_FACTOR;
|
||||
data.levitateOffset = Math.sin(data.oscillationTime) * LEVITATE_AMPLITUDE;
|
||||
book.position.y = data.originalY + data.levitateOffset + LEVITATE_AMPLITUDE/2;
|
||||
} else if (levitation.state === 'returning') {
|
||||
book.position.y = THREE.MathUtils.lerp(book.position.y, data.originalY, RETURN_RATE);
|
||||
data.levitateOffset = book.position.y - data.originalY;
|
||||
|
||||
if (Math.abs(data.levitateOffset) > 0.001) {
|
||||
allBooksReturned = false;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if (levitation.state === 'returning' && allBooksReturned) {
|
||||
levitation.state = 'resting';
|
||||
}
|
||||
}
|
||||
|
||||
// --- Animation Loop ---
|
||||
export function animate() {
|
||||
requestAnimationFrame(animate);
|
||||
@ -98,6 +157,7 @@ export function animate() {
|
||||
updateScreenLight();
|
||||
updateVideo();
|
||||
updateVcr();
|
||||
updateBooks();
|
||||
updateDoor();
|
||||
|
||||
// RENDER!
|
||||
|
||||
@ -102,8 +102,16 @@ export function createBookshelf(x, z, rotationY, uniqueSeed) {
|
||||
|
||||
book.castShadow = true;
|
||||
book.receiveShadow = true;
|
||||
// Store original Y position and animation data
|
||||
book.userData.originalY = book.position.y;
|
||||
book.userData.levitateOffset = 0;
|
||||
book.userData.oscillationTime = Math.random() * Math.PI * 2; // Start at random phase
|
||||
shelfGroup.add(book);
|
||||
|
||||
if (Math.random() > 0.8) {
|
||||
state.books.push(book);
|
||||
}
|
||||
|
||||
currentBookX += bookWidth + 0.002; // Tiny gap between books
|
||||
|
||||
if (seededRandom() > 0.92) {
|
||||
|
||||
@ -44,6 +44,11 @@ export function initState() {
|
||||
// Utilities
|
||||
loader: new THREE.TextureLoader(),
|
||||
landingSurfaces: [],
|
||||
bookLevitation: {
|
||||
state: 'resting', // 'resting', 'levitating', 'returning'
|
||||
timer: 0,
|
||||
},
|
||||
books: [], // Array to hold all individual book meshes for animation
|
||||
raycaster: new THREE.Raycaster(),
|
||||
seed: 12345,
|
||||
};
|
||||
|
||||
Loading…
Reference in New Issue
Block a user