stabilize dead air camera reconciliation
All checks were successful
build / image (push) Successful in 27s
All checks were successful
build / image (push) Successful in 27s
This commit is contained in:
@@ -172,7 +172,7 @@ function updateRuntime(
|
|||||||
const local = world.players.find((player) => player.id === playerId);
|
const local = world.players.find((player) => player.id === playerId);
|
||||||
const fractionalSeconds = frame.interpolationAlpha / DEAD_AIR_TICK_RATE;
|
const fractionalSeconds = frame.interpolationAlpha / DEAD_AIR_TICK_RATE;
|
||||||
if (local) {
|
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(
|
runtime.camera.position.set(
|
||||||
local.x + local.velocityX * fractionalSeconds + frame.localCorrection.x * correctionLife,
|
local.x + local.velocityX * fractionalSeconds + frame.localCorrection.x * correctionLife,
|
||||||
1.62,
|
1.62,
|
||||||
|
|||||||
@@ -203,11 +203,36 @@ export function useDeadAirClient(): DeadAirClientView {
|
|||||||
const localAfter = (engine.currentState as DeadAirClientState).players.find(
|
const localAfter = (engine.currentState as DeadAirClientState).players.find(
|
||||||
(player) => player.id === engine.localPlayerId,
|
(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;
|
const correction = renderSource.current.localCorrection;
|
||||||
correction.x += localBefore.x - localAfter.x;
|
const decay = Math.exp(-(now - correction.updatedAt) / 115);
|
||||||
correction.z += localBefore.z - localAfter.z;
|
const leadSeconds = clock.interpolationAlpha / deadAirGame.tickRateHz;
|
||||||
correction.updatedAt = performance.now();
|
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;
|
break;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -67,6 +67,20 @@ const baseDeadAirGame = defineMultiplayerGame<DeadAirGameContract>({
|
|||||||
applyInput(state, input, { playerId, emit }) {
|
applyInput(state, input, { playerId, emit }) {
|
||||||
const player = findPlayer(state, playerId);
|
const player = findPlayer(state, playerId);
|
||||||
if (!player || player.bot || !player.alive || state.resetTicks > 0) return;
|
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);
|
applyCommand(player, input);
|
||||||
if (input.reload) beginReload(player);
|
if (input.reload) beginReload(player);
|
||||||
if (input.toggleFlashlight) player.flashlight = !player.flashlight;
|
if (input.toggleFlashlight) player.flashlight = !player.flashlight;
|
||||||
@@ -370,6 +384,7 @@ function createPlayer(id: number, bot: boolean, round: number): DeadAirAuthority
|
|||||||
return {
|
return {
|
||||||
id,
|
id,
|
||||||
bot,
|
bot,
|
||||||
|
engaged: bot,
|
||||||
alive: true,
|
alive: true,
|
||||||
x: spawn.x,
|
x: spawn.x,
|
||||||
z: spawn.z,
|
z: spawn.z,
|
||||||
@@ -517,7 +532,12 @@ function fire(
|
|||||||
let hit: DeadAirAuthorityPlayer | null = null;
|
let hit: DeadAirAuthorityPlayer | null = null;
|
||||||
let hitDistance = shotRange;
|
let hitDistance = shotRange;
|
||||||
for (const target of state.players) {
|
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 offsetX = target.x - shooter.x;
|
||||||
const offsetZ = target.z - shooter.z;
|
const offsetZ = target.z - shooter.z;
|
||||||
const forwardDistance = offsetX * directionX + offsetZ * directionZ;
|
const forwardDistance = offsetX * directionX + offsetZ * directionZ;
|
||||||
@@ -673,7 +693,7 @@ function updateWarden(
|
|||||||
tick: number,
|
tick: number,
|
||||||
): void {
|
): void {
|
||||||
const visibleTargets = state.players.filter(
|
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(
|
visibleTargets.sort(
|
||||||
(left, right) =>
|
(left, right) =>
|
||||||
|
|||||||
@@ -31,6 +31,8 @@ export interface DeadAirBotState {
|
|||||||
export interface DeadAirAuthorityPlayer {
|
export interface DeadAirAuthorityPlayer {
|
||||||
id: number;
|
id: number;
|
||||||
bot: boolean;
|
bot: boolean;
|
||||||
|
/** Humans remain untargetable until they produce deliberate play input. */
|
||||||
|
engaged: boolean;
|
||||||
alive: boolean;
|
alive: boolean;
|
||||||
x: number;
|
x: number;
|
||||||
z: number;
|
z: number;
|
||||||
|
|||||||
Reference in New Issue
Block a user