客户端使用
NetworkPlugin
Section titled “NetworkPlugin”NetworkPlugin 是客户端网络功能的核心入口,基于 @esengine/rpc 提供类型安全的网络通信。
import { Core } from '@esengine/ecs-framework';import { NetworkPlugin } from '@esengine/network';
// 创建并安装插件const networkPlugin = new NetworkPlugin();await Core.installPlugin(networkPlugin);
// 连接服务器const success = await networkPlugin.connect({ url: 'ws://localhost:3000', playerName: 'PlayerName', roomId: 'room-1' // 可选});
// 断开连接await networkPlugin.disconnect();class NetworkPlugin { readonly name: string; readonly version: string;
// 访问器 get networkService(): GameNetworkService; get syncSystem(): NetworkSyncSystem; get spawnSystem(): NetworkSpawnSystem; get inputSystem(): NetworkInputSystem; get localPlayerId(): number; get isConnected(): boolean;
// 连接服务器 connect(options: { url: string; playerName: string; roomId?: string; }): Promise<boolean>;
// 断开连接 disconnect(): Promise<void>;
// 注册预制体工厂 registerPrefab(prefabType: string, factory: PrefabFactory): void;
// 发送输入 sendMoveInput(x: number, y: number): void; sendActionInput(action: string): void;}NetworkIdentity
Section titled “NetworkIdentity”网络标识组件,每个网络同步的实体必须拥有:
class NetworkIdentity extends Component { netId: number; // 网络唯一 ID ownerId: number; // 所有者客户端 ID bIsLocalPlayer: boolean; // 是否为本地玩家 bHasAuthority: boolean; // 是否有权限控制}使用示例:
networkPlugin.registerPrefab('player', (scene, spawn) => { const entity = scene.createEntity(`player_${spawn.netId}`);
const identity = entity.addComponent(new NetworkIdentity()); identity.netId = spawn.netId; identity.ownerId = spawn.ownerId; identity.bIsLocalPlayer = spawn.ownerId === networkPlugin.localPlayerId;
return entity;});NetworkTransform
Section titled “NetworkTransform”网络变换组件,用于位置和旋转同步:
class NetworkTransform extends Component { position: { x: number; y: number }; rotation: number; velocity: { x: number; y: number };}NetworkSyncSystem
Section titled “NetworkSyncSystem”处理服务器状态同步和插值:
- 接收服务器状态快照
- 将状态存入快照缓冲区
- 对远程实体进行插值平滑
NetworkSpawnSystem
Section titled “NetworkSpawnSystem”处理实体的网络生成和销毁:
- 监听 Spawn/Despawn 消息
- 使用注册的预制体工厂创建实体
- 管理网络实体的生命周期
NetworkInputSystem
Section titled “NetworkInputSystem”处理本地玩家输入的网络发送:
class NetworkInputSystem extends EntitySystem { addMoveInput(x: number, y: number): void; addActionInput(action: string): void; clearInput(): void;}使用示例:
// 方式 1:通过 NetworkPlugin(推荐)networkPlugin.sendMoveInput(0, 1);networkPlugin.sendActionInput('jump');
// 方式 2:直接使用 inputSystemconst inputSystem = networkPlugin.inputSystem;inputSystem.addMoveInput(0, 1);inputSystem.addActionInput('jump');预制体工厂用于创建网络实体:
type PrefabFactory = (scene: Scene, spawn: MsgSpawn) => Entity;完整示例:
// 注册玩家预制体networkPlugin.registerPrefab('player', (scene, spawn) => { const entity = scene.createEntity(`player_${spawn.netId}`);
const identity = entity.addComponent(new NetworkIdentity()); identity.netId = spawn.netId; identity.ownerId = spawn.ownerId; identity.bIsLocalPlayer = spawn.ownerId === networkPlugin.localPlayerId;
entity.addComponent(new NetworkTransform());
// 本地玩家添加输入组件 if (identity.bIsLocalPlayer) { entity.addComponent(new LocalInputComponent()); }
return entity;});
// 注册敌人预制体networkPlugin.registerPrefab('enemy', (scene, spawn) => { const entity = scene.createEntity(`enemy_${spawn.netId}`);
const identity = entity.addComponent(new NetworkIdentity()); identity.netId = spawn.netId; identity.ownerId = spawn.ownerId;
entity.addComponent(new NetworkTransform()); entity.addComponent(new EnemyComponent());
return entity;});创建自定义输入处理系统:
import { EntitySystem, Matcher, Entity } from '@esengine/ecs-framework';import { NetworkPlugin, NetworkIdentity } from '@esengine/network';
class LocalInputHandler extends EntitySystem { private _networkPlugin: NetworkPlugin | null = null;
constructor() { super(Matcher.empty().all(NetworkIdentity, LocalInputComponent)); }
protected onAddedToScene(): void { this._networkPlugin = Core.getPlugin(NetworkPlugin); }
protected processEntity(entity: Entity, dt: number): void { if (!this._networkPlugin) return;
const identity = entity.getComponent(NetworkIdentity)!; if (!identity.bIsLocalPlayer) return;
// 读取键盘输入 let moveX = 0; let moveY = 0;
if (keyboard.isPressed('A')) moveX -= 1; if (keyboard.isPressed('D')) moveX += 1; if (keyboard.isPressed('W')) moveY += 1; if (keyboard.isPressed('S')) moveY -= 1;
if (moveX !== 0 || moveY !== 0) { this._networkPlugin.sendMoveInput(moveX, moveY); }
if (keyboard.isJustPressed('Space')) { this._networkPlugin.sendActionInput('jump'); } }}连接状态监听
Section titled “连接状态监听”使用 GameNetworkService 的链式 API 监听消息:
const { networkService } = networkPlugin;
// 监听状态同步networkService.onSync((data) => { console.log('收到同步数据:', data.entities.length, '个实体');});
// 监听实体生成networkService.onSpawn((data) => { console.log('生成实体:', data.prefab, 'netId:', data.netId);});
// 监听实体销毁networkService.onDespawn((data) => { console.log('销毁实体:', data.netId);});
// 通过连接选项设置回调await networkPlugin.connect({ url: 'ws://localhost:3000', playerName: 'Player1', onConnect: () => console.log('已连接'), onDisconnect: (reason) => console.log('已断开:', reason), onError: (error) => console.error('网络错误:', error)});-
权限检查:使用
bHasAuthority检查是否有权限修改实体 -
本地玩家标识:通过
bIsLocalPlayer区分本地和远程玩家 -
预制体管理:为每种网络实体类型注册对应的预制体工厂
-
输入发送:推荐使用
NetworkPlugin.sendMoveInput()和sendActionInput()方法