pages235/renderer/viewer/lib/cameraBobbing.ts
Vitaly 65af9a73c2
feat: rework hand! enable by default, fix bow anim (#261)
* refactor swing animation to controller

* idle animator!!!!

* implelment state switch transition

* a huge fix for UI server edit!

* adjust ui scaling so main menu elements clip less

* view bobbing, new config name, ws:

* EXTREMELY important fixes to entities rendering

* a lot of fixes, add dns resolve fallback

* improve f3 E, fix modal not found edge case

* set correctly target for old browsers, should fix ios 14 crash

* unecessary big refactor, to fix ts err

* fix isWysiwyg check

* fix entities rendering count
2025-02-15 05:14:36 +03:00

94 lines
3.1 KiB
TypeScript

export class CameraBobbing {
private walkDistance = 0
private prevWalkDistance = 0
private bobAmount = 0
private prevBobAmount = 0
private readonly gameTimer = new GameTimer()
// eslint-disable-next-line max-params
constructor (
private readonly BOB_FREQUENCY: number = Math.PI, // How fast the bob cycles
private readonly BOB_BASE_AMPLITUDE: number = 0.5, // Base amplitude of the bob
private readonly VERTICAL_MULTIPLIER: number = 1, // Vertical movement multiplier
private readonly ROTATION_MULTIPLIER_Z: number = 3, // Roll rotation multiplier
private readonly ROTATION_MULTIPLIER_X: number = 5 // Pitch rotation multiplier
) {}
// Call this when player is moving
public updateWalkDistance (distance: number): void {
this.prevWalkDistance = this.walkDistance
this.walkDistance = distance
}
// Call this when player is moving to update bob amount
public updateBobAmount (isMoving: boolean): void {
const targetBob = isMoving ? 1 : 0
this.prevBobAmount = this.bobAmount
// Update timing
const ticks = this.gameTimer.update()
const deltaTime = ticks / 20 // Convert ticks to seconds assuming 20 TPS
// Smooth transition for bob amount
const bobDelta = (targetBob - this.bobAmount) * Math.min(1, deltaTime * 10)
this.bobAmount += bobDelta
}
// Call this in your render/animation loop
public getBobbing (): { position: { x: number, y: number }, rotation: { x: number, z: number } } {
// Interpolate walk distance
const walkDist = this.prevWalkDistance +
(this.walkDistance - this.prevWalkDistance) * this.gameTimer.partialTick
// Interpolate bob amount
const bob = this.prevBobAmount +
(this.bobAmount - this.prevBobAmount) * this.gameTimer.partialTick
// Calculate total distance for bob cycle
const totalDist = -(walkDist * this.BOB_FREQUENCY)
// Calculate offsets
const xOffset = Math.sin(totalDist) * bob * this.BOB_BASE_AMPLITUDE
const yOffset = -Math.abs(Math.cos(totalDist) * bob) * this.VERTICAL_MULTIPLIER
// Calculate rotations (in radians)
const zRot = (Math.sin(totalDist) * bob * this.ROTATION_MULTIPLIER_Z) * (Math.PI / 180)
const xRot = (Math.abs(Math.cos(totalDist - 0.2) * bob) * this.ROTATION_MULTIPLIER_X) * (Math.PI / 180)
return {
position: { x: xOffset, y: yOffset },
rotation: { x: xRot, z: zRot }
}
}
}
class GameTimer {
private readonly msPerTick: number
private lastMs: number
public partialTick = 0
constructor (tickRate = 20) {
this.msPerTick = 1000 / tickRate
this.lastMs = performance.now()
}
update (): number {
const currentMs = performance.now()
const deltaSinceLastTick = currentMs - this.lastMs
// Calculate how much of a tick has passed
const tickDelta = deltaSinceLastTick / this.msPerTick
this.lastMs = currentMs
// Add to accumulated partial ticks
this.partialTick += tickDelta
// Get whole number of ticks that should occur
const wholeTicks = Math.floor(this.partialTick)
// Keep the remainder as the new partial tick
this.partialTick -= wholeTicks
return wholeTicks
}
}