接入ResLogin.otherPlayers
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
import { find } from "cc";
|
||||
import { BaseState } from "../../Framework/FSM/BaseState";
|
||||
import { UIMgr } from "../../Framework/UI/UIMgr";
|
||||
import { PlayerInfo } from "../../Shared/protocols/MsgResLogin";
|
||||
import { MsgResLogin, PlayerInfo } from "../../Shared/protocols/MsgResLogin";
|
||||
import { UIGame } from "../Game/UIGame";
|
||||
import { World } from "../Game/World";
|
||||
|
||||
@@ -14,6 +14,7 @@ import { World } from "../Game/World";
|
||||
* - 游戏主循环
|
||||
*/
|
||||
export class AppStatusGame extends BaseState {
|
||||
private _cmd: MsgResLogin
|
||||
private _player: PlayerInfo = null;
|
||||
private _isNewPlayer: boolean = false;
|
||||
private _uiGame: UIGame = null;
|
||||
@@ -25,10 +26,11 @@ export class AppStatusGame extends BaseState {
|
||||
/**
|
||||
* 进入游戏状态
|
||||
*/
|
||||
async onEnter(params?: any): Promise<void> {
|
||||
async onEnter(params?: MsgResLogin): Promise<void> {
|
||||
super.onEnter(params);
|
||||
|
||||
console.log("[AppStatusGame] 进入游戏世界");
|
||||
console.log("[AppStatusGame] 进入游戏世界", params);
|
||||
this._cmd = params
|
||||
|
||||
// 保存玩家信息
|
||||
if (params) {
|
||||
@@ -87,7 +89,7 @@ export class AppStatusGame extends BaseState {
|
||||
}
|
||||
|
||||
// 初始化世界,传入本地玩家信息
|
||||
await World.getInstance().init(worldRoot, this._player);
|
||||
await World.getInstance().init(worldRoot, this._player, this._cmd.otherPlayers);
|
||||
|
||||
console.log("[AppStatusGame] 游戏初始化完成");
|
||||
}
|
||||
@@ -117,7 +119,10 @@ export class AppStatusGame extends BaseState {
|
||||
* 更新游戏状态(每帧调用)
|
||||
*/
|
||||
onUpdate(dt: number): void {
|
||||
// TODO: 游戏主循环逻辑
|
||||
// 更新世界状态,包括玩家信息显示
|
||||
World.getInstance().update(dt);
|
||||
|
||||
// TODO: 其他游戏主循环逻辑
|
||||
// - 更新角色位置
|
||||
// - 检测碰撞
|
||||
// - 更新敌人AI
|
||||
|
||||
@@ -1,21 +1,28 @@
|
||||
import { Camera, Component, Node, Vec3, _decorator } from 'cc';
|
||||
import { _decorator, Camera, Component, find, Node, Vec3 } from 'cc';
|
||||
|
||||
const { ccclass, property } = _decorator;
|
||||
|
||||
/**
|
||||
* CameraController 摄像机控制器
|
||||
* 负责让摄像机跟随指定的目标物体
|
||||
* 负责让摄像机跟随指定的目标物体,并提供摄像机坐标转换功能
|
||||
* 支持世界摄像机和UI摄像机的概念
|
||||
*/
|
||||
@ccclass('CameraController')
|
||||
export class CameraController extends Component {
|
||||
/** 跟随目标 */
|
||||
private target: Node = null;
|
||||
|
||||
/** 摄像机节点 */
|
||||
private cameraNode: Node = null;
|
||||
/** 世界摄像机节点 (Main Camera) */
|
||||
private worldCameraNode: Node = null;
|
||||
|
||||
/** 摄像机组件 */
|
||||
private camera: Camera = null;
|
||||
/** 世界摄像机组件 */
|
||||
private worldCamera: Camera = null;
|
||||
|
||||
/** UI摄像机节点 (Canvas/Camera) */
|
||||
private uiCameraNode: Node = null;
|
||||
|
||||
/** UI摄像机组件 */
|
||||
private uiCamera: Camera = null;
|
||||
|
||||
/** 相对偏移量 */
|
||||
private offset: Vec3 = new Vec3(0, 10, 8);
|
||||
@@ -29,21 +36,49 @@ export class CameraController extends Component {
|
||||
public smoothFollow: boolean = true;
|
||||
|
||||
onLoad() {
|
||||
this.findMainCamera();
|
||||
this.findCameras();
|
||||
}
|
||||
|
||||
/**
|
||||
* 查找主摄像机
|
||||
* 查找所有摄像机
|
||||
*/
|
||||
private findMainCamera(): void {
|
||||
// 查找名为 "Main Camera" 的摄像机
|
||||
const mainCameraNode = this.node.scene.getChildByName('Main Camera');
|
||||
private findCameras(): void {
|
||||
this.findWorldCamera();
|
||||
this.findUICamera();
|
||||
}
|
||||
|
||||
/**
|
||||
* 查找世界摄像机 (Main Camera)
|
||||
*/
|
||||
private findWorldCamera(): void {
|
||||
// 查找名为 "Main Camera" 的世界摄像机
|
||||
const mainCameraNode = find('Main Camera', this.node.scene);
|
||||
if (mainCameraNode) {
|
||||
this.cameraNode = mainCameraNode;
|
||||
this.camera = mainCameraNode.getComponent(Camera);
|
||||
console.log('[CameraController] 找到主摄像机:', mainCameraNode.name);
|
||||
this.worldCameraNode = mainCameraNode;
|
||||
this.worldCamera = mainCameraNode.getComponent(Camera);
|
||||
console.log('[CameraController] 找到世界摄像机:', mainCameraNode.name);
|
||||
} else {
|
||||
console.error('[CameraController] 未找到主摄像机(Main Camera)');
|
||||
console.error('[CameraController] 未找到世界摄像机(Main Camera)');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 查找UI摄像机 (Canvas/Camera)
|
||||
*/
|
||||
private findUICamera(): void {
|
||||
// 查找Canvas下的Camera作为UI摄像机
|
||||
const canvasNode = find('Canvas', this.node.scene);
|
||||
if (canvasNode) {
|
||||
const uiCameraNode = canvasNode.getChildByName('Camera');
|
||||
if (uiCameraNode) {
|
||||
this.uiCameraNode = uiCameraNode;
|
||||
this.uiCamera = uiCameraNode.getComponent(Camera);
|
||||
console.log('[CameraController] 找到UI摄像机:', uiCameraNode.name);
|
||||
} else {
|
||||
console.error('[CameraController] 未找到Canvas下的Camera节点');
|
||||
}
|
||||
} else {
|
||||
console.error('[CameraController] 未找到Canvas节点');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -54,7 +89,7 @@ export class CameraController extends Component {
|
||||
public setTarget(target: Node): void {
|
||||
this.target = target;
|
||||
|
||||
if (this.target && this.cameraNode) {
|
||||
if (this.target && this.worldCameraNode) {
|
||||
// 立即设置初始位置
|
||||
this.updateCameraPosition(false);
|
||||
console.log('[CameraController] 设置跟随目标:', target.name);
|
||||
@@ -77,14 +112,35 @@ export class CameraController extends Component {
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取摄像机节点
|
||||
* 获取世界摄像机节点
|
||||
*/
|
||||
public getCameraNode(): Node {
|
||||
return this.cameraNode;
|
||||
public getWorldCameraNode(): Node {
|
||||
return this.worldCameraNode;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取世界摄像机组件
|
||||
*/
|
||||
public getWorldCamera(): Camera {
|
||||
return this.worldCamera;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取UI摄像机节点
|
||||
*/
|
||||
public getUICameraNode(): Node {
|
||||
return this.uiCameraNode;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取UI摄像机组件
|
||||
*/
|
||||
public getUICamera(): Camera {
|
||||
return this.uiCamera;
|
||||
}
|
||||
|
||||
update(deltaTime: number) {
|
||||
if (this.target && this.cameraNode) {
|
||||
if (this.target && this.worldCameraNode) {
|
||||
this.updateCameraPosition(this.smoothFollow, deltaTime);
|
||||
}
|
||||
}
|
||||
@@ -95,7 +151,7 @@ export class CameraController extends Component {
|
||||
* @param deltaTime 帧时间(smooth为true时需要)
|
||||
*/
|
||||
private updateCameraPosition(smooth: boolean = true, deltaTime: number = 0): void {
|
||||
if (!this.target || !this.cameraNode) {
|
||||
if (!this.target || !this.worldCameraNode) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -105,17 +161,17 @@ export class CameraController extends Component {
|
||||
|
||||
if (smooth && deltaTime > 0) {
|
||||
// 平滑跟随
|
||||
const currentPosition = this.cameraNode.worldPosition;
|
||||
const currentPosition = this.worldCameraNode.worldPosition;
|
||||
const newPosition = new Vec3();
|
||||
Vec3.lerp(newPosition, currentPosition, targetPosition, this.followSpeed * deltaTime);
|
||||
this.cameraNode.setWorldPosition(newPosition);
|
||||
this.worldCameraNode.setWorldPosition(newPosition);
|
||||
} else {
|
||||
// 立即跟随
|
||||
this.cameraNode.setWorldPosition(targetPosition);
|
||||
this.worldCameraNode.setWorldPosition(targetPosition);
|
||||
}
|
||||
|
||||
// 让摄像机看向目标
|
||||
this.cameraNode.lookAt(this.target.worldPosition);
|
||||
this.worldCameraNode.lookAt(this.target.worldPosition);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -126,13 +182,83 @@ export class CameraController extends Component {
|
||||
console.log('[CameraController] 停止跟随');
|
||||
}
|
||||
|
||||
/**
|
||||
* 将世界坐标转换为指定摄像机的屏幕坐标
|
||||
* @param worldPosition 世界坐标(Vec3)
|
||||
* @param camera 目标摄像机
|
||||
* @returns 摄像机坐标系下的屏幕坐标(Vec3),z为深度
|
||||
*/
|
||||
public worldToCameraPosition(worldPosition: Vec3, camera: Camera): Vec3 {
|
||||
if (!camera) {
|
||||
console.warn('[CameraController] 摄像机参数为空,无法进行坐标转换');
|
||||
return new Vec3(0, 0, 0);
|
||||
}
|
||||
|
||||
// 使用摄像机的worldToScreen方法将世界坐标转换为屏幕坐标
|
||||
const screenPos = new Vec3();
|
||||
camera.worldToScreen(worldPosition, screenPos);
|
||||
return screenPos;
|
||||
}
|
||||
|
||||
/**
|
||||
* 将摄像机屏幕坐标转换为指定Node的本地坐标
|
||||
* 计算流程:摄像机坐标 -> 世界坐标 -> Node本地坐标
|
||||
* @param screenPosition 摄像机屏幕坐标
|
||||
* @param sourceCamera 源摄像机
|
||||
* @param targetNode 目标Node节点
|
||||
* @returns 目标Node的本地坐标
|
||||
*/
|
||||
public CameraToNode(screenPosition: Vec3, sourceCamera: Camera, targetNode: Node): Vec3 {
|
||||
if (!sourceCamera || !targetNode) {
|
||||
console.warn('[CameraController] 摄像机或目标节点参数为空,无法进行坐标转换');
|
||||
return new Vec3(0, 0, 0);
|
||||
}
|
||||
|
||||
// 第一步:摄像机屏幕坐标转世界坐标
|
||||
const worldPosition = new Vec3();
|
||||
sourceCamera.screenToWorld(screenPosition, worldPosition);
|
||||
|
||||
// 第二步:世界坐标转Node本地坐标
|
||||
const localPosition = new Vec3();
|
||||
targetNode.inverseTransformPoint(localPosition, worldPosition);
|
||||
|
||||
return localPosition;
|
||||
}
|
||||
|
||||
/**
|
||||
* 将世界坐标转换为UI摄像机节点的本地坐标
|
||||
* @param worldPosition 世界坐标
|
||||
* @returns UI摄像机节点的本地坐标
|
||||
*/
|
||||
public worldToUICamera(worldPosition: Vec3, node: Node): Vec3 {
|
||||
if (!this.worldCamera || !this.uiCameraNode) {
|
||||
console.warn('[CameraController] 世界摄像机或UI摄像机节点未初始化');
|
||||
return new Vec3(0, 0, 0);
|
||||
}
|
||||
|
||||
// 第一步:世界坐标转世界摄像机屏幕坐标
|
||||
const worldCameraPos = this.worldToCameraPosition(worldPosition, this.worldCamera);
|
||||
|
||||
// 第二步:世界摄像机屏幕坐标转UI摄像机节点本地坐标
|
||||
const uiCameraLocalPos = this.CameraToNode(worldCameraPos, this.uiCamera, node);
|
||||
return uiCameraLocalPos;
|
||||
|
||||
// // 世界坐标转Node本地坐标
|
||||
// const localPosition = new Vec3();
|
||||
// node.inverseTransformPoint(localPosition, worldPosition);
|
||||
|
||||
return worldPosition;
|
||||
}
|
||||
|
||||
/**
|
||||
* 销毁控制器
|
||||
*/
|
||||
onDestroy(): void {
|
||||
this.target = null;
|
||||
this.cameraNode = null;
|
||||
this.camera = null;
|
||||
this.worldCameraNode = null;
|
||||
this.worldCamera = null;
|
||||
this.uiCameraNode = null;
|
||||
this.uiCamera = null;
|
||||
console.log('[CameraController] 摄像机控制器已销毁');
|
||||
}
|
||||
}
|
||||
@@ -43,7 +43,7 @@ export class PlayerController extends Component {
|
||||
this.playerInfo = playerInfo;
|
||||
const x = playerInfo.position.x / 1000
|
||||
const y = playerInfo.position.y / 1000
|
||||
this.lastSentPosition.set(x, 0, y);
|
||||
this.lastSentPosition.set(x, y, 0);
|
||||
|
||||
// 获取 RoleController 组件
|
||||
this.roleController = this.node.getComponentInChildren(RoleController);
|
||||
@@ -109,11 +109,11 @@ export class PlayerController extends Component {
|
||||
|
||||
// W - 向前
|
||||
if (this.keyStates.get(KeyCode.KEY_W)) {
|
||||
this.moveDirection.z -= 1;
|
||||
this.moveDirection.y += 1;
|
||||
}
|
||||
// S - 向后
|
||||
if (this.keyStates.get(KeyCode.KEY_S)) {
|
||||
this.moveDirection.z += 1;
|
||||
this.moveDirection.y -= 1;
|
||||
}
|
||||
// A - 向左
|
||||
if (this.keyStates.get(KeyCode.KEY_A)) {
|
||||
@@ -144,7 +144,7 @@ export class PlayerController extends Component {
|
||||
|
||||
// 更新朝向(让角色面向移动方向)
|
||||
if (this.moveDirection.lengthSqr() > 0) {
|
||||
const targetAngle = Math.atan2(this.moveDirection.x, -this.moveDirection.z) * (180 / Math.PI);
|
||||
const targetAngle = Math.atan2(this.moveDirection.x, -this.moveDirection.y) * (180 / Math.PI);
|
||||
this.node.setRotationFromEuler(0, targetAngle, 0);
|
||||
}
|
||||
|
||||
@@ -168,7 +168,7 @@ export class PlayerController extends Component {
|
||||
|
||||
// 如果移动距离超过阈值,发送移动请求
|
||||
if (distance >= this.moveSendThreshold) {
|
||||
this.sendMoveRequest(currentPos.x, currentPos.z);
|
||||
this.sendMoveRequest(currentPos.x, currentPos.y);
|
||||
this.lastSentPosition.set(currentPos);
|
||||
}
|
||||
}
|
||||
@@ -176,12 +176,12 @@ export class PlayerController extends Component {
|
||||
/**
|
||||
* 发送移动请求到服务器
|
||||
*/
|
||||
private async sendMoveRequest(x: number, z: number): Promise<void> {
|
||||
private async sendMoveRequest(x: number, y: number): Promise<void> {
|
||||
try {
|
||||
const netManager = NetManager.getInstance();
|
||||
const result = await netManager.callMsg(new MoveMessagePair(), {
|
||||
x: Math.round(x * 1000),
|
||||
y: Math.round(z * 1000)
|
||||
y: Math.round(y * 1000)
|
||||
});
|
||||
|
||||
if (!result) {
|
||||
@@ -194,8 +194,8 @@ export class PlayerController extends Component {
|
||||
const serverPos = result.position;
|
||||
const x = serverPos.x / 1000
|
||||
const y = serverPos.y / 1000
|
||||
this.node.setPosition(x, 0, y);
|
||||
this.lastSentPosition.set(x, 0, y);
|
||||
this.node.setPosition(x, y, 0);
|
||||
this.lastSentPosition.set(x, y, 0);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('[PlayerController] 发送移动请求异常:', error);
|
||||
|
||||
88
client/assets/scripts/App/Game/PlayerInfo.ts
Normal file
88
client/assets/scripts/App/Game/PlayerInfo.ts
Normal file
@@ -0,0 +1,88 @@
|
||||
import { _decorator, Component, Label, Vec3 } from 'cc';
|
||||
|
||||
const { ccclass, property } = _decorator;
|
||||
|
||||
export interface PlayerInfoData {
|
||||
/** 玩家ID */
|
||||
playerId: string;
|
||||
/** 玩家名称 */
|
||||
playerName: string;
|
||||
/** 屏幕坐标位置(用于设置UI位置) */
|
||||
screenPosition: Vec3;
|
||||
/** 真实位置(用于显示到文本中) */
|
||||
realPosition: Vec3;
|
||||
}
|
||||
|
||||
/**
|
||||
* PlayerInfo 玩家信息显示组件
|
||||
* 显示玩家名称和坐标信息
|
||||
*/
|
||||
@ccclass('PlayerInfo')
|
||||
export class PlayerInfo extends Component {
|
||||
/** 文本显示组件 */
|
||||
private textLabel: Label = null;
|
||||
|
||||
/** 当前玩家数据 */
|
||||
private playerData: PlayerInfoData = null;
|
||||
|
||||
onLoad() {
|
||||
// 查找text子节点的Label组件
|
||||
const textNode = this.node.getChildByName('text');
|
||||
if (textNode) {
|
||||
this.textLabel = textNode.getComponent(Label);
|
||||
if (!this.textLabel) {
|
||||
console.error('[PlayerInfo] text子节点没有Label组件');
|
||||
}
|
||||
} else {
|
||||
console.error('[PlayerInfo] 找不到text子节点');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新玩家信息
|
||||
* @param playerData 玩家数据
|
||||
*/
|
||||
public updatePlayerInfo(playerData: PlayerInfoData): void {
|
||||
this.playerData = playerData;
|
||||
|
||||
if (!this.textLabel) {
|
||||
console.error('[PlayerInfo] 文本组件未初始化');
|
||||
return;
|
||||
}
|
||||
|
||||
// 格式化显示文本:玩家名和真实坐标信息
|
||||
const displayText = `${playerData.playerName}\n坐标: (${Math.round(playerData.realPosition.x)}, ${Math.round(playerData.realPosition.y)})`;
|
||||
this.textLabel.string = displayText;
|
||||
|
||||
// 设置UI位置为屏幕坐标
|
||||
this.node.setPosition(playerData.screenPosition);
|
||||
|
||||
//console.log(`[PlayerInfo] 更新玩家信息: ${playerData.playerName}, 屏幕坐标: (${playerData.screenPosition.x}, ${playerData.screenPosition.y}), 真实坐标: (${playerData.realPosition.x}, ${playerData.realPosition.y})`);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前玩家数据
|
||||
*/
|
||||
public getPlayerData(): PlayerInfoData {
|
||||
return this.playerData;
|
||||
}
|
||||
|
||||
/**
|
||||
* 隐藏玩家信息
|
||||
*/
|
||||
public hide(): void {
|
||||
this.node.active = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 显示玩家信息
|
||||
*/
|
||||
public show(): void {
|
||||
this.node.active = true;
|
||||
}
|
||||
|
||||
onDestroy() {
|
||||
this.textLabel = null;
|
||||
this.playerData = null;
|
||||
}
|
||||
}
|
||||
9
client/assets/scripts/App/Game/PlayerInfo.ts.meta
Normal file
9
client/assets/scripts/App/Game/PlayerInfo.ts.meta
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"ver": "4.0.24",
|
||||
"importer": "typescript",
|
||||
"imported": true,
|
||||
"uuid": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
|
||||
"files": [],
|
||||
"subMetas": {},
|
||||
"userData": {}
|
||||
}
|
||||
@@ -40,8 +40,8 @@ export class RemotePlayer {
|
||||
this.playerName = playerName;
|
||||
const x = position.x / 1000
|
||||
const y = position.y / 1000
|
||||
this.currentPosition.set(x, 0, y);
|
||||
this.targetPosition.set(x, 0, y);
|
||||
this.currentPosition.set(x, y, 0);
|
||||
this.targetPosition.set(x, y, 0);
|
||||
|
||||
// 获取 RoleController 组件
|
||||
this.roleController = this.playerNode.getComponentInChildren(RoleController);
|
||||
@@ -79,7 +79,7 @@ export class RemotePlayer {
|
||||
|
||||
// 计算朝向
|
||||
if (distance > 0.01) {
|
||||
const targetAngle = Math.atan2(direction.x, -direction.z) * (180 / Math.PI);
|
||||
const targetAngle = Math.atan2(direction.x, -direction.y) * (180 / Math.PI);
|
||||
this.playerNode.setRotationFromEuler(0, targetAngle, 0);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
import { _decorator } from 'cc';
|
||||
import { _decorator, instantiate, Node, Prefab } from 'cc';
|
||||
import { ResMgr } from '../../Framework/ResMgr/ResMgr';
|
||||
import { UIBase } from '../../Framework/UI/UIBase';
|
||||
import { PlayerInfo, PlayerInfoData } from './PlayerInfo';
|
||||
|
||||
const { ccclass, property } = _decorator;
|
||||
|
||||
@@ -9,21 +11,22 @@ const { ccclass, property } = _decorator;
|
||||
*/
|
||||
@ccclass('UIGame')
|
||||
export class UIGame extends UIBase {
|
||||
/** 玩家信息显示容器 */
|
||||
private playerInfoContainer: Node = null;
|
||||
|
||||
protected onLoad(): void {
|
||||
console.log('[UIGame] onLoad');
|
||||
/** 玩家信息预制体 */
|
||||
private playerInfoPrefab: Prefab = null;
|
||||
|
||||
/** 当前显示的玩家信息组件Map<playerId, PlayerInfo> */
|
||||
private playerInfoComponents: Map<string, PlayerInfo> = new Map();
|
||||
|
||||
async onStart() {
|
||||
this.initPlayerInfoContainer();
|
||||
await this.loadPlayerInfoPrefab();
|
||||
}
|
||||
|
||||
protected onEnable(): void {
|
||||
console.log('[UIGame] onEnable');
|
||||
}
|
||||
|
||||
protected onDisable(): void {
|
||||
console.log('[UIGame] onDisable');
|
||||
}
|
||||
|
||||
protected onDestroy(): void {
|
||||
console.log('[UIGame] onDestroy');
|
||||
onEnd(): void {
|
||||
this.clearPlayerInfoComponents();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -33,4 +36,95 @@ export class UIGame extends UIBase {
|
||||
return 'res://UI/Game/UIGame';
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化玩家信息容器
|
||||
*/
|
||||
private initPlayerInfoContainer(): void {
|
||||
// 查找或创建玩家信息容器
|
||||
this.playerInfoContainer = this._node.getChildByName('PlayerInfoContainer');
|
||||
if (!this.playerInfoContainer) {
|
||||
this.playerInfoContainer = new Node('PlayerInfoContainer');
|
||||
this._node.addChild(this.playerInfoContainer);
|
||||
}
|
||||
console.log('[UIGame] 玩家信息容器已初始化');
|
||||
}
|
||||
|
||||
/**
|
||||
* 加载玩家信息预制体
|
||||
*/
|
||||
private async loadPlayerInfoPrefab(): Promise<void> {
|
||||
try {
|
||||
this.playerInfoPrefab = await ResMgr.getInstance().load('res', 'UI/Game/PlayerInfo', Prefab);
|
||||
console.log('[UIGame] 玩家信息预制体加载成功');
|
||||
} catch (error) {
|
||||
console.error('[UIGame] 加载玩家信息预制体失败:', error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新玩家信息显示
|
||||
* @param playerDataList 玩家数据列表
|
||||
*/
|
||||
public updatePlayerInfo(playerDataList: PlayerInfoData[]): void {
|
||||
if (!this.playerInfoPrefab || !this.playerInfoContainer) {
|
||||
console.error('[UIGame] 玩家信息预制体或容器未准备好');
|
||||
return;
|
||||
}
|
||||
|
||||
// 记录当前更新的玩家ID
|
||||
const currentPlayerIds = new Set<string>();
|
||||
|
||||
for (const playerData of playerDataList) {
|
||||
currentPlayerIds.add(playerData.playerId);
|
||||
|
||||
let playerInfoComponent = this.playerInfoComponents.get(playerData.playerId);
|
||||
|
||||
if (!playerInfoComponent) {
|
||||
// 创建新的玩家信息组件
|
||||
const playerInfoNode = instantiate(this.playerInfoPrefab);
|
||||
this.playerInfoContainer.addChild(playerInfoNode);
|
||||
|
||||
playerInfoComponent = playerInfoNode.getComponent(PlayerInfo);
|
||||
if (!playerInfoComponent) {
|
||||
playerInfoComponent = playerInfoNode.addComponent(PlayerInfo);
|
||||
}
|
||||
|
||||
this.playerInfoComponents.set(playerData.playerId, playerInfoComponent);
|
||||
console.log('[UIGame] 创建玩家信息组件:', playerData.playerName);
|
||||
}
|
||||
|
||||
// 更新玩家信息
|
||||
playerInfoComponent.updatePlayerInfo(playerData);
|
||||
}
|
||||
|
||||
// 移除不再存在的玩家信息
|
||||
for (const [playerId, playerInfoComponent] of this.playerInfoComponents.entries()) {
|
||||
if (!currentPlayerIds.has(playerId)) {
|
||||
playerInfoComponent.node.destroy();
|
||||
this.playerInfoComponents.delete(playerId);
|
||||
console.log('[UIGame] 移除玩家信息组件:', playerId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 清理所有玩家信息组件
|
||||
*/
|
||||
private clearPlayerInfoComponents(): void {
|
||||
for (const [playerId, playerInfoComponent] of this.playerInfoComponents.entries()) {
|
||||
if (playerInfoComponent && playerInfoComponent.node) {
|
||||
playerInfoComponent.node.destroy();
|
||||
}
|
||||
}
|
||||
this.playerInfoComponents.clear();
|
||||
|
||||
// 释放预制体资源
|
||||
if (this.playerInfoPrefab) {
|
||||
ResMgr.getInstance().release('resources', 'res://UI/Game/PlayerInfo');
|
||||
this.playerInfoPrefab = null;
|
||||
}
|
||||
|
||||
console.log('[UIGame] 已清理所有玩家信息组件');
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,12 +1,15 @@
|
||||
import { instantiate, Node, Prefab } from 'cc';
|
||||
import { NetManager } from '../../Framework/Net/NetManager';
|
||||
import { ResMgr } from '../../Framework/ResMgr/ResMgr';
|
||||
import { UIMgr } from '../../Framework/UI/UIMgr';
|
||||
import { MsgPlayerJoin } from '../../Shared/protocols/MsgPlayerJoin';
|
||||
import { MsgPlayerMove } from '../../Shared/protocols/MsgPlayerMove';
|
||||
import { PlayerInfo } from '../../Shared/protocols/MsgResLogin';
|
||||
import { CameraController } from './CameraController';
|
||||
import { PlayerController } from './PlayerController';
|
||||
import { PlayerInfoData } from './PlayerInfo';
|
||||
import { RemotePlayer } from './RemotePlayer';
|
||||
import { UIGame } from './UIGame';
|
||||
|
||||
/**
|
||||
* World 世界管理器
|
||||
@@ -37,6 +40,9 @@ export class World {
|
||||
/** 摄像机控制器 */
|
||||
private cameraController: CameraController = null;
|
||||
|
||||
/** UIGame实例 */
|
||||
private uiGameInstance: UIGame = null;
|
||||
|
||||
private constructor() { }
|
||||
|
||||
public static getInstance(): World {
|
||||
@@ -50,11 +56,15 @@ export class World {
|
||||
* 初始化世界
|
||||
* @param worldRoot 世界根节点
|
||||
* @param localPlayer 本地玩家信息
|
||||
* @param otherPlayers 其他在线玩家信息
|
||||
*/
|
||||
public async init(worldRoot: Node, localPlayer: PlayerInfo): Promise<void> {
|
||||
public async init(worldRoot: Node, localPlayer: PlayerInfo, otherPlayers?: PlayerInfo[]): Promise<void> {
|
||||
this.worldRoot = worldRoot;
|
||||
this.localPlayer = localPlayer;
|
||||
|
||||
// 获取UIGame实例
|
||||
this.uiGameInstance = UIMgr.getInstance().get(UIGame) as UIGame;
|
||||
|
||||
// 加载玩家模型预制体
|
||||
await this.loadPlayerPrefab();
|
||||
|
||||
@@ -64,6 +74,9 @@ export class World {
|
||||
// 创建本地玩家
|
||||
await this.createLocalPlayer();
|
||||
|
||||
// 创建其他已在线的玩家
|
||||
await this.createOtherPlayers(otherPlayers);
|
||||
|
||||
console.log('[World] 世界初始化完成');
|
||||
}
|
||||
|
||||
@@ -113,7 +126,7 @@ export class World {
|
||||
// 实例化玩家节点
|
||||
this.localPlayerNode = instantiate(this.playerPrefab);
|
||||
this.localPlayerNode.name = `Player_${this.localPlayer.id}_Local`;
|
||||
this.localPlayerNode.setPosition(x, 0, y);
|
||||
this.localPlayerNode.setPosition(x, y, 0);
|
||||
this.worldRoot.addChild(this.localPlayerNode);
|
||||
|
||||
// 创建本地玩家控制器
|
||||
@@ -127,6 +140,56 @@ export class World {
|
||||
console.log('[World] 本地玩家创建完成:', this.localPlayer.name);
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建其他已在线的玩家
|
||||
*/
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理玩家加入消息
|
||||
*/
|
||||
@@ -160,7 +223,9 @@ export class World {
|
||||
// 实例化玩家节点
|
||||
const playerNode = instantiate(this.playerPrefab);
|
||||
playerNode.name = `Player_${msg.playerId}_Remote`;
|
||||
playerNode.setPosition(msg.position.x, 0, msg.position.y);
|
||||
const x = msg.position.x / 1000
|
||||
const y = msg.position.y / 1000
|
||||
playerNode.setPosition(x, y, 0);
|
||||
this.worldRoot.addChild(playerNode);
|
||||
|
||||
// 创建远程玩家控制器
|
||||
@@ -195,6 +260,61 @@ export class World {
|
||||
return this.localPlayerController;
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新所有玩家信息显示
|
||||
*/
|
||||
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();
|
||||
}
|
||||
|
||||
/**
|
||||
* 销毁世界
|
||||
*/
|
||||
|
||||
@@ -97,10 +97,7 @@ export class UILogin extends UIBase {
|
||||
|
||||
// 登录成功,切换到游戏状态
|
||||
const appStatusManager = AppStatusManager.getInstance();
|
||||
appStatusManager.changeState('Game', {
|
||||
player: result.player,
|
||||
isNewPlayer: result.isNewPlayer || false
|
||||
});
|
||||
appStatusManager.changeState('Game', result);
|
||||
|
||||
// 隐藏登录界面
|
||||
this.hide();
|
||||
|
||||
Reference in New Issue
Block a user