2025-12-18 13:25:04 +08:00
|
|
|
import { instantiate, Node, Prefab } from 'cc';
|
2025-12-14 23:35:08 +08:00
|
|
|
import { NetManager } from '../../Framework/Net/NetManager';
|
|
|
|
|
import { ResMgr } from '../../Framework/ResMgr/ResMgr';
|
2025-12-18 16:04:56 +08:00
|
|
|
import { UIMgr } from '../../Framework/UI/UIMgr';
|
2025-12-14 23:35:08 +08:00
|
|
|
import { MsgPlayerJoin } from '../../Shared/protocols/MsgPlayerJoin';
|
|
|
|
|
import { MsgPlayerMove } from '../../Shared/protocols/MsgPlayerMove';
|
2025-12-18 13:25:04 +08:00
|
|
|
import { PlayerInfo } from '../../Shared/protocols/MsgResLogin';
|
|
|
|
|
import { CameraController } from './CameraController';
|
2025-12-14 23:35:08 +08:00
|
|
|
import { PlayerController } from './PlayerController';
|
2025-12-18 16:04:56 +08:00
|
|
|
import { PlayerInfoData } from './PlayerInfo';
|
2025-12-14 23:35:08 +08:00
|
|
|
import { RemotePlayer } from './RemotePlayer';
|
2025-12-18 16:04:56 +08:00
|
|
|
import { UIGame } from './UIGame';
|
2025-12-14 23:35:08 +08:00
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* World 世界管理器
|
|
|
|
|
* 负责管理游戏世界中的所有玩家
|
|
|
|
|
* 包括本地玩家和远程玩家的创建、更新和销毁
|
|
|
|
|
*/
|
|
|
|
|
export class World {
|
|
|
|
|
private static instance: World = null;
|
|
|
|
|
|
|
|
|
|
/** 世界根节点 */
|
|
|
|
|
private worldRoot: Node = null;
|
|
|
|
|
|
|
|
|
|
/** 本地玩家信息 */
|
|
|
|
|
private localPlayer: PlayerInfo = null;
|
|
|
|
|
|
|
|
|
|
/** 本地玩家控制器 */
|
|
|
|
|
private localPlayerController: PlayerController = null;
|
|
|
|
|
|
|
|
|
|
/** 本地玩家节点 */
|
|
|
|
|
private localPlayerNode: Node = null;
|
|
|
|
|
|
|
|
|
|
/** 远程玩家列表 Map<playerId, RemotePlayer> */
|
|
|
|
|
private remotePlayers: Map<string, RemotePlayer> = new Map();
|
|
|
|
|
|
|
|
|
|
/** 玩家模型预制体 */
|
|
|
|
|
private playerPrefab: Prefab = null;
|
|
|
|
|
|
2025-12-18 13:25:04 +08:00
|
|
|
/** 摄像机控制器 */
|
|
|
|
|
private cameraController: CameraController = null;
|
|
|
|
|
|
2025-12-18 16:04:56 +08:00
|
|
|
/** UIGame实例 */
|
|
|
|
|
private uiGameInstance: UIGame = null;
|
|
|
|
|
|
2025-12-18 13:25:04 +08:00
|
|
|
private constructor() { }
|
2025-12-14 23:35:08 +08:00
|
|
|
|
|
|
|
|
public static getInstance(): World {
|
|
|
|
|
if (!World.instance) {
|
|
|
|
|
World.instance = new World();
|
|
|
|
|
}
|
|
|
|
|
return World.instance;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 初始化世界
|
|
|
|
|
* @param worldRoot 世界根节点
|
|
|
|
|
* @param localPlayer 本地玩家信息
|
2025-12-18 16:04:56 +08:00
|
|
|
* @param otherPlayers 其他在线玩家信息
|
2025-12-14 23:35:08 +08:00
|
|
|
*/
|
2025-12-18 16:04:56 +08:00
|
|
|
public async init(worldRoot: Node, localPlayer: PlayerInfo, otherPlayers?: PlayerInfo[]): Promise<void> {
|
2025-12-14 23:35:08 +08:00
|
|
|
this.worldRoot = worldRoot;
|
|
|
|
|
this.localPlayer = localPlayer;
|
|
|
|
|
|
2025-12-18 16:04:56 +08:00
|
|
|
// 获取UIGame实例
|
|
|
|
|
this.uiGameInstance = UIMgr.getInstance().get(UIGame) as UIGame;
|
|
|
|
|
|
2025-12-14 23:35:08 +08:00
|
|
|
// 加载玩家模型预制体
|
|
|
|
|
await this.loadPlayerPrefab();
|
|
|
|
|
|
|
|
|
|
// 注册网络消息监听
|
|
|
|
|
this.registerNetworkListeners();
|
|
|
|
|
|
|
|
|
|
// 创建本地玩家
|
|
|
|
|
await this.createLocalPlayer();
|
|
|
|
|
|
2025-12-18 16:04:56 +08:00
|
|
|
// 创建其他已在线的玩家
|
|
|
|
|
await this.createOtherPlayers(otherPlayers);
|
|
|
|
|
|
2025-12-14 23:35:08 +08:00
|
|
|
console.log('[World] 世界初始化完成');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 加载玩家模型预制体
|
|
|
|
|
*/
|
|
|
|
|
private async loadPlayerPrefab(): Promise<void> {
|
|
|
|
|
try {
|
2025-12-18 13:25:04 +08:00
|
|
|
this.playerPrefab = await ResMgr.getInstance().load('res', 'Actor/M1/M1', Prefab);
|
2025-12-14 23:35:08 +08:00
|
|
|
console.log('[World] 玩家模型预制体加载成功');
|
|
|
|
|
} catch (error) {
|
|
|
|
|
console.error('[World] 加载玩家模型预制体失败:', error);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 注册网络消息监听
|
|
|
|
|
*/
|
|
|
|
|
private registerNetworkListeners(): void {
|
|
|
|
|
const netManager = NetManager.getInstance();
|
|
|
|
|
|
|
|
|
|
// 监听玩家加入消息
|
|
|
|
|
netManager.listenMsg('PlayerJoin', (msg: MsgPlayerJoin) => {
|
|
|
|
|
this.onPlayerJoin(msg);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// 监听玩家移动消息
|
|
|
|
|
netManager.listenMsg('PlayerMove', (msg: MsgPlayerMove) => {
|
|
|
|
|
this.onPlayerMove(msg);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
console.log('[World] 网络消息监听注册完成');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 创建本地玩家
|
|
|
|
|
*/
|
|
|
|
|
private async createLocalPlayer(): Promise<void> {
|
|
|
|
|
if (!this.playerPrefab) {
|
|
|
|
|
console.error('[World] 玩家模型预制体未加载');
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2025-12-18 13:43:28 +08:00
|
|
|
const x = this.localPlayer.position.x / 1000
|
|
|
|
|
const y = this.localPlayer.position.y / 1000
|
|
|
|
|
|
2025-12-14 23:35:08 +08:00
|
|
|
// 实例化玩家节点
|
|
|
|
|
this.localPlayerNode = instantiate(this.playerPrefab);
|
|
|
|
|
this.localPlayerNode.name = `Player_${this.localPlayer.id}_Local`;
|
2025-12-18 16:04:56 +08:00
|
|
|
this.localPlayerNode.setPosition(x, y, 0);
|
2025-12-14 23:35:08 +08:00
|
|
|
this.worldRoot.addChild(this.localPlayerNode);
|
|
|
|
|
|
|
|
|
|
// 创建本地玩家控制器
|
|
|
|
|
this.localPlayerController = this.localPlayerNode.addComponent(PlayerController);
|
|
|
|
|
this.localPlayerController.init(this.localPlayer);
|
|
|
|
|
|
2025-12-18 13:25:04 +08:00
|
|
|
// 创建并绑定摄像机控制器(只有本地玩家需要)
|
|
|
|
|
this.cameraController = this.worldRoot.addComponent(CameraController) as CameraController;
|
|
|
|
|
this.cameraController.setTarget(this.localPlayerNode);
|
|
|
|
|
|
2025-12-14 23:35:08 +08:00
|
|
|
console.log('[World] 本地玩家创建完成:', this.localPlayer.name);
|
|
|
|
|
}
|
|
|
|
|
|
2025-12-18 16:04:56 +08:00
|
|
|
/**
|
|
|
|
|
* 创建其他已在线的玩家
|
|
|
|
|
*/
|
|
|
|
|
private async createOtherPlayers(otherPlayers?: PlayerInfo[]): Promise<void> {
|
|
|
|
|
if (!otherPlayers || otherPlayers.length === 0) {
|
|
|
|
|
console.log('[World] 无其他在线玩家');
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!this.playerPrefab) {
|
|
|
|
|
console.error('[World] 玩家模型预制体未加载,无法创建其他玩家');
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
console.log('[World] 开始创建其他在线玩家,数量:', otherPlayers.length);
|
|
|
|
|
|
|
|
|
|
for (const playerInfo of otherPlayers) {
|
|
|
|
|
// 跳过本地玩家(双重保险)
|
|
|
|
|
if (playerInfo.id === this.localPlayer.id) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 检查是否已存在
|
|
|
|
|
if (this.remotePlayers.has(playerInfo.id)) {
|
|
|
|
|
console.warn('[World] 远程玩家已存在:', playerInfo.id);
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
// 实例化玩家节点
|
|
|
|
|
const playerNode = instantiate(this.playerPrefab);
|
|
|
|
|
playerNode.name = `Player_${playerInfo.id}_Remote`;
|
|
|
|
|
const x = playerInfo.position.x / 1000;
|
|
|
|
|
const y = playerInfo.position.y / 1000;
|
|
|
|
|
playerNode.setPosition(x, y, 0);
|
|
|
|
|
this.worldRoot.addChild(playerNode);
|
|
|
|
|
|
|
|
|
|
// 创建远程玩家控制器
|
|
|
|
|
const remotePlayer = new RemotePlayer();
|
|
|
|
|
remotePlayer.init(playerNode, playerInfo.id, playerInfo.name, playerInfo.position);
|
|
|
|
|
|
|
|
|
|
this.remotePlayers.set(playerInfo.id, remotePlayer);
|
|
|
|
|
|
|
|
|
|
console.log('[World] 其他玩家创建完成:', playerInfo.name);
|
|
|
|
|
} catch (error) {
|
|
|
|
|
console.error('[World] 创建其他玩家失败:', playerInfo.name, error);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-12-14 23:35:08 +08:00
|
|
|
/**
|
|
|
|
|
* 处理玩家加入消息
|
|
|
|
|
*/
|
|
|
|
|
private onPlayerJoin(msg: MsgPlayerJoin): void {
|
|
|
|
|
console.log('[World] 玩家加入:', msg.playerName);
|
|
|
|
|
|
|
|
|
|
// 如果是本地玩家,不处理
|
|
|
|
|
if (msg.playerId === this.localPlayer.id) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 创建远程玩家
|
|
|
|
|
this.createRemotePlayer(msg);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 创建远程玩家
|
|
|
|
|
*/
|
|
|
|
|
private async createRemotePlayer(msg: MsgPlayerJoin): Promise<void> {
|
|
|
|
|
if (!this.playerPrefab) {
|
|
|
|
|
console.error('[World] 玩家模型预制体未加载');
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 检查是否已存在
|
|
|
|
|
if (this.remotePlayers.has(msg.playerId)) {
|
|
|
|
|
console.warn('[World] 远程玩家已存在:', msg.playerId);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 实例化玩家节点
|
|
|
|
|
const playerNode = instantiate(this.playerPrefab);
|
|
|
|
|
playerNode.name = `Player_${msg.playerId}_Remote`;
|
2025-12-18 16:04:56 +08:00
|
|
|
const x = msg.position.x / 1000
|
|
|
|
|
const y = msg.position.y / 1000
|
|
|
|
|
playerNode.setPosition(x, y, 0);
|
2025-12-14 23:35:08 +08:00
|
|
|
this.worldRoot.addChild(playerNode);
|
|
|
|
|
|
|
|
|
|
// 创建远程玩家控制器
|
|
|
|
|
const remotePlayer = new RemotePlayer();
|
|
|
|
|
remotePlayer.init(playerNode, msg.playerId, msg.playerName, msg.position);
|
|
|
|
|
|
|
|
|
|
this.remotePlayers.set(msg.playerId, remotePlayer);
|
|
|
|
|
|
|
|
|
|
console.log('[World] 远程玩家创建完成:', msg.playerName);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 处理玩家移动消息
|
|
|
|
|
*/
|
|
|
|
|
private onPlayerMove(msg: MsgPlayerMove): void {
|
|
|
|
|
// 如果是本地玩家,不处理
|
|
|
|
|
if (msg.playerId === this.localPlayer.id) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 更新远程玩家位置
|
|
|
|
|
const remotePlayer = this.remotePlayers.get(msg.playerId);
|
|
|
|
|
if (remotePlayer) {
|
|
|
|
|
remotePlayer.updatePosition(msg.position);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 获取本地玩家控制器
|
|
|
|
|
*/
|
|
|
|
|
public getLocalPlayerController(): PlayerController {
|
|
|
|
|
return this.localPlayerController;
|
|
|
|
|
}
|
|
|
|
|
|
2025-12-18 16:04:56 +08:00
|
|
|
/**
|
|
|
|
|
* 更新所有玩家信息显示
|
|
|
|
|
*/
|
|
|
|
|
public updateAllPlayersInfo(): void {
|
|
|
|
|
if (!this.uiGameInstance || !this.cameraController) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const uinode = this.uiGameInstance.getNode()
|
|
|
|
|
const playerDataList: PlayerInfoData[] = [];
|
|
|
|
|
|
|
|
|
|
// 添加本地玩家信息
|
|
|
|
|
if (this.localPlayerNode && this.localPlayer) {
|
|
|
|
|
// 直接使用节点的世界坐标
|
|
|
|
|
const worldPos = this.localPlayerNode.worldPosition;
|
|
|
|
|
// 正确的坐标转换流程:世界坐标 -> 世界摄像机坐标 -> UI摄像机坐标
|
|
|
|
|
const uiScreenPos = this.cameraController.worldToUICamera(worldPos, uinode);
|
|
|
|
|
|
|
|
|
|
playerDataList.push({
|
|
|
|
|
playerId: this.localPlayer.id,
|
|
|
|
|
playerName: this.localPlayer.name,
|
|
|
|
|
screenPosition: uiScreenPos,
|
|
|
|
|
realPosition: worldPos // 使用世界坐标作为真实位置
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 添加远程玩家信息
|
|
|
|
|
for (const [playerId, remotePlayer] of this.remotePlayers.entries()) {
|
|
|
|
|
const remotePlayerNode = remotePlayer.getPlayerNode();
|
|
|
|
|
if (remotePlayerNode) {
|
|
|
|
|
const worldPos = remotePlayerNode.worldPosition;
|
|
|
|
|
// 正确的坐标转换流程:世界坐标 -> 世界摄像机坐标 -> UI摄像机坐标
|
|
|
|
|
const uiScreenPos = this.cameraController.worldToUICamera(worldPos, uinode);
|
|
|
|
|
|
|
|
|
|
playerDataList.push({
|
|
|
|
|
playerId: playerId,
|
|
|
|
|
playerName: remotePlayer.getPlayerName(),
|
|
|
|
|
screenPosition: uiScreenPos,
|
|
|
|
|
realPosition: worldPos // 使用世界坐标作为真实位置
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 更新UI显示
|
|
|
|
|
this.uiGameInstance.updatePlayerInfo(playerDataList);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 在update中定期更新玩家信息显示
|
|
|
|
|
*/
|
|
|
|
|
public update(deltaTime: number): void {
|
|
|
|
|
// 每帧更新玩家信息显示
|
|
|
|
|
this.updateAllPlayersInfo();
|
|
|
|
|
}
|
|
|
|
|
|
2025-12-14 23:35:08 +08:00
|
|
|
/**
|
|
|
|
|
* 销毁世界
|
|
|
|
|
*/
|
|
|
|
|
public destroy(): void {
|
|
|
|
|
// 注意: TSRPC 的 listenMsg 不提供取消监听的方法
|
|
|
|
|
// 在实际使用中,监听会在连接断开时自动清除
|
|
|
|
|
|
|
|
|
|
// 销毁本地玩家
|
|
|
|
|
if (this.localPlayerNode) {
|
|
|
|
|
this.localPlayerNode.destroy();
|
|
|
|
|
this.localPlayerNode = null;
|
|
|
|
|
}
|
|
|
|
|
this.localPlayerController = null;
|
|
|
|
|
|
2025-12-18 13:25:04 +08:00
|
|
|
// 销毁摄像机控制器
|
|
|
|
|
if (this.cameraController) {
|
|
|
|
|
this.cameraController.node.removeComponent(CameraController);
|
|
|
|
|
this.cameraController = null;
|
|
|
|
|
}
|
|
|
|
|
|
2025-12-14 23:35:08 +08:00
|
|
|
// 销毁所有远程玩家
|
|
|
|
|
this.remotePlayers.forEach((remotePlayer) => {
|
|
|
|
|
remotePlayer.destroy();
|
|
|
|
|
});
|
|
|
|
|
this.remotePlayers.clear();
|
|
|
|
|
|
|
|
|
|
// 释放资源
|
|
|
|
|
if (this.playerPrefab) {
|
|
|
|
|
ResMgr.getInstance().release('resources', 'res://Actor/M1/M1');
|
|
|
|
|
this.playerPrefab = null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
this.worldRoot = null;
|
|
|
|
|
this.localPlayer = null;
|
|
|
|
|
|
|
|
|
|
console.log('[World] 世界已销毁');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 清理单例
|
|
|
|
|
*/
|
|
|
|
|
public static clear(): void {
|
|
|
|
|
if (World.instance) {
|
|
|
|
|
World.instance.destroy();
|
|
|
|
|
World.instance = null;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|