import { _decorator, Component, Node, Prefab, instantiate, SpriteFrame, director } from "cc"; import { ActorManager } from "../Entity/Actor/ActorManager"; import DataManager from "../Global/DataManager"; import { JoyStickManager } from "../UI/JoyStickManager"; import { ResourceManager } from "../Global/ResourceManager"; import { EventEnum, PrefabPathEnum, SceneEnum, TexturePathEnum } from "../Enum"; import NetworkManager from "../Global/NetworkManager"; import ObjectPoolManager from "../Global/ObjectPoolManager"; import { BulletManager } from "../Entity/Bullet/BulletManager"; import { ApiMsgEnum, EntityTypeEnum, IClientInput, IMsgServerSync, InputTypeEnum, toFixed } from "../Common"; import EventManager from "../Global/EventManager"; import { deepClone } from "../Utils"; const { ccclass } = _decorator; @ccclass("BattleManager") export class BattleManager extends Component { private stage: Node; private ui: Node; private shouldUpdate = false; private pendingMsg = []; async start() { //清空 this.clearGame(); //资源加载和网络连接同步执行 await Promise.all([this.loadRes(), this.connectServer()]); this.initGame(); // 在场景初始化完毕之前,卡主别的玩家,准备好以后再告知服务器,等所有玩家都准备好以后才开始,这里就不做了 NetworkManager.Instance.listenMsg(ApiMsgEnum.MsgServerSync, this.listenServerSync, this); NetworkManager.Instance.listenMsg(ApiMsgEnum.MsgGameEnd, this.listenGameEnd, this); EventManager.Instance.on(EventEnum.ClientSync, this.handleClientSync, this); EventManager.Instance.on(EventEnum.GameEnd, this.handleGameEnd, this); } clearGame() { //事件 NetworkManager.Instance.unlistenMsg(ApiMsgEnum.MsgServerSync, this.listenServerSync, this); NetworkManager.Instance.unlistenMsg(ApiMsgEnum.MsgGameEnd, this.listenGameEnd, this); EventManager.Instance.off(EventEnum.ClientSync, this.handleClientSync, this); EventManager.Instance.off(EventEnum.GameEnd, this.handleGameEnd, this); //数据 this.shouldUpdate = false; ObjectPoolManager.Instance.reset(); DataManager.Instance.reset(); //节点 this.stage = DataManager.Instance.stage = this.node.getChildByName("Stage"); this.ui = this.node.getChildByName("UI"); this.stage.destroyAllChildren(); this.ui.destroyAllChildren(); } async loadRes() { const list = []; for (const type in PrefabPathEnum) { const p = ResourceManager.Instance.loadRes(PrefabPathEnum[type], Prefab).then((prefab) => { DataManager.Instance.prefabMap.set(type, prefab); }); list.push(p); } for (const type in TexturePathEnum) { const p = ResourceManager.Instance.loadDir(TexturePathEnum[type], SpriteFrame).then((spriteFrames) => { DataManager.Instance.textureMap.set(type, spriteFrames); }); list.push(p); } await Promise.all(list); } async connectServer() { if (!(await NetworkManager.Instance.connect().catch(() => false))) { await new Promise((resolve) => setTimeout(resolve, 1000)); await this.connectServer(); } } async initGame() { this.initJoyStick(); this.initShoot(); this.initMap(); this.shouldUpdate = true; } initJoyStick() { const prefab = DataManager.Instance.prefabMap.get(EntityTypeEnum.JoyStick); const joySitck = instantiate(prefab); joySitck.setParent(this.ui); const jm = (DataManager.Instance.jm = joySitck.getComponent(JoyStickManager)); jm.init(); } initShoot() { const prefab = DataManager.Instance.prefabMap.get(EntityTypeEnum.Shoot); const shoot = instantiate(prefab); shoot.setParent(this.ui); } initMap() { const prefab = DataManager.Instance.prefabMap.get(EntityTypeEnum.Map1); const map = instantiate(prefab); map.setParent(this.stage); } update(dt: number) { if (!this.shouldUpdate) { return; } this.render(); this.tick(dt); } tick(dt: number) { this.tickPlayer(dt); // this.tickGlobal(dt) } tickPlayer(dt: number) { for (const p of DataManager.Instance.state.actors) { const playerManager = DataManager.Instance.actorMap.get(p.id); if (!playerManager) { return; } playerManager.tick(dt); } } // tickGlobal(dt: number) { // DataManager.Instance.applyInput({ // type: InputTypeEnum.TimePast, // dt: toFixed(dt), // }) // } render() { this.renderPlayer(); this.renderBullet(); } renderPlayer() { for (const p of DataManager.Instance.state.actors) { let actorManager = DataManager.Instance.actorMap.get(p.id); if (!actorManager) { const playerPrefab = DataManager.Instance.prefabMap.get(p.type); const player = instantiate(playerPrefab); player.setParent(this.stage); actorManager = player.addComponent(ActorManager); DataManager.Instance.actorMap.set(p.id, actorManager); actorManager.init(p); } else { actorManager.render(p); } } } renderBullet() { for (const b of DataManager.Instance.state.bullets) { let bulletManager = DataManager.Instance.bulletMap.get(b.id); if (!bulletManager) { const bullet = ObjectPoolManager.Instance.get(b.type); bulletManager = bullet.getComponent(BulletManager) || bullet.addComponent(BulletManager); DataManager.Instance.bulletMap.set(b.id, bulletManager); bulletManager.init(b); } else { bulletManager.render(b); } } } handleClientSync(input: IClientInput) { const msg = { frameId: DataManager.Instance.frameId++, input, }; NetworkManager.Instance.sendMsg(ApiMsgEnum.MsgClientSync, msg); //移动才做预测,射击不做 if (input.type === InputTypeEnum.ActorMove) { DataManager.Instance.applyInput(input); this.pendingMsg.push(msg); } } listenServerSync({ lastFrameId, inputs }: IMsgServerSync) { //回滚上次服务器状态 DataManager.Instance.state = DataManager.Instance.lastState; //应用服务器输入 for (const input of inputs) { DataManager.Instance.applyInput(input); } //记录最新的服务器状态 DataManager.Instance.lastState = deepClone(DataManager.Instance.state); //过滤本地输入 this.pendingMsg = this.pendingMsg.filter((msg) => msg.frameId > lastFrameId); //应用本地输入 for (const msg of this.pendingMsg) { DataManager.Instance.applyInput(msg.input); } } async handleGameEnd() { const { success, res, error } = await NetworkManager.Instance.callApi(ApiMsgEnum.ApiGameEnd, { rid: DataManager.Instance.roomInfo.id }); if (!success) { console.log(error); return; } } listenGameEnd() { this.clearGame(); director.loadScene(SceneEnum.Hall); } }