feat: add fixed-point math and network sync, fix docs links (#440)
- feat(math): add Fixed32, FixedMath, FixedVector2 for deterministic calculations - feat(network): add FixedSnapshotBuffer and FixedClientPrediction for lockstep sync - docs: fix relative links in behavior-tree, blueprint, guide docs - docs: add missing sidebar items (cocos-editor, distributed) - docs: add scene-manager and persistent-entity Chinese translations
This commit is contained in:
@@ -32,6 +32,7 @@
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@esengine/ecs-framework": "workspace:*",
|
||||
"@esengine/ecs-framework-math": "workspace:*",
|
||||
"@esengine/blueprint": "workspace:*"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
@@ -42,6 +43,7 @@
|
||||
"devDependencies": {
|
||||
"@esengine/blueprint": "workspace:*",
|
||||
"@esengine/ecs-framework": "workspace:*",
|
||||
"@esengine/ecs-framework-math": "workspace:*",
|
||||
"@esengine/build-config": "workspace:*",
|
||||
"rimraf": "^5.0.5",
|
||||
"tsup": "^8.0.0",
|
||||
|
||||
@@ -138,6 +138,11 @@ export type {
|
||||
ComponentSyncEvent,
|
||||
ComponentSyncEventListener,
|
||||
ComponentSyncConfig,
|
||||
// Fixed-point sync types
|
||||
IFixedTransformStateRaw,
|
||||
IFixedTransformStateWithVelocityRaw,
|
||||
IFixedInterpolator,
|
||||
IFixedExtrapolator,
|
||||
} from './sync'
|
||||
|
||||
export {
|
||||
@@ -158,6 +163,15 @@ export {
|
||||
// Component sync
|
||||
ComponentSyncSystem,
|
||||
createComponentSyncSystem,
|
||||
// Fixed-point sync (Deterministic Lockstep)
|
||||
FixedTransformState,
|
||||
FixedTransformStateWithVelocity,
|
||||
createZeroFixedTransformState,
|
||||
createZeroFixedTransformStateWithVelocity,
|
||||
FixedTransformInterpolator,
|
||||
FixedHermiteTransformInterpolator,
|
||||
createFixedTransformInterpolator,
|
||||
createFixedHermiteTransformInterpolator,
|
||||
} from './sync'
|
||||
|
||||
// ============================================================================
|
||||
|
||||
@@ -0,0 +1,485 @@
|
||||
/**
|
||||
* @zh 定点数客户端预测
|
||||
* @en Fixed-point Client Prediction
|
||||
*
|
||||
* @zh 用于帧同步的确定性客户端预测和回滚
|
||||
* @en Deterministic client prediction and rollback for lockstep
|
||||
*/
|
||||
|
||||
import { Fixed32, FixedVector2 } from '@esengine/ecs-framework-math';
|
||||
|
||||
// =============================================================================
|
||||
// 定点数输入快照接口 | Fixed Input Snapshot Interface
|
||||
// =============================================================================
|
||||
|
||||
/**
|
||||
* @zh 定点数输入快照
|
||||
* @en Fixed-point input snapshot
|
||||
*/
|
||||
export interface IFixedInputSnapshot<TInput> {
|
||||
/**
|
||||
* @zh 输入帧号
|
||||
* @en Input frame number
|
||||
*/
|
||||
readonly frame: number;
|
||||
|
||||
/**
|
||||
* @zh 输入数据
|
||||
* @en Input data
|
||||
*/
|
||||
readonly input: TInput;
|
||||
}
|
||||
|
||||
/**
|
||||
* @zh 定点数预测状态
|
||||
* @en Fixed-point predicted state
|
||||
*/
|
||||
export interface IFixedPredictedState<TState> {
|
||||
/**
|
||||
* @zh 状态数据
|
||||
* @en State data
|
||||
*/
|
||||
readonly state: TState;
|
||||
|
||||
/**
|
||||
* @zh 对应的帧号
|
||||
* @en Corresponding frame number
|
||||
*/
|
||||
readonly frame: number;
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// 定点数预测器接口 | Fixed Predictor Interface
|
||||
// =============================================================================
|
||||
|
||||
/**
|
||||
* @zh 定点数状态预测器接口
|
||||
* @en Fixed-point state predictor interface
|
||||
*
|
||||
* @zh 必须使用定点数运算确保确定性
|
||||
* @en Must use fixed-point arithmetic to ensure determinism
|
||||
*/
|
||||
export interface IFixedPredictor<TState, TInput> {
|
||||
/**
|
||||
* @zh 根据当前状态和输入预测下一状态
|
||||
* @en Predict next state based on current state and input
|
||||
*
|
||||
* @param state - @zh 当前状态 @en Current state
|
||||
* @param input - @zh 输入 @en Input
|
||||
* @param deltaTime - @zh 固定时间步长(定点数)@en Fixed delta time (fixed-point)
|
||||
* @returns @zh 预测的状态 @en Predicted state
|
||||
*/
|
||||
predict(state: TState, input: TInput, deltaTime: Fixed32): TState;
|
||||
}
|
||||
|
||||
/**
|
||||
* @zh 状态位置提取器接口
|
||||
* @en State position extractor interface
|
||||
*/
|
||||
export interface IFixedStatePositionExtractor<TState> {
|
||||
/**
|
||||
* @zh 从状态中提取位置
|
||||
* @en Extract position from state
|
||||
*/
|
||||
getPosition(state: TState): FixedVector2;
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// 定点数客户端预测配置 | Fixed Client Prediction Config
|
||||
// =============================================================================
|
||||
|
||||
/**
|
||||
* @zh 定点数客户端预测配置
|
||||
* @en Fixed-point client prediction configuration
|
||||
*/
|
||||
export interface FixedClientPredictionConfig {
|
||||
/**
|
||||
* @zh 最大未确认输入数量
|
||||
* @en Maximum unacknowledged inputs
|
||||
*/
|
||||
maxUnacknowledgedInputs: number;
|
||||
|
||||
/**
|
||||
* @zh 固定时间步长(定点数)
|
||||
* @en Fixed delta time (fixed-point)
|
||||
*/
|
||||
fixedDeltaTime: Fixed32;
|
||||
|
||||
/**
|
||||
* @zh 校正阈值(定点数,超过此值才进行校正)
|
||||
* @en Reconciliation threshold (fixed-point, correction only above this value)
|
||||
*/
|
||||
reconciliationThreshold: Fixed32;
|
||||
|
||||
/**
|
||||
* @zh 是否启用平滑校正(帧同步通常关闭)
|
||||
* @en Enable smooth reconciliation (usually disabled for lockstep)
|
||||
*/
|
||||
enableSmoothReconciliation: boolean;
|
||||
|
||||
/**
|
||||
* @zh 平滑校正速度(定点数)
|
||||
* @en Smooth reconciliation speed (fixed-point)
|
||||
*/
|
||||
reconciliationSpeed: Fixed32;
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// 定点数客户端预测管理器 | Fixed Client Prediction Manager
|
||||
// =============================================================================
|
||||
|
||||
/**
|
||||
* @zh 定点数客户端预测管理器
|
||||
* @en Fixed-point client prediction manager
|
||||
*
|
||||
* @zh 提供确定性的客户端预测和服务器状态回滚校正
|
||||
* @en Provides deterministic client prediction and server state rollback reconciliation
|
||||
*/
|
||||
export class FixedClientPrediction<TState, TInput> {
|
||||
private readonly _predictor: IFixedPredictor<TState, TInput>;
|
||||
private readonly _config: FixedClientPredictionConfig;
|
||||
private readonly _pendingInputs: IFixedInputSnapshot<TInput>[] = [];
|
||||
private _lastAcknowledgedFrame: number = 0;
|
||||
private _currentFrame: number = 0;
|
||||
private _lastServerState: TState | null = null;
|
||||
private _predictedState: TState | null = null;
|
||||
private _correctionOffset: FixedVector2 = FixedVector2.ZERO;
|
||||
private _stateHistory: Map<number, TState> = new Map();
|
||||
private readonly _maxHistorySize: number = 120;
|
||||
|
||||
constructor(
|
||||
predictor: IFixedPredictor<TState, TInput>,
|
||||
config?: Partial<FixedClientPredictionConfig>
|
||||
) {
|
||||
this._predictor = predictor;
|
||||
this._config = {
|
||||
maxUnacknowledgedInputs: 60,
|
||||
fixedDeltaTime: Fixed32.from(1 / 60),
|
||||
reconciliationThreshold: Fixed32.from(0.001),
|
||||
enableSmoothReconciliation: false,
|
||||
reconciliationSpeed: Fixed32.from(10),
|
||||
...config
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @zh 获取当前预测状态
|
||||
* @en Get current predicted state
|
||||
*/
|
||||
get predictedState(): TState | null {
|
||||
return this._predictedState;
|
||||
}
|
||||
|
||||
/**
|
||||
* @zh 获取校正偏移(用于渲染平滑)
|
||||
* @en Get correction offset (for render smoothing)
|
||||
*/
|
||||
get correctionOffset(): FixedVector2 {
|
||||
return this._correctionOffset;
|
||||
}
|
||||
|
||||
/**
|
||||
* @zh 获取待确认输入数量
|
||||
* @en Get pending input count
|
||||
*/
|
||||
get pendingInputCount(): number {
|
||||
return this._pendingInputs.length;
|
||||
}
|
||||
|
||||
/**
|
||||
* @zh 获取当前帧号
|
||||
* @en Get current frame number
|
||||
*/
|
||||
get currentFrame(): number {
|
||||
return this._currentFrame;
|
||||
}
|
||||
|
||||
/**
|
||||
* @zh 获取最后确认帧号
|
||||
* @en Get last acknowledged frame
|
||||
*/
|
||||
get lastAcknowledgedFrame(): number {
|
||||
return this._lastAcknowledgedFrame;
|
||||
}
|
||||
|
||||
/**
|
||||
* @zh 记录并预测输入
|
||||
* @en Record and predict input
|
||||
*
|
||||
* @param input - @zh 输入数据 @en Input data
|
||||
* @param currentState - @zh 当前状态 @en Current state
|
||||
* @returns @zh 预测的状态 @en Predicted state
|
||||
*/
|
||||
recordInput(input: TInput, currentState: TState): TState {
|
||||
this._currentFrame++;
|
||||
|
||||
const inputSnapshot: IFixedInputSnapshot<TInput> = {
|
||||
frame: this._currentFrame,
|
||||
input
|
||||
};
|
||||
|
||||
this._pendingInputs.push(inputSnapshot);
|
||||
|
||||
while (this._pendingInputs.length > this._config.maxUnacknowledgedInputs) {
|
||||
this._pendingInputs.shift();
|
||||
}
|
||||
|
||||
this._predictedState = this._predictor.predict(
|
||||
currentState,
|
||||
input,
|
||||
this._config.fixedDeltaTime
|
||||
);
|
||||
|
||||
this._stateHistory.set(this._currentFrame, this._predictedState);
|
||||
this._cleanupHistory();
|
||||
|
||||
return this._predictedState;
|
||||
}
|
||||
|
||||
/**
|
||||
* @zh 获取指定帧的输入
|
||||
* @en Get input at specific frame
|
||||
*/
|
||||
getInputAtFrame(frame: number): IFixedInputSnapshot<TInput> | null {
|
||||
return this._pendingInputs.find(i => i.frame === frame) ?? null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @zh 获取所有待确认输入
|
||||
* @en Get all pending inputs
|
||||
*/
|
||||
getPendingInputs(): readonly IFixedInputSnapshot<TInput>[] {
|
||||
return this._pendingInputs;
|
||||
}
|
||||
|
||||
/**
|
||||
* @zh 处理服务器状态并进行回滚校正
|
||||
* @en Process server state and perform rollback reconciliation
|
||||
*
|
||||
* @param serverState - @zh 服务器权威状态 @en Server authoritative state
|
||||
* @param serverFrame - @zh 服务器状态对应的帧号 @en Server state frame number
|
||||
* @param positionExtractor - @zh 状态位置提取器 @en State position extractor
|
||||
* @returns @zh 校正后的状态 @en Reconciled state
|
||||
*/
|
||||
reconcile(
|
||||
serverState: TState,
|
||||
serverFrame: number,
|
||||
positionExtractor: IFixedStatePositionExtractor<TState>
|
||||
): TState {
|
||||
this._lastServerState = serverState;
|
||||
this._lastAcknowledgedFrame = serverFrame;
|
||||
|
||||
while (this._pendingInputs.length > 0 && this._pendingInputs[0].frame <= serverFrame) {
|
||||
this._pendingInputs.shift();
|
||||
}
|
||||
|
||||
const localStateAtServerFrame = this._stateHistory.get(serverFrame);
|
||||
|
||||
if (localStateAtServerFrame) {
|
||||
const serverPos = positionExtractor.getPosition(serverState);
|
||||
const localPos = positionExtractor.getPosition(localStateAtServerFrame);
|
||||
const error = serverPos.sub(localPos);
|
||||
const errorMagnitude = error.length();
|
||||
|
||||
if (errorMagnitude.gt(this._config.reconciliationThreshold)) {
|
||||
if (this._config.enableSmoothReconciliation) {
|
||||
const t = Fixed32.min(
|
||||
Fixed32.ONE,
|
||||
this._config.reconciliationSpeed.mul(this._config.fixedDeltaTime)
|
||||
);
|
||||
this._correctionOffset = this._correctionOffset.add(error.mul(t));
|
||||
|
||||
const decayRate = Fixed32.from(0.9);
|
||||
this._correctionOffset = this._correctionOffset.mul(decayRate);
|
||||
} else {
|
||||
this._correctionOffset = FixedVector2.ZERO;
|
||||
}
|
||||
|
||||
let state = serverState;
|
||||
for (const inputSnapshot of this._pendingInputs) {
|
||||
state = this._predictor.predict(
|
||||
state,
|
||||
inputSnapshot.input,
|
||||
this._config.fixedDeltaTime
|
||||
);
|
||||
this._stateHistory.set(inputSnapshot.frame, state);
|
||||
}
|
||||
this._predictedState = state;
|
||||
|
||||
return state;
|
||||
}
|
||||
}
|
||||
|
||||
let state = serverState;
|
||||
for (const inputSnapshot of this._pendingInputs) {
|
||||
state = this._predictor.predict(
|
||||
state,
|
||||
inputSnapshot.input,
|
||||
this._config.fixedDeltaTime
|
||||
);
|
||||
}
|
||||
this._predictedState = state;
|
||||
|
||||
return state;
|
||||
}
|
||||
|
||||
/**
|
||||
* @zh 回滚到指定帧并重新模拟
|
||||
* @en Rollback to specific frame and re-simulate
|
||||
*
|
||||
* @param targetFrame - @zh 目标帧号 @en Target frame number
|
||||
* @param authoritativeState - @zh 权威状态 @en Authoritative state
|
||||
* @returns @zh 重新模拟后的当前状态 @en Re-simulated current state
|
||||
*/
|
||||
rollbackAndResimulate(targetFrame: number, authoritativeState: TState): TState {
|
||||
this._stateHistory.set(targetFrame, authoritativeState);
|
||||
|
||||
let state = authoritativeState;
|
||||
const inputsToResimulate = this._pendingInputs.filter(i => i.frame > targetFrame);
|
||||
|
||||
for (const inputSnapshot of inputsToResimulate) {
|
||||
state = this._predictor.predict(
|
||||
state,
|
||||
inputSnapshot.input,
|
||||
this._config.fixedDeltaTime
|
||||
);
|
||||
this._stateHistory.set(inputSnapshot.frame, state);
|
||||
}
|
||||
|
||||
this._predictedState = state;
|
||||
return state;
|
||||
}
|
||||
|
||||
/**
|
||||
* @zh 获取历史状态
|
||||
* @en Get historical state
|
||||
*/
|
||||
getStateAtFrame(frame: number): TState | null {
|
||||
return this._stateHistory.get(frame) ?? null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @zh 清空预测状态
|
||||
* @en Clear prediction state
|
||||
*/
|
||||
clear(): void {
|
||||
this._pendingInputs.length = 0;
|
||||
this._lastAcknowledgedFrame = 0;
|
||||
this._currentFrame = 0;
|
||||
this._lastServerState = null;
|
||||
this._predictedState = null;
|
||||
this._correctionOffset = FixedVector2.ZERO;
|
||||
this._stateHistory.clear();
|
||||
}
|
||||
|
||||
private _cleanupHistory(): void {
|
||||
if (this._stateHistory.size > this._maxHistorySize) {
|
||||
const sortedFrames = Array.from(this._stateHistory.keys()).sort((a, b) => a - b);
|
||||
const framesToRemove = sortedFrames.slice(
|
||||
0,
|
||||
this._stateHistory.size - this._maxHistorySize
|
||||
);
|
||||
for (const frame of framesToRemove) {
|
||||
this._stateHistory.delete(frame);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// 工厂函数 | Factory Functions
|
||||
// =============================================================================
|
||||
|
||||
/**
|
||||
* @zh 创建定点数客户端预测管理器
|
||||
* @en Create fixed-point client prediction manager
|
||||
*/
|
||||
export function createFixedClientPrediction<TState, TInput>(
|
||||
predictor: IFixedPredictor<TState, TInput>,
|
||||
config?: Partial<FixedClientPredictionConfig>
|
||||
): FixedClientPrediction<TState, TInput> {
|
||||
return new FixedClientPrediction(predictor, config);
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// 预设预测器 | Preset Predictors
|
||||
// =============================================================================
|
||||
|
||||
/**
|
||||
* @zh 移动输入类型
|
||||
* @en Movement input type
|
||||
*/
|
||||
export interface IFixedMovementInput {
|
||||
/**
|
||||
* @zh X方向输入 (-1, 0, 1)
|
||||
* @en X direction input (-1, 0, 1)
|
||||
*/
|
||||
readonly dx: number;
|
||||
|
||||
/**
|
||||
* @zh Y方向输入 (-1, 0, 1)
|
||||
* @en Y direction input (-1, 0, 1)
|
||||
*/
|
||||
readonly dy: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* @zh 移动状态类型
|
||||
* @en Movement state type
|
||||
*/
|
||||
export interface IFixedMovementState {
|
||||
/**
|
||||
* @zh 位置
|
||||
* @en Position
|
||||
*/
|
||||
readonly position: FixedVector2;
|
||||
|
||||
/**
|
||||
* @zh 速度
|
||||
* @en Velocity
|
||||
*/
|
||||
readonly velocity: FixedVector2;
|
||||
}
|
||||
|
||||
/**
|
||||
* @zh 创建简单移动预测器
|
||||
* @en Create simple movement predictor
|
||||
*
|
||||
* @param speed - @zh 移动速度(定点数)@en Movement speed (fixed-point)
|
||||
*/
|
||||
export function createFixedMovementPredictor(
|
||||
speed: Fixed32
|
||||
): IFixedPredictor<IFixedMovementState, IFixedMovementInput> {
|
||||
return {
|
||||
predict(
|
||||
state: IFixedMovementState,
|
||||
input: IFixedMovementInput,
|
||||
deltaTime: Fixed32
|
||||
): IFixedMovementState {
|
||||
const inputVec = FixedVector2.from(input.dx, input.dy);
|
||||
const normalizedInput =
|
||||
inputVec.lengthSquared().gt(Fixed32.ZERO) ? inputVec.normalize() : inputVec;
|
||||
|
||||
const velocity = normalizedInput.mul(speed);
|
||||
const displacement = velocity.mul(deltaTime);
|
||||
const newPosition = state.position.add(displacement);
|
||||
|
||||
return {
|
||||
position: newPosition,
|
||||
velocity
|
||||
};
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @zh 创建移动状态位置提取器
|
||||
* @en Create movement state position extractor
|
||||
*/
|
||||
export function createFixedMovementPositionExtractor(): IFixedStatePositionExtractor<IFixedMovementState> {
|
||||
return {
|
||||
getPosition(state: IFixedMovementState): FixedVector2 {
|
||||
return state.position;
|
||||
}
|
||||
};
|
||||
}
|
||||
304
packages/framework/network/src/sync/fixed/FixedSnapshotBuffer.ts
Normal file
304
packages/framework/network/src/sync/fixed/FixedSnapshotBuffer.ts
Normal file
@@ -0,0 +1,304 @@
|
||||
/**
|
||||
* @zh 定点数快照缓冲区
|
||||
* @en Fixed-point Snapshot Buffer
|
||||
*
|
||||
* @zh 用于帧同步确定性计算的快照缓冲区
|
||||
* @en Snapshot buffer for deterministic lockstep calculations
|
||||
*/
|
||||
|
||||
import { Fixed32 } from '@esengine/ecs-framework-math';
|
||||
|
||||
// =============================================================================
|
||||
// 定点数快照接口 | Fixed Snapshot Interfaces
|
||||
// =============================================================================
|
||||
|
||||
/**
|
||||
* @zh 定点数状态快照
|
||||
* @en Fixed-point state snapshot
|
||||
*/
|
||||
export interface IFixedStateSnapshot<T> {
|
||||
/**
|
||||
* @zh 帧号(定点数时间戳)
|
||||
* @en Frame number (fixed-point timestamp)
|
||||
*/
|
||||
readonly frame: number;
|
||||
|
||||
/**
|
||||
* @zh 状态数据
|
||||
* @en State data
|
||||
*/
|
||||
readonly state: T;
|
||||
}
|
||||
|
||||
/**
|
||||
* @zh 定点数快照缓冲区配置
|
||||
* @en Fixed-point snapshot buffer configuration
|
||||
*/
|
||||
export interface IFixedSnapshotBufferConfig {
|
||||
/**
|
||||
* @zh 最大快照数量
|
||||
* @en Maximum snapshot count
|
||||
*/
|
||||
maxSize: number;
|
||||
|
||||
/**
|
||||
* @zh 插值延迟帧数
|
||||
* @en Interpolation delay in frames
|
||||
*/
|
||||
interpolationDelayFrames: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* @zh 插值结果
|
||||
* @en Interpolation result
|
||||
*/
|
||||
export interface IFixedInterpolationResult<T> {
|
||||
/**
|
||||
* @zh 前一个快照
|
||||
* @en Previous snapshot
|
||||
*/
|
||||
readonly from: IFixedStateSnapshot<T>;
|
||||
|
||||
/**
|
||||
* @zh 后一个快照
|
||||
* @en Next snapshot
|
||||
*/
|
||||
readonly to: IFixedStateSnapshot<T>;
|
||||
|
||||
/**
|
||||
* @zh 插值因子 (0-1)
|
||||
* @en Interpolation factor (0-1)
|
||||
*/
|
||||
readonly t: Fixed32;
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// 定点数快照缓冲区实现 | Fixed Snapshot Buffer Implementation
|
||||
// =============================================================================
|
||||
|
||||
/**
|
||||
* @zh 定点数快照缓冲区
|
||||
* @en Fixed-point snapshot buffer
|
||||
*
|
||||
* @zh 使用帧号而非毫秒时间戳,确保跨平台确定性
|
||||
* @en Uses frame numbers instead of millisecond timestamps for cross-platform determinism
|
||||
*/
|
||||
export class FixedSnapshotBuffer<T> {
|
||||
private readonly _buffer: IFixedStateSnapshot<T>[] = [];
|
||||
private readonly _maxSize: number;
|
||||
private readonly _interpolationDelayFrames: number;
|
||||
|
||||
constructor(config: IFixedSnapshotBufferConfig) {
|
||||
this._maxSize = config.maxSize;
|
||||
this._interpolationDelayFrames = config.interpolationDelayFrames;
|
||||
}
|
||||
|
||||
/**
|
||||
* @zh 获取缓冲区大小
|
||||
* @en Get buffer size
|
||||
*/
|
||||
get size(): number {
|
||||
return this._buffer.length;
|
||||
}
|
||||
|
||||
/**
|
||||
* @zh 获取插值延迟帧数
|
||||
* @en Get interpolation delay in frames
|
||||
*/
|
||||
get interpolationDelayFrames(): number {
|
||||
return this._interpolationDelayFrames;
|
||||
}
|
||||
|
||||
/**
|
||||
* @zh 添加快照
|
||||
* @en Add snapshot
|
||||
*
|
||||
* @param snapshot - @zh 状态快照 @en State snapshot
|
||||
*/
|
||||
push(snapshot: IFixedStateSnapshot<T>): void {
|
||||
let insertIndex = this._buffer.length;
|
||||
for (let i = this._buffer.length - 1; i >= 0; i--) {
|
||||
if (this._buffer[i].frame <= snapshot.frame) {
|
||||
insertIndex = i + 1;
|
||||
break;
|
||||
}
|
||||
if (i === 0) {
|
||||
insertIndex = 0;
|
||||
}
|
||||
}
|
||||
|
||||
this._buffer.splice(insertIndex, 0, snapshot);
|
||||
|
||||
while (this._buffer.length > this._maxSize) {
|
||||
this._buffer.shift();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @zh 根据帧号获取插值快照
|
||||
* @en Get interpolation snapshots by frame number
|
||||
*
|
||||
* @param currentFrame - @zh 当前帧号 @en Current frame number
|
||||
* @returns @zh 插值结果(包含定点数插值因子)或 null @en Interpolation result with fixed-point factor or null
|
||||
*/
|
||||
getInterpolationSnapshots(currentFrame: number): IFixedInterpolationResult<T> | null {
|
||||
if (this._buffer.length < 2) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const targetFrame = currentFrame - this._interpolationDelayFrames;
|
||||
|
||||
for (let i = 0; i < this._buffer.length - 1; i++) {
|
||||
const prev = this._buffer[i];
|
||||
const next = this._buffer[i + 1];
|
||||
|
||||
if (prev.frame <= targetFrame && next.frame >= targetFrame) {
|
||||
const duration = next.frame - prev.frame;
|
||||
let t: Fixed32;
|
||||
if (duration > 0) {
|
||||
const elapsed = targetFrame - prev.frame;
|
||||
t = Fixed32.from(elapsed).div(Fixed32.from(duration));
|
||||
t = Fixed32.clamp(t, Fixed32.ZERO, Fixed32.ONE);
|
||||
} else {
|
||||
t = Fixed32.ZERO;
|
||||
}
|
||||
return { from: prev, to: next, t };
|
||||
}
|
||||
}
|
||||
|
||||
if (targetFrame > this._buffer[this._buffer.length - 1].frame) {
|
||||
const prev = this._buffer[this._buffer.length - 2];
|
||||
const next = this._buffer[this._buffer.length - 1];
|
||||
const duration = next.frame - prev.frame;
|
||||
let t: Fixed32;
|
||||
if (duration > 0) {
|
||||
const elapsed = targetFrame - prev.frame;
|
||||
t = Fixed32.from(elapsed).div(Fixed32.from(duration));
|
||||
t = Fixed32.min(t, Fixed32.from(2));
|
||||
} else {
|
||||
t = Fixed32.ONE;
|
||||
}
|
||||
return { from: prev, to: next, t };
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @zh 根据精确帧时间获取插值快照(支持子帧插值)
|
||||
* @en Get interpolation snapshots by precise frame time (supports sub-frame interpolation)
|
||||
*
|
||||
* @param frameTime - @zh 精确帧时间(定点数)@en Precise frame time (fixed-point)
|
||||
* @returns @zh 插值结果或 null @en Interpolation result or null
|
||||
*/
|
||||
getInterpolationSnapshotsFixed(frameTime: Fixed32): IFixedInterpolationResult<T> | null {
|
||||
if (this._buffer.length < 2) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const targetFrame = frameTime.sub(Fixed32.from(this._interpolationDelayFrames));
|
||||
|
||||
for (let i = 0; i < this._buffer.length - 1; i++) {
|
||||
const prev = this._buffer[i];
|
||||
const next = this._buffer[i + 1];
|
||||
const prevFrame = Fixed32.from(prev.frame);
|
||||
const nextFrame = Fixed32.from(next.frame);
|
||||
|
||||
if (prevFrame.le(targetFrame) && nextFrame.ge(targetFrame)) {
|
||||
const duration = nextFrame.sub(prevFrame);
|
||||
let t: Fixed32;
|
||||
if (duration.gt(Fixed32.ZERO)) {
|
||||
t = targetFrame.sub(prevFrame).div(duration);
|
||||
t = Fixed32.clamp(t, Fixed32.ZERO, Fixed32.ONE);
|
||||
} else {
|
||||
t = Fixed32.ZERO;
|
||||
}
|
||||
return { from: prev, to: next, t };
|
||||
}
|
||||
}
|
||||
|
||||
const lastFrame = Fixed32.from(this._buffer[this._buffer.length - 1].frame);
|
||||
if (targetFrame.gt(lastFrame)) {
|
||||
const prev = this._buffer[this._buffer.length - 2];
|
||||
const next = this._buffer[this._buffer.length - 1];
|
||||
const prevFrame = Fixed32.from(prev.frame);
|
||||
const nextFrame = Fixed32.from(next.frame);
|
||||
const duration = nextFrame.sub(prevFrame);
|
||||
let t: Fixed32;
|
||||
if (duration.gt(Fixed32.ZERO)) {
|
||||
t = targetFrame.sub(prevFrame).div(duration);
|
||||
t = Fixed32.min(t, Fixed32.from(2));
|
||||
} else {
|
||||
t = Fixed32.ONE;
|
||||
}
|
||||
return { from: prev, to: next, t };
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @zh 获取最新快照
|
||||
* @en Get latest snapshot
|
||||
*/
|
||||
getLatest(): IFixedStateSnapshot<T> | null {
|
||||
return this._buffer.length > 0 ? this._buffer[this._buffer.length - 1] : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @zh 获取特定帧号的快照
|
||||
* @en Get snapshot at specific frame
|
||||
*/
|
||||
getAtFrame(frame: number): IFixedStateSnapshot<T> | null {
|
||||
for (const snapshot of this._buffer) {
|
||||
if (snapshot.frame === frame) {
|
||||
return snapshot;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @zh 获取特定帧号之后的所有快照
|
||||
* @en Get all snapshots after specific frame
|
||||
*/
|
||||
getSnapshotsAfter(frame: number): IFixedStateSnapshot<T>[] {
|
||||
return this._buffer.filter(s => s.frame > frame);
|
||||
}
|
||||
|
||||
/**
|
||||
* @zh 移除指定帧号之前的所有快照
|
||||
* @en Remove all snapshots before specific frame
|
||||
*/
|
||||
removeSnapshotsBefore(frame: number): void {
|
||||
while (this._buffer.length > 0 && this._buffer[0].frame < frame) {
|
||||
this._buffer.shift();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @zh 清空缓冲区
|
||||
* @en Clear buffer
|
||||
*/
|
||||
clear(): void {
|
||||
this._buffer.length = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// 工厂函数 | Factory Functions
|
||||
// =============================================================================
|
||||
|
||||
/**
|
||||
* @zh 创建定点数快照缓冲区
|
||||
* @en Create fixed-point snapshot buffer
|
||||
*
|
||||
* @param maxSize - @zh 最大快照数量(默认 30)@en Maximum snapshot count (default 30)
|
||||
* @param interpolationDelayFrames - @zh 插值延迟帧数(默认 2)@en Interpolation delay frames (default 2)
|
||||
*/
|
||||
export function createFixedSnapshotBuffer<T>(
|
||||
maxSize: number = 30,
|
||||
interpolationDelayFrames: number = 2
|
||||
): FixedSnapshotBuffer<T> {
|
||||
return new FixedSnapshotBuffer<T>({ maxSize, interpolationDelayFrames });
|
||||
}
|
||||
@@ -0,0 +1,229 @@
|
||||
/**
|
||||
* @zh 定点数变换插值器
|
||||
* @en Fixed-point Transform Interpolator
|
||||
*
|
||||
* @zh 用于帧同步确定性计算的插值器
|
||||
* @en Interpolator for deterministic lockstep calculations
|
||||
*/
|
||||
|
||||
import { Fixed32, FixedVector2, FixedMath } from '@esengine/ecs-framework-math';
|
||||
import {
|
||||
FixedTransformState,
|
||||
FixedTransformStateWithVelocity,
|
||||
type IFixedTransformStateRaw,
|
||||
type IFixedTransformStateWithVelocityRaw
|
||||
} from './FixedTransformState';
|
||||
|
||||
// =============================================================================
|
||||
// 插值器接口 | Interpolator Interface
|
||||
// =============================================================================
|
||||
|
||||
/**
|
||||
* @zh 定点数插值器接口
|
||||
* @en Fixed-point interpolator interface
|
||||
*/
|
||||
export interface IFixedInterpolator<T> {
|
||||
/**
|
||||
* @zh 在两个状态之间插值
|
||||
* @en Interpolate between two states
|
||||
* @param from - @zh 起始状态 @en Start state
|
||||
* @param to - @zh 结束状态 @en End state
|
||||
* @param t - @zh 插值因子 (0-1) @en Interpolation factor (0-1)
|
||||
*/
|
||||
interpolate(from: T, to: T, t: Fixed32): T;
|
||||
}
|
||||
|
||||
/**
|
||||
* @zh 定点数外推器接口
|
||||
* @en Fixed-point extrapolator interface
|
||||
*/
|
||||
export interface IFixedExtrapolator<T> {
|
||||
/**
|
||||
* @zh 基于速度外推状态
|
||||
* @en Extrapolate state based on velocity
|
||||
* @param state - @zh 当前状态 @en Current state
|
||||
* @param deltaTime - @zh 时间增量 @en Time delta
|
||||
*/
|
||||
extrapolate(state: T, deltaTime: Fixed32): T;
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// 定点数变换插值器 | Fixed Transform Interpolator
|
||||
// =============================================================================
|
||||
|
||||
/**
|
||||
* @zh 定点数变换状态插值器
|
||||
* @en Fixed-point transform state interpolator
|
||||
*/
|
||||
export class FixedTransformInterpolator
|
||||
implements IFixedInterpolator<FixedTransformState>, IFixedExtrapolator<FixedTransformStateWithVelocity> {
|
||||
|
||||
/**
|
||||
* @zh 在两个变换状态之间插值
|
||||
* @en Interpolate between two transform states
|
||||
*/
|
||||
interpolate(from: FixedTransformState, to: FixedTransformState, t: Fixed32): FixedTransformState {
|
||||
return new FixedTransformState(
|
||||
from.position.lerp(to.position, t),
|
||||
FixedMath.lerpAngle(from.rotation, to.rotation, t)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @zh 基于速度外推变换状态
|
||||
* @en Extrapolate transform state based on velocity
|
||||
*/
|
||||
extrapolate(
|
||||
state: FixedTransformStateWithVelocity,
|
||||
deltaTime: Fixed32
|
||||
): FixedTransformStateWithVelocity {
|
||||
return new FixedTransformStateWithVelocity(
|
||||
state.position.add(state.velocity.mul(deltaTime)),
|
||||
state.rotation.add(state.angularVelocity.mul(deltaTime)),
|
||||
state.velocity,
|
||||
state.angularVelocity
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @zh 使用原始值进行插值
|
||||
* @en Interpolate using raw values
|
||||
*/
|
||||
interpolateRaw(
|
||||
from: IFixedTransformStateRaw,
|
||||
to: IFixedTransformStateRaw,
|
||||
t: number
|
||||
): IFixedTransformStateRaw {
|
||||
const fromState = FixedTransformState.fromRaw(from);
|
||||
const toState = FixedTransformState.fromRaw(to);
|
||||
const tFixed = Fixed32.from(t);
|
||||
return this.interpolate(fromState, toState, tFixed).toRaw();
|
||||
}
|
||||
|
||||
/**
|
||||
* @zh 使用原始值进行外推
|
||||
* @en Extrapolate using raw values
|
||||
*/
|
||||
extrapolateRaw(
|
||||
state: IFixedTransformStateWithVelocityRaw,
|
||||
deltaTimeMs: number
|
||||
): IFixedTransformStateWithVelocityRaw {
|
||||
const fixedState = FixedTransformStateWithVelocity.fromRaw(state);
|
||||
const deltaTime = Fixed32.from(deltaTimeMs / 1000); // ms to seconds
|
||||
return this.extrapolate(fixedState, deltaTime).toRaw();
|
||||
}
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// 赫尔米特插值器 | Hermite Interpolator
|
||||
// =============================================================================
|
||||
|
||||
/**
|
||||
* @zh 定点数赫尔米特变换插值器(更平滑的曲线)
|
||||
* @en Fixed-point Hermite transform interpolator (smoother curves)
|
||||
*/
|
||||
export class FixedHermiteTransformInterpolator
|
||||
implements IFixedInterpolator<FixedTransformStateWithVelocity> {
|
||||
|
||||
/**
|
||||
* @zh 快照间隔时间(秒)
|
||||
* @en Snapshot interval in seconds
|
||||
*/
|
||||
private readonly snapshotInterval: Fixed32;
|
||||
|
||||
constructor(snapshotIntervalMs: number = 100) {
|
||||
this.snapshotInterval = Fixed32.from(snapshotIntervalMs / 1000);
|
||||
}
|
||||
|
||||
/**
|
||||
* @zh 使用赫尔米特插值
|
||||
* @en Use Hermite interpolation
|
||||
*/
|
||||
interpolate(
|
||||
from: FixedTransformStateWithVelocity,
|
||||
to: FixedTransformStateWithVelocity,
|
||||
t: Fixed32
|
||||
): FixedTransformStateWithVelocity {
|
||||
const t2 = t.mul(t);
|
||||
const t3 = t2.mul(t);
|
||||
|
||||
const two = Fixed32.from(2);
|
||||
const three = Fixed32.from(3);
|
||||
const six = Fixed32.from(6);
|
||||
const four = Fixed32.from(4);
|
||||
|
||||
// Hermite basis functions
|
||||
// h00 = 2t³ - 3t² + 1
|
||||
const h00 = two.mul(t3).sub(three.mul(t2)).add(Fixed32.ONE);
|
||||
// h10 = t³ - 2t² + t
|
||||
const h10 = t3.sub(two.mul(t2)).add(t);
|
||||
// h01 = -2t³ + 3t²
|
||||
const h01 = two.neg().mul(t3).add(three.mul(t2));
|
||||
// h11 = t³ - t²
|
||||
const h11 = t3.sub(t2);
|
||||
|
||||
const dt = this.snapshotInterval;
|
||||
|
||||
// Position interpolation
|
||||
const x = h00.mul(from.position.x)
|
||||
.add(h10.mul(from.velocity.x).mul(dt))
|
||||
.add(h01.mul(to.position.x))
|
||||
.add(h11.mul(to.velocity.x).mul(dt));
|
||||
|
||||
const y = h00.mul(from.position.y)
|
||||
.add(h10.mul(from.velocity.y).mul(dt))
|
||||
.add(h01.mul(to.position.y))
|
||||
.add(h11.mul(to.velocity.y).mul(dt));
|
||||
|
||||
// Velocity derivatives
|
||||
// dh00 = 6t² - 6t
|
||||
const dh00 = six.mul(t2).sub(six.mul(t));
|
||||
// dh10 = 3t² - 4t + 1
|
||||
const dh10 = three.mul(t2).sub(four.mul(t)).add(Fixed32.ONE);
|
||||
// dh01 = -6t² + 6t
|
||||
const dh01 = six.neg().mul(t2).add(six.mul(t));
|
||||
// dh11 = 3t² - 2t
|
||||
const dh11 = three.mul(t2).sub(two.mul(t));
|
||||
|
||||
const velocityX = dh00.mul(from.position.x)
|
||||
.add(dh10.mul(from.velocity.x).mul(dt))
|
||||
.add(dh01.mul(to.position.x))
|
||||
.add(dh11.mul(to.velocity.x).mul(dt))
|
||||
.div(dt);
|
||||
|
||||
const velocityY = dh00.mul(from.position.y)
|
||||
.add(dh10.mul(from.velocity.y).mul(dt))
|
||||
.add(dh01.mul(to.position.y))
|
||||
.add(dh11.mul(to.velocity.y).mul(dt))
|
||||
.div(dt);
|
||||
|
||||
return new FixedTransformStateWithVelocity(
|
||||
new FixedVector2(x, y),
|
||||
FixedMath.lerpAngle(from.rotation, to.rotation, t),
|
||||
new FixedVector2(velocityX, velocityY),
|
||||
Fixed32.lerp(from.angularVelocity, to.angularVelocity, t)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// 工厂函数 | Factory Functions
|
||||
// =============================================================================
|
||||
|
||||
/**
|
||||
* @zh 创建定点数变换插值器
|
||||
* @en Create fixed-point transform interpolator
|
||||
*/
|
||||
export function createFixedTransformInterpolator(): FixedTransformInterpolator {
|
||||
return new FixedTransformInterpolator();
|
||||
}
|
||||
|
||||
/**
|
||||
* @zh 创建定点数赫尔米特变换插值器
|
||||
* @en Create fixed-point Hermite transform interpolator
|
||||
*/
|
||||
export function createFixedHermiteTransformInterpolator(
|
||||
snapshotIntervalMs?: number
|
||||
): FixedHermiteTransformInterpolator {
|
||||
return new FixedHermiteTransformInterpolator(snapshotIntervalMs);
|
||||
}
|
||||
265
packages/framework/network/src/sync/fixed/FixedTransformState.ts
Normal file
265
packages/framework/network/src/sync/fixed/FixedTransformState.ts
Normal file
@@ -0,0 +1,265 @@
|
||||
/**
|
||||
* @zh 定点数变换状态
|
||||
* @en Fixed-point Transform State
|
||||
*
|
||||
* @zh 用于帧同步确定性计算的变换状态
|
||||
* @en Transform state for deterministic lockstep calculations
|
||||
*/
|
||||
|
||||
import { Fixed32, FixedVector2 } from '@esengine/ecs-framework-math';
|
||||
|
||||
// =============================================================================
|
||||
// 定点数变换状态接口 | Fixed Transform State Interface
|
||||
// =============================================================================
|
||||
|
||||
/**
|
||||
* @zh 定点数变换状态(原始值)
|
||||
* @en Fixed-point transform state (raw values)
|
||||
*
|
||||
* @zh 用于网络传输的原始整数格式,确保跨平台一致性
|
||||
* @en Raw integer format for network transmission, ensures cross-platform consistency
|
||||
*/
|
||||
export interface IFixedTransformStateRaw {
|
||||
/**
|
||||
* @zh X 坐标原始值
|
||||
* @en X coordinate raw value
|
||||
*/
|
||||
x: number;
|
||||
|
||||
/**
|
||||
* @zh Y 坐标原始值
|
||||
* @en Y coordinate raw value
|
||||
*/
|
||||
y: number;
|
||||
|
||||
/**
|
||||
* @zh 旋转角度原始值(弧度 * 65536)
|
||||
* @en Rotation raw value (radians * 65536)
|
||||
*/
|
||||
rotation: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* @zh 带速度的定点数变换状态(原始值)
|
||||
* @en Fixed-point transform state with velocity (raw values)
|
||||
*/
|
||||
export interface IFixedTransformStateWithVelocityRaw extends IFixedTransformStateRaw {
|
||||
/**
|
||||
* @zh X 速度原始值
|
||||
* @en X velocity raw value
|
||||
*/
|
||||
velocityX: number;
|
||||
|
||||
/**
|
||||
* @zh Y 速度原始值
|
||||
* @en Y velocity raw value
|
||||
*/
|
||||
velocityY: number;
|
||||
|
||||
/**
|
||||
* @zh 角速度原始值
|
||||
* @en Angular velocity raw value
|
||||
*/
|
||||
angularVelocity: number;
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// 定点数变换状态类 | Fixed Transform State Class
|
||||
// =============================================================================
|
||||
|
||||
/**
|
||||
* @zh 定点数变换状态
|
||||
* @en Fixed-point transform state
|
||||
*/
|
||||
export class FixedTransformState {
|
||||
readonly position: FixedVector2;
|
||||
readonly rotation: Fixed32;
|
||||
|
||||
constructor(position: FixedVector2, rotation: Fixed32) {
|
||||
this.position = position;
|
||||
this.rotation = rotation;
|
||||
}
|
||||
|
||||
/**
|
||||
* @zh 从原始值创建
|
||||
* @en Create from raw values
|
||||
*/
|
||||
static fromRaw(raw: IFixedTransformStateRaw): FixedTransformState {
|
||||
return new FixedTransformState(
|
||||
FixedVector2.fromRaw(raw.x, raw.y),
|
||||
Fixed32.fromRaw(raw.rotation)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @zh 从浮点数创建
|
||||
* @en Create from floating-point numbers
|
||||
*/
|
||||
static from(x: number, y: number, rotation: number): FixedTransformState {
|
||||
return new FixedTransformState(
|
||||
FixedVector2.from(x, y),
|
||||
Fixed32.from(rotation)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @zh 转换为原始值(用于网络传输)
|
||||
* @en Convert to raw values (for network transmission)
|
||||
*/
|
||||
toRaw(): IFixedTransformStateRaw {
|
||||
return {
|
||||
x: this.position.x.toRaw(),
|
||||
y: this.position.y.toRaw(),
|
||||
rotation: this.rotation.toRaw()
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @zh 转换为浮点数对象(用于渲染)
|
||||
* @en Convert to floating-point object (for rendering)
|
||||
*/
|
||||
toFloat(): { x: number; y: number; rotation: number } {
|
||||
return {
|
||||
x: this.position.x.toNumber(),
|
||||
y: this.position.y.toNumber(),
|
||||
rotation: this.rotation.toNumber()
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @zh 检查是否相等
|
||||
* @en Check equality
|
||||
*/
|
||||
equals(other: FixedTransformState): boolean {
|
||||
return this.position.equals(other.position) && this.rotation.eq(other.rotation);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @zh 带速度的定点数变换状态
|
||||
* @en Fixed-point transform state with velocity
|
||||
*/
|
||||
export class FixedTransformStateWithVelocity {
|
||||
readonly position: FixedVector2;
|
||||
readonly rotation: Fixed32;
|
||||
readonly velocity: FixedVector2;
|
||||
readonly angularVelocity: Fixed32;
|
||||
|
||||
constructor(
|
||||
position: FixedVector2,
|
||||
rotation: Fixed32,
|
||||
velocity: FixedVector2,
|
||||
angularVelocity: Fixed32
|
||||
) {
|
||||
this.position = position;
|
||||
this.rotation = rotation;
|
||||
this.velocity = velocity;
|
||||
this.angularVelocity = angularVelocity;
|
||||
}
|
||||
|
||||
/**
|
||||
* @zh 从原始值创建
|
||||
* @en Create from raw values
|
||||
*/
|
||||
static fromRaw(raw: IFixedTransformStateWithVelocityRaw): FixedTransformStateWithVelocity {
|
||||
return new FixedTransformStateWithVelocity(
|
||||
FixedVector2.fromRaw(raw.x, raw.y),
|
||||
Fixed32.fromRaw(raw.rotation),
|
||||
FixedVector2.fromRaw(raw.velocityX, raw.velocityY),
|
||||
Fixed32.fromRaw(raw.angularVelocity)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @zh 从浮点数创建
|
||||
* @en Create from floating-point numbers
|
||||
*/
|
||||
static from(
|
||||
x: number,
|
||||
y: number,
|
||||
rotation: number,
|
||||
velocityX: number,
|
||||
velocityY: number,
|
||||
angularVelocity: number
|
||||
): FixedTransformStateWithVelocity {
|
||||
return new FixedTransformStateWithVelocity(
|
||||
FixedVector2.from(x, y),
|
||||
Fixed32.from(rotation),
|
||||
FixedVector2.from(velocityX, velocityY),
|
||||
Fixed32.from(angularVelocity)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @zh 转换为原始值
|
||||
* @en Convert to raw values
|
||||
*/
|
||||
toRaw(): IFixedTransformStateWithVelocityRaw {
|
||||
return {
|
||||
x: this.position.x.toRaw(),
|
||||
y: this.position.y.toRaw(),
|
||||
rotation: this.rotation.toRaw(),
|
||||
velocityX: this.velocity.x.toRaw(),
|
||||
velocityY: this.velocity.y.toRaw(),
|
||||
angularVelocity: this.angularVelocity.toRaw()
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @zh 转换为浮点数对象
|
||||
* @en Convert to floating-point object
|
||||
*/
|
||||
toFloat(): {
|
||||
x: number;
|
||||
y: number;
|
||||
rotation: number;
|
||||
velocityX: number;
|
||||
velocityY: number;
|
||||
angularVelocity: number;
|
||||
} {
|
||||
return {
|
||||
x: this.position.x.toNumber(),
|
||||
y: this.position.y.toNumber(),
|
||||
rotation: this.rotation.toNumber(),
|
||||
velocityX: this.velocity.x.toNumber(),
|
||||
velocityY: this.velocity.y.toNumber(),
|
||||
angularVelocity: this.angularVelocity.toNumber()
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @zh 检查是否相等
|
||||
* @en Check equality
|
||||
*/
|
||||
equals(other: FixedTransformStateWithVelocity): boolean {
|
||||
return this.position.equals(other.position) &&
|
||||
this.rotation.eq(other.rotation) &&
|
||||
this.velocity.equals(other.velocity) &&
|
||||
this.angularVelocity.eq(other.angularVelocity);
|
||||
}
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// 工具函数 | Utility Functions
|
||||
// =============================================================================
|
||||
|
||||
/**
|
||||
* @zh 创建零状态
|
||||
* @en Create zero state
|
||||
*/
|
||||
export function createZeroFixedTransformState(): FixedTransformState {
|
||||
return new FixedTransformState(FixedVector2.ZERO, Fixed32.ZERO);
|
||||
}
|
||||
|
||||
/**
|
||||
* @zh 创建带速度的零状态
|
||||
* @en Create zero state with velocity
|
||||
*/
|
||||
export function createZeroFixedTransformStateWithVelocity(): FixedTransformStateWithVelocity {
|
||||
return new FixedTransformStateWithVelocity(
|
||||
FixedVector2.ZERO,
|
||||
Fixed32.ZERO,
|
||||
FixedVector2.ZERO,
|
||||
Fixed32.ZERO
|
||||
);
|
||||
}
|
||||
63
packages/framework/network/src/sync/fixed/index.ts
Normal file
63
packages/framework/network/src/sync/fixed/index.ts
Normal file
@@ -0,0 +1,63 @@
|
||||
/**
|
||||
* @zh 定点数网络同步模块
|
||||
* @en Fixed-point network sync module
|
||||
*
|
||||
* @zh 用于帧同步确定性计算的网络同步类型和工具
|
||||
* @en Network sync types and utilities for deterministic lockstep calculations
|
||||
*/
|
||||
|
||||
// =============================================================================
|
||||
// 变换状态 | Transform State
|
||||
// =============================================================================
|
||||
|
||||
export {
|
||||
FixedTransformState,
|
||||
FixedTransformStateWithVelocity,
|
||||
createZeroFixedTransformState,
|
||||
createZeroFixedTransformStateWithVelocity,
|
||||
type IFixedTransformStateRaw,
|
||||
type IFixedTransformStateWithVelocityRaw,
|
||||
} from './FixedTransformState';
|
||||
|
||||
// =============================================================================
|
||||
// 插值器 | Interpolators
|
||||
// =============================================================================
|
||||
|
||||
export {
|
||||
FixedTransformInterpolator,
|
||||
FixedHermiteTransformInterpolator,
|
||||
createFixedTransformInterpolator,
|
||||
createFixedHermiteTransformInterpolator,
|
||||
type IFixedInterpolator,
|
||||
type IFixedExtrapolator,
|
||||
} from './FixedTransformInterpolator';
|
||||
|
||||
// =============================================================================
|
||||
// 快照缓冲区 | Snapshot Buffer
|
||||
// =============================================================================
|
||||
|
||||
export {
|
||||
FixedSnapshotBuffer,
|
||||
createFixedSnapshotBuffer,
|
||||
type IFixedStateSnapshot,
|
||||
type IFixedSnapshotBufferConfig,
|
||||
type IFixedInterpolationResult,
|
||||
} from './FixedSnapshotBuffer';
|
||||
|
||||
// =============================================================================
|
||||
// 客户端预测 | Client Prediction
|
||||
// =============================================================================
|
||||
|
||||
export {
|
||||
FixedClientPrediction,
|
||||
createFixedClientPrediction,
|
||||
createFixedMovementPredictor,
|
||||
createFixedMovementPositionExtractor,
|
||||
type IFixedInputSnapshot,
|
||||
type IFixedPredictedState,
|
||||
type IFixedPredictor,
|
||||
type IFixedStatePositionExtractor,
|
||||
type FixedClientPredictionConfig,
|
||||
type IFixedMovementInput,
|
||||
type IFixedMovementState,
|
||||
} from './FixedClientPrediction';
|
||||
@@ -78,3 +78,49 @@ export {
|
||||
ComponentSyncSystem,
|
||||
createComponentSyncSystem
|
||||
} from './ComponentSync';
|
||||
|
||||
// =============================================================================
|
||||
// 定点数同步 | Fixed-point Sync (Deterministic Lockstep)
|
||||
// =============================================================================
|
||||
|
||||
export {
|
||||
// Transform State
|
||||
FixedTransformState,
|
||||
FixedTransformStateWithVelocity,
|
||||
createZeroFixedTransformState,
|
||||
createZeroFixedTransformStateWithVelocity,
|
||||
// Interpolators
|
||||
FixedTransformInterpolator,
|
||||
FixedHermiteTransformInterpolator,
|
||||
createFixedTransformInterpolator,
|
||||
createFixedHermiteTransformInterpolator,
|
||||
// Snapshot Buffer
|
||||
FixedSnapshotBuffer,
|
||||
createFixedSnapshotBuffer,
|
||||
// Client Prediction
|
||||
FixedClientPrediction,
|
||||
createFixedClientPrediction,
|
||||
createFixedMovementPredictor,
|
||||
createFixedMovementPositionExtractor,
|
||||
} from './fixed';
|
||||
|
||||
export type {
|
||||
// Transform State Types
|
||||
IFixedTransformStateRaw,
|
||||
IFixedTransformStateWithVelocityRaw,
|
||||
// Interpolator Types
|
||||
IFixedInterpolator,
|
||||
IFixedExtrapolator,
|
||||
// Snapshot Buffer Types
|
||||
IFixedStateSnapshot,
|
||||
IFixedSnapshotBufferConfig,
|
||||
IFixedInterpolationResult,
|
||||
// Client Prediction Types
|
||||
IFixedInputSnapshot,
|
||||
IFixedPredictedState,
|
||||
IFixedPredictor,
|
||||
IFixedStatePositionExtractor,
|
||||
FixedClientPredictionConfig,
|
||||
IFixedMovementInput,
|
||||
IFixedMovementState,
|
||||
} from './fixed';
|
||||
|
||||
Reference in New Issue
Block a user