import { Label, dragonBones, instantiate } from 'cc'; import { find } from 'cc'; import { _decorator, Component, Node } from 'cc'; import { v3 } from 'cc'; import { Charactor } from './Charactor/Charactor'; import { GameEventEnum, GameSystemInputType } from './GameStruct'; import { CharactorAI } from './Charactor/CharactorAI'; import { JCMGO } from '../ThirdParty/JCMGO'; import { BuiltinMatchInfos } from './View/GameMatchView'; const { ccclass, property } = _decorator; @ccclass('GameMode') export class GameMode extends Component { protected onLoad(): void { //监听处理游戏开始 window.gm.node.on(GameEventEnum.GameStart, this.handleGameStart.bind(this)); //监听处理游戏结束 window.gm.node.on(GameEventEnum.GameEnd, this.handleGameEnd.bind(this)); //监听匹配结束 window.gm.node.on(GameEventEnum.MatchEnd, () => { //显示游戏角色 find("Canvas/CharactorGroup").active = true; }); } protected start(): void { this.initLines(); //显示匹配界面 find("Canvas-001/GameMatchView").active = true; } protected lateUpdate(): void { this.updateFollowCamera(); this.uploadInputs(); } handleGameStart() { this.displayReadyGo(); //打开游戏UI界面 find("Canvas-001/GameModeView").active = true; //ai判断 for (let i = 0; i < window.gm.state.aiFlags.length; i++) { if ( window.gm.state.aiFlags[i]) { this.getCharactorNode(i).addComponent(CharactorAI); } } } handleGameEnd() { //关闭游戏UI界面 find("Canvas-001/GameModeView").active = false; //打开结算界面 find("Canvas-001/GameSettleView").active = true; //关闭socket window.gm.socketPlayer.close(); } /**初始化-起跑线/终点 */ private initLines() { let startLine = find("Canvas/BandGroup/Start"); let endLine = instantiate(startLine); endLine.name = "End"; endLine.position = endLine.position.add(v3(window.gm.config.endPointDistance)); find("Canvas/BandGroup").addChild(endLine); } /**更新相机跟随 */ private updateFollowCamera() { let bgGroupNode = this.getBGGroupNode(); let cameraNode = this.getCameraNode(); let charactorNode = this.getCharactorNode(window.gm.state.myPlayerIndex); //相机跟随我控制的角色 cameraNode.position = cameraNode.position.set(charactorNode.position.x, cameraNode.position.y) //根据相机位置,动态补充背景 while (cameraNode.position.x >= bgGroupNode.children[1].position.x) { bgGroupNode.children[0].setPosition( bgGroupNode.children[0].position.add(v3(720)) ) bgGroupNode.children[0].setSiblingIndex(1); } } /**显示ReadyGo动画 */ private displayReadyGo() { let dp = find("Canvas-001/ReadyGoDisplay").getComponent(dragonBones.ArmatureDisplay); dp.node.active = true; dp.addEventListener(dragonBones.EventObject.COMPLETE, () => { dp.node.destroy(); }, this); window.gm.node.emit(GameEventEnum.ReadyGo); } //========================获取节点======================== private _cameraNode: Node; private getCameraNode() { if (!this._cameraNode) this._cameraNode = find("Canvas/Camera"); return this._cameraNode; } private _bgGroupNode: Node; private getBGGroupNode() { if (!this._bgGroupNode) { this._bgGroupNode = find("Canvas/BGGroup"); if (this._bgGroupNode.children.length == 1) { this._bgGroupNode.addChild(instantiate(this._bgGroupNode.children[0])) } } return this._bgGroupNode; } private _charactorGroup: Node; public getCharactor(index: number) { if (!this._charactorGroup) this._charactorGroup = find("Canvas/CharactorGroup") return this._charactorGroup.children[index].getComponent(Charactor); } public getCharactorNode(index: number) { if (!this._charactorGroup) this._charactorGroup = find("Canvas/CharactorGroup") return this._charactorGroup.children[index]; } //========================游戏逻辑处理(逻辑/渲染已分离)======================== /**处理进入房间 */ handleEnterRoom(data: JCMGO.RoomAddPlayerBst) { if (data.isSelf) window.gm.state.myPlayerIndex = data.playerIndex; } /**处理匹配完成 */ handleMatchComplete(data: JCMGO.RoomEndMatchingBst) { JCMGO.ExactMath.setSeed(data.timestamp); let aiInfoIndexes = []; for (let i = 0; i < BuiltinMatchInfos.aiPlayerInfos.length; i++) aiInfoIndexes.push(i); aiInfoIndexes.sort(() => JCMGO.ExactMath.sub(0.5, JCMGO.ExactMath.random())) window.gm.state.matchPlayerInfos = new Array(window.gm.config.maxMembers); for (let i = 0; i < window.gm.state.matchPlayerInfos.length; i++) { let playerInfo = data.roomInfo.playerInfos[i]; if (!playerInfo) { //这个索引位没有真人,就分配个ai playerInfo = BuiltinMatchInfos.aiPlayerInfos[aiInfoIndexes.shift()]; window.gm.state.aiFlags[i] = true; } else { window.gm.state.aiFlags[i] = false; } window.gm.state.matchPlayerInfos[i] = JCMGO.ObjectUtils.merge({}, playerInfo) as MatchPlayerInfo; } window.gm.node.emit(GameEventEnum.MatchEnd); } handleFrameSyncStart() { window.gm.state.isFrameSyncStarted = true; window.gm.node.emit(GameEventEnum.GameStart); } /**上传帧输入 */ public uploadInputs() { if (!window.gm.state.isFrameSyncStarted || window.gm.state.authGameSystemState.gameOver) return; //本地预测时间间隔(最终还是以服务器下发的为准) let nowTime = Date.now(); if (window.gm.state.lastUploadTime === 0) window.gm.state.lastUploadTime = nowTime; //模拟本地预测帧并更新状态 let moniFrame = window.gm.socketPlayer.moniServerFrame({sign: window.gm.state.lastSign.toString(), dt: nowTime - window.gm.state.lastUploadTime}); window.gm.state.lastUploadTime = nowTime; window.gm.state.localMoniFrames.push(moniFrame); this.handleInputs(window.gm.state.gameSystemState, moniFrame.inputs as GameSystemInput[], moniFrame.dt); //上传帧输入到服务器 window.gm.socketPlayer.uploadInputs(window.gm.state.lastSign.toString()); window.gm.state.lastSign++; //清除缓存的帧输入 window.gm.socketPlayer.clearInputs(); } /**处理服务器下发的帧数据 */ public handleRecvFrame(frame: JCMGO.Frame) { //更新权威状态 this.handleInputs(window.gm.state.authGameSystemState, frame.inputs as GameSystemInput[], frame.dt); //移除已经被服务器认证的本地帧 for (let i = window.gm.state.localMoniFrames.length - 1; i >= 0; i--) { let localFrame = window.gm.state.localMoniFrames[i]; if (parseInt(localFrame.sign) <= parseInt(frame.sign)) { window.gm.state.localMoniFrames.splice(i, 1); } } //当前状态回滚到权威状态 window.gm.state.gameSystemState = JCMGO.ObjectUtils.merge({}, window.gm.state.authGameSystemState) as GameSystemState; //预测并更新当前状态 window.gm.state.localMoniFrames.forEach((frame: JCMGO.Frame) => { this.handleInputs(window.gm.state.gameSystemState, frame.inputs as GameSystemInput[], frame.dt) }); // find("Canvas-001/DebugUI/Label").getComponent(Label).string = JSON.stringify( // window.gm.state.authGameSystemState.gameOver ? window.gm.state.authGameSystemState : window.gm.state.gameSystemState, // null, "\t"); } /**处理帧输入 */ private handleInputs(state: GameSystemState, inputs: GameSystemInput[], dt: number) { if (state.gameOver) return; //计时 state.time = JCMGO.ExactMath.add(state.time, dt); //判断readygo是否完成 if (!state.readyComplete) { if (state.time > 1600) { state.readyComplete = true; } else { return; } } //处理玩家输入 if (inputs instanceof Array) { inputs.forEach((input: GameSystemInput) => { if (input.type === GameSystemInputType.PlayerAcc) { let ps = state.playerStates.find(v => v.index === input.playerIndex); if (ps) { ps.speedX = JCMGO.ExactMath.add(ps.speedX, window.gm.config.acc); ps.lastAccTime = state.time; ps.accCount++; } return; } }); } //===========================处理游戏逻辑====================== //更新玩家状态 state.playerStates.forEach(ps => { //如果超过1秒没加速,就开始减速 if (JCMGO.ExactMath.sub(state.time, ps.lastAccTime) > window.gm.config.decGap) { ps.speedX = JCMGO.ExactMath.add( ps.speedX, JCMGO.ExactMath.mul(window.gm.config.dec, JCMGO.ExactMath.div(dt, 1000)) ); if (ps.speedX < 0) ps.speedX = 0; } //更新位置 ps.moveX = JCMGO.ExactMath.add( ps.moveX, JCMGO.ExactMath.mul(ps.speedX, JCMGO.ExactMath.div(dt, 1000)) ); }); //判断是否有玩家获胜,游戏结束 let winners: PlayerState[] = []; for (const ps of state.playerStates) { if (ps.moveX >= window.gm.config.endPointDistance) { winners.push(ps); } } if (winners.length > 0) { winners.sort((a, b) => b.moveX - a.moveX); state.winPlayerIndex = winners[0].index; state.gameOver = true; this.emitGameEnd(); } } private _emitedGameEnd = false; private emitGameEnd() { if (this._emitedGameEnd) return; if (window.gm.state.authGameSystemState.gameOver) { this._emitedGameEnd = true; window.gm.node.emit(GameEventEnum.GameEnd); } } }