stabilize dead air camera reconciliation
All checks were successful
build / image (push) Successful in 27s

This commit is contained in:
Syncer Deploy
2026-08-31 13:21:31 -04:00
parent 6361ecbdd7
commit 6c345ba595
4 changed files with 54 additions and 7 deletions

View File

@@ -172,7 +172,7 @@ function updateRuntime(
const local = world.players.find((player) => player.id === playerId);
const fractionalSeconds = frame.interpolationAlpha / DEAD_AIR_TICK_RATE;
if (local) {
const correctionLife = Math.max(0, 1 - (time - frame.localCorrection.updatedAt) / 130);
const correctionLife = Math.exp(-(time - frame.localCorrection.updatedAt) / 115);
runtime.camera.position.set(
local.x + local.velocityX * fractionalSeconds + frame.localCorrection.x * correctionLife,
1.62,

View File

@@ -203,11 +203,36 @@ export function useDeadAirClient(): DeadAirClientView {
const localAfter = (engine.currentState as DeadAirClientState).players.find(
(player) => player.id === engine.localPlayerId,
);
if (localBefore && localAfter && decoded.snapshot.state.round === renderSource.current.state.round) {
if (
localBefore &&
localAfter &&
localBefore.alive === localAfter.alive &&
decoded.snapshot.state.round === renderSource.current.state.round
) {
const now = performance.now();
const correction = renderSource.current.localCorrection;
correction.x += localBefore.x - localAfter.x;
correction.z += localBefore.z - localAfter.z;
correction.updatedAt = performance.now();
const decay = Math.exp(-(now - correction.updatedAt) / 115);
const leadSeconds = clock.interpolationAlpha / deadAirGame.tickRateHz;
const deltaX =
localBefore.x + localBefore.velocityX * leadSeconds -
localAfter.x - localAfter.velocityX * leadSeconds;
const deltaZ =
localBefore.z + localBefore.velocityZ * leadSeconds -
localAfter.z - localAfter.velocityZ * leadSeconds;
if (Math.hypot(deltaX, deltaZ) < 3) {
correction.x = correction.x * decay + deltaX;
correction.z = correction.z * decay + deltaZ;
} else {
correction.x = 0;
correction.z = 0;
}
correction.updatedAt = now;
} else {
renderSource.current.localCorrection = {
x: 0,
z: 0,
updatedAt: performance.now(),
};
}
break;
}

View File

@@ -67,6 +67,20 @@ const baseDeadAirGame = defineMultiplayerGame<DeadAirGameContract>({
applyInput(state, input, { playerId, emit }) {
const player = findPlayer(state, playerId);
if (!player || player.bot || !player.alive || state.resetTicks > 0) return;
if (
Math.abs(input.forward) > 0.01 ||
Math.abs(input.strafe) > 0.01 ||
input.fire ||
input.sprint ||
input.reload ||
input.interact ||
input.toggleFlashlight ||
input.throwDecoy ||
angleDifference(input.yaw, player.yaw) > 0.003 ||
Math.abs(input.pitch - player.pitch) > 0.003
) {
player.engaged = true;
}
applyCommand(player, input);
if (input.reload) beginReload(player);
if (input.toggleFlashlight) player.flashlight = !player.flashlight;
@@ -370,6 +384,7 @@ function createPlayer(id: number, bot: boolean, round: number): DeadAirAuthority
return {
id,
bot,
engaged: bot,
alive: true,
x: spawn.x,
z: spawn.z,
@@ -517,7 +532,12 @@ function fire(
let hit: DeadAirAuthorityPlayer | null = null;
let hitDistance = shotRange;
for (const target of state.players) {
if (!target.alive || target.id === shooter.id || target.bot === shooter.bot) continue;
if (
!target.alive ||
target.id === shooter.id ||
target.bot === shooter.bot ||
(!target.bot && !target.engaged)
) continue;
const offsetX = target.x - shooter.x;
const offsetZ = target.z - shooter.z;
const forwardDistance = offsetX * directionX + offsetZ * directionZ;
@@ -673,7 +693,7 @@ function updateWarden(
tick: number,
): void {
const visibleTargets = state.players.filter(
(target) => !target.bot && target.alive && canSeePlayer(state, warden, target),
(target) => !target.bot && target.engaged && target.alive && canSeePlayer(state, warden, target),
);
visibleTargets.sort(
(left, right) =>

View File

@@ -31,6 +31,8 @@ export interface DeadAirBotState {
export interface DeadAirAuthorityPlayer {
id: number;
bot: boolean;
/** Humans remain untargetable until they produce deliberate play input. */
engaged: boolean;
alive: boolean;
x: number;
z: number;