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 ---
|
// --- Animation Loop ---
|
||||||
export function animate() {
|
export function animate() {
|
||||||
requestAnimationFrame(animate);
|
requestAnimationFrame(animate);
|
||||||
@ -98,6 +157,7 @@ export function animate() {
|
|||||||
updateScreenLight();
|
updateScreenLight();
|
||||||
updateVideo();
|
updateVideo();
|
||||||
updateVcr();
|
updateVcr();
|
||||||
|
updateBooks();
|
||||||
updateDoor();
|
updateDoor();
|
||||||
|
|
||||||
// RENDER!
|
// RENDER!
|
||||||
|
|||||||
@ -102,8 +102,16 @@ export function createBookshelf(x, z, rotationY, uniqueSeed) {
|
|||||||
|
|
||||||
book.castShadow = true;
|
book.castShadow = true;
|
||||||
book.receiveShadow = 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);
|
shelfGroup.add(book);
|
||||||
|
|
||||||
|
if (Math.random() > 0.8) {
|
||||||
|
state.books.push(book);
|
||||||
|
}
|
||||||
|
|
||||||
currentBookX += bookWidth + 0.002; // Tiny gap between books
|
currentBookX += bookWidth + 0.002; // Tiny gap between books
|
||||||
|
|
||||||
if (seededRandom() > 0.92) {
|
if (seededRandom() > 0.92) {
|
||||||
|
|||||||
@ -44,6 +44,11 @@ export function initState() {
|
|||||||
// Utilities
|
// Utilities
|
||||||
loader: new THREE.TextureLoader(),
|
loader: new THREE.TextureLoader(),
|
||||||
landingSurfaces: [],
|
landingSurfaces: [],
|
||||||
|
bookLevitation: {
|
||||||
|
state: 'resting', // 'resting', 'levitating', 'returning'
|
||||||
|
timer: 0,
|
||||||
|
},
|
||||||
|
books: [], // Array to hold all individual book meshes for animation
|
||||||
raycaster: new THREE.Raycaster(),
|
raycaster: new THREE.Raycaster(),
|
||||||
seed: 12345,
|
seed: 12345,
|
||||||
};
|
};
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user