chore: update pathfinding, add rpc/world-streaming docs, refactor world-streaming location (#376)
This commit is contained in:
@@ -0,0 +1,103 @@
|
||||
import { Component, ECSComponent, Serializable, Serialize, Property } from '@esengine/ecs-framework';
|
||||
import type { IChunkCoord, IChunkBounds } from '../types';
|
||||
import { EChunkState } from '../types';
|
||||
|
||||
/**
|
||||
* 区块组件
|
||||
*
|
||||
* Attached to chunk root entities. Tracks chunk state and bounds.
|
||||
*
|
||||
* 区块组件挂载在区块根实体上,用于管理区块状态和边界信息。
|
||||
*/
|
||||
@ECSComponent('Chunk')
|
||||
@Serializable({ version: 1, typeId: 'Chunk' })
|
||||
export class ChunkComponent extends Component {
|
||||
@Serialize()
|
||||
@Property({ type: 'integer', label: 'Coord X', readOnly: true })
|
||||
private _coordX: number = 0;
|
||||
|
||||
@Serialize()
|
||||
@Property({ type: 'integer', label: 'Coord Y', readOnly: true })
|
||||
private _coordY: number = 0;
|
||||
|
||||
@Serialize()
|
||||
@Property({ type: 'string', label: 'State', readOnly: true })
|
||||
private _state: EChunkState = EChunkState.Unloaded;
|
||||
|
||||
@Serialize()
|
||||
private _minX: number = 0;
|
||||
|
||||
@Serialize()
|
||||
private _minY: number = 0;
|
||||
|
||||
@Serialize()
|
||||
private _maxX: number = 0;
|
||||
|
||||
@Serialize()
|
||||
private _maxY: number = 0;
|
||||
|
||||
private _lastAccessTime: number = 0;
|
||||
|
||||
get coord(): IChunkCoord {
|
||||
return { x: this._coordX, y: this._coordY };
|
||||
}
|
||||
|
||||
get bounds(): IChunkBounds {
|
||||
return {
|
||||
minX: this._minX,
|
||||
minY: this._minY,
|
||||
maxX: this._maxX,
|
||||
maxY: this._maxY
|
||||
};
|
||||
}
|
||||
|
||||
get state(): EChunkState {
|
||||
return this._state;
|
||||
}
|
||||
|
||||
get lastAccessTime(): number {
|
||||
return this._lastAccessTime;
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化区块
|
||||
*
|
||||
* Initialize chunk with coordinates and bounds.
|
||||
*/
|
||||
initialize(coord: IChunkCoord, bounds: IChunkBounds): void {
|
||||
this._coordX = coord.x;
|
||||
this._coordY = coord.y;
|
||||
this._minX = bounds.minX;
|
||||
this._minY = bounds.minY;
|
||||
this._maxX = bounds.maxX;
|
||||
this._maxY = bounds.maxY;
|
||||
this._lastAccessTime = Date.now();
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置区块状态
|
||||
*
|
||||
* Set chunk state.
|
||||
*/
|
||||
setState(state: EChunkState): void {
|
||||
this._state = state;
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新访问时间
|
||||
*
|
||||
* Update last access time for LRU tracking.
|
||||
*/
|
||||
touch(): void {
|
||||
this._lastAccessTime = Date.now();
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查点是否在区块内
|
||||
*
|
||||
* Check if a point is within chunk bounds.
|
||||
*/
|
||||
containsPoint(x: number, y: number): boolean {
|
||||
return x >= this._minX && x < this._maxX && y >= this._minY && y < this._maxY;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,133 @@
|
||||
import { Component, ECSComponent, Serializable, Serialize, Property } from '@esengine/ecs-framework';
|
||||
import type { IChunkCoord, IStreamingConfig } from '../types';
|
||||
import { DEFAULT_STREAMING_CONFIG } from '../types';
|
||||
|
||||
/**
|
||||
* 区块加载器组件
|
||||
*
|
||||
* Singleton component that manages streaming configuration.
|
||||
* Attach to a manager entity in the scene.
|
||||
*
|
||||
* 单例组件,管理流式加载配置。挂载在场景管理实体上。
|
||||
*/
|
||||
@ECSComponent('ChunkLoader')
|
||||
@Serializable({ version: 1, typeId: 'ChunkLoader' })
|
||||
export class ChunkLoaderComponent extends Component {
|
||||
@Serialize()
|
||||
@Property({ type: 'integer', label: 'Chunk Size', min: 64, max: 4096 })
|
||||
private _chunkSize: number = DEFAULT_STREAMING_CONFIG.chunkSize;
|
||||
|
||||
@Serialize()
|
||||
@Property({ type: 'integer', label: 'Load Radius', min: 1, max: 10 })
|
||||
private _loadRadius: number = DEFAULT_STREAMING_CONFIG.loadRadius;
|
||||
|
||||
@Serialize()
|
||||
@Property({ type: 'integer', label: 'Unload Radius', min: 2, max: 20 })
|
||||
private _unloadRadius: number = DEFAULT_STREAMING_CONFIG.unloadRadius;
|
||||
|
||||
@Serialize()
|
||||
@Property({ type: 'integer', label: 'Max Loads Per Frame', min: 1, max: 10 })
|
||||
private _maxLoadsPerFrame: number = DEFAULT_STREAMING_CONFIG.maxLoadsPerFrame;
|
||||
|
||||
@Serialize()
|
||||
@Property({ type: 'integer', label: 'Max Unloads Per Frame', min: 1, max: 10 })
|
||||
private _maxUnloadsPerFrame: number = DEFAULT_STREAMING_CONFIG.maxUnloadsPerFrame;
|
||||
|
||||
@Serialize()
|
||||
@Property({ type: 'integer', label: 'Unload Delay (ms)', min: 0, max: 30000 })
|
||||
private _unloadDelay: number = DEFAULT_STREAMING_CONFIG.unloadDelay;
|
||||
|
||||
@Serialize()
|
||||
@Property({ type: 'boolean', label: 'Enable Prefetch' })
|
||||
private _bEnablePrefetch: boolean = DEFAULT_STREAMING_CONFIG.bEnablePrefetch;
|
||||
|
||||
@Serialize()
|
||||
@Property({ type: 'integer', label: 'Prefetch Radius', min: 0, max: 5 })
|
||||
private _prefetchRadius: number = DEFAULT_STREAMING_CONFIG.prefetchRadius;
|
||||
|
||||
get chunkSize(): number {
|
||||
return this._chunkSize;
|
||||
}
|
||||
|
||||
get loadRadius(): number {
|
||||
return this._loadRadius;
|
||||
}
|
||||
|
||||
get unloadRadius(): number {
|
||||
return this._unloadRadius;
|
||||
}
|
||||
|
||||
get maxLoadsPerFrame(): number {
|
||||
return this._maxLoadsPerFrame;
|
||||
}
|
||||
|
||||
get maxUnloadsPerFrame(): number {
|
||||
return this._maxUnloadsPerFrame;
|
||||
}
|
||||
|
||||
get unloadDelay(): number {
|
||||
return this._unloadDelay;
|
||||
}
|
||||
|
||||
get bEnablePrefetch(): boolean {
|
||||
return this._bEnablePrefetch;
|
||||
}
|
||||
|
||||
get prefetchRadius(): number {
|
||||
return this._prefetchRadius;
|
||||
}
|
||||
|
||||
/**
|
||||
* 应用配置
|
||||
*
|
||||
* Apply streaming configuration.
|
||||
*/
|
||||
applyConfig(config: Partial<IStreamingConfig>): void {
|
||||
if (config.chunkSize !== undefined) this._chunkSize = config.chunkSize;
|
||||
if (config.loadRadius !== undefined) this._loadRadius = config.loadRadius;
|
||||
if (config.unloadRadius !== undefined) this._unloadRadius = config.unloadRadius;
|
||||
if (config.maxLoadsPerFrame !== undefined) this._maxLoadsPerFrame = config.maxLoadsPerFrame;
|
||||
if (config.maxUnloadsPerFrame !== undefined) this._maxUnloadsPerFrame = config.maxUnloadsPerFrame;
|
||||
if (config.unloadDelay !== undefined) this._unloadDelay = config.unloadDelay;
|
||||
if (config.bEnablePrefetch !== undefined) this._bEnablePrefetch = config.bEnablePrefetch;
|
||||
if (config.prefetchRadius !== undefined) this._prefetchRadius = config.prefetchRadius;
|
||||
}
|
||||
|
||||
/**
|
||||
* 世界坐标转区块坐标
|
||||
*
|
||||
* Convert world position to chunk coordinates.
|
||||
*/
|
||||
worldToChunk(worldX: number, worldY: number): IChunkCoord {
|
||||
return {
|
||||
x: Math.floor(worldX / this._chunkSize),
|
||||
y: Math.floor(worldY / this._chunkSize)
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 区块坐标转世界坐标(返回区块中心)
|
||||
*
|
||||
* Convert chunk coordinates to world position (chunk center).
|
||||
*/
|
||||
chunkToWorld(coord: IChunkCoord): { x: number; y: number } {
|
||||
return {
|
||||
x: coord.x * this._chunkSize + this._chunkSize * 0.5,
|
||||
y: coord.y * this._chunkSize + this._chunkSize * 0.5
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取区块边界
|
||||
*
|
||||
* Get chunk world-space bounds.
|
||||
*/
|
||||
getChunkBounds(coord: IChunkCoord): { minX: number; minY: number; maxX: number; maxY: number } {
|
||||
return {
|
||||
minX: coord.x * this._chunkSize,
|
||||
minY: coord.y * this._chunkSize,
|
||||
maxX: (coord.x + 1) * this._chunkSize,
|
||||
maxY: (coord.y + 1) * this._chunkSize
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
import { Component, ECSComponent, Serializable, Serialize, Property } from '@esengine/ecs-framework';
|
||||
import type { IPositionable } from '@esengine/spatial';
|
||||
import type { IVector2 } from '@esengine/ecs-framework-math';
|
||||
|
||||
/**
|
||||
* 流式锚点组件
|
||||
*
|
||||
* Marks an entity as a streaming anchor point.
|
||||
* Chunks are loaded/unloaded based on distance to anchors.
|
||||
*
|
||||
* 标记实体作为流式加载锚点。通常挂载在玩家或摄像机实体上,
|
||||
* 系统会根据锚点位置加载/卸载周围区块。
|
||||
*
|
||||
* 用户需要在每帧更新此组件的 x/y 位置。
|
||||
* User must update the x/y position each frame.
|
||||
*/
|
||||
@ECSComponent('StreamingAnchor')
|
||||
@Serializable({ version: 1, typeId: 'StreamingAnchor' })
|
||||
export class StreamingAnchorComponent extends Component implements IPositionable {
|
||||
/**
|
||||
* 当前 X 位置
|
||||
*
|
||||
* Current X position in world units.
|
||||
*/
|
||||
@Serialize()
|
||||
@Property({ type: 'number', label: 'X' })
|
||||
x: number = 0;
|
||||
|
||||
/**
|
||||
* 当前 Y 位置
|
||||
*
|
||||
* Current Y position in world units.
|
||||
*/
|
||||
@Serialize()
|
||||
@Property({ type: 'number', label: 'Y' })
|
||||
y: number = 0;
|
||||
|
||||
/**
|
||||
* 获取位置 (IPositionable 接口)
|
||||
*
|
||||
* Get position (IPositionable interface).
|
||||
*/
|
||||
get position(): IVector2 {
|
||||
return { x: this.x, y: this.y };
|
||||
}
|
||||
/**
|
||||
* 锚点权重
|
||||
*
|
||||
* Weight multiplier for this anchor's load radius.
|
||||
* Higher values mean larger load radius around this anchor.
|
||||
*/
|
||||
@Serialize()
|
||||
@Property({ type: 'number', label: 'Weight', min: 0.1, max: 10 })
|
||||
weight: number = 1.0;
|
||||
|
||||
/**
|
||||
* 是否启用预加载
|
||||
*
|
||||
* Enable directional prefetching based on movement.
|
||||
*/
|
||||
@Serialize()
|
||||
@Property({ type: 'boolean', label: 'Enable Prefetch' })
|
||||
bEnablePrefetch: boolean = true;
|
||||
|
||||
/**
|
||||
* 上一帧位置 X
|
||||
*
|
||||
* Previous frame X position for velocity calculation.
|
||||
*/
|
||||
previousX: number = 0;
|
||||
|
||||
/**
|
||||
* 上一帧位置 Y
|
||||
*
|
||||
* Previous frame Y position for velocity calculation.
|
||||
*/
|
||||
previousY: number = 0;
|
||||
|
||||
/**
|
||||
* 速度 X 分量
|
||||
*
|
||||
* X component of velocity (units per second).
|
||||
*/
|
||||
velocityX: number = 0;
|
||||
|
||||
/**
|
||||
* 速度 Y 分量
|
||||
*
|
||||
* Y component of velocity (units per second).
|
||||
*/
|
||||
velocityY: number = 0;
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
export { ChunkComponent } from './ChunkComponent';
|
||||
export { StreamingAnchorComponent } from './StreamingAnchorComponent';
|
||||
export { ChunkLoaderComponent } from './ChunkLoaderComponent';
|
||||
Reference in New Issue
Block a user