diff --git a/examples/cocos-creator-multiplayer/backend/src/index.ts b/examples/cocos-creator-multiplayer/backend/src/index.ts index 76d656e..8c89268 100644 --- a/examples/cocos-creator-multiplayer/backend/src/index.ts +++ b/examples/cocos-creator-multiplayer/backend/src/index.ts @@ -2,7 +2,6 @@ import 'k8w-extend-native'; import * as path from "path"; import { WsConnection, WsServer } from "tsrpc"; import { Room } from './models/Room'; -import { gameConfig } from './shared/game/gameConfig'; import { serviceProto, ServiceType } from './shared/protocols/serviceProto'; // Create the Server @@ -19,19 +18,7 @@ server.flows.postDisconnectFlow.push(v => { } return v; -}) - -// 模拟网络延迟 -if (gameConfig.networkLag) { - server.flows.preRecvDataFlow.push(async v => { - await new Promise(rs => { setTimeout(rs, gameConfig.networkLag) }) - return v; - }) - server.flows.preSendDataFlow.push(async v => { - await new Promise(rs => { setTimeout(rs, gameConfig.networkLag) }) - return v; - }) -} +}); export const roomInstance = new Room(server); diff --git a/examples/cocos-creator-multiplayer/backend/src/shared/game/GameSystem.ts b/examples/cocos-creator-multiplayer/backend/src/shared/game/GameSystem.ts index e73064b..b90dd89 100644 --- a/examples/cocos-creator-multiplayer/backend/src/shared/game/GameSystem.ts +++ b/examples/cocos-creator-multiplayer/backend/src/shared/game/GameSystem.ts @@ -43,12 +43,14 @@ export class GameSystem { else if (input.type === 'PlayerAttack') { let player = this._state.players.find(v => v.id === input.playerId); if (player) { - this._state.arrows.push({ + let newArrow: ArrowState = { id: this._state.nextArrowId++, fromPlayerId: input.playerId, targetPos: { ...input.targetPos }, targetTime: input.targetTime - }); + }; + this._state.arrows.push(newArrow); + this.onNewArrow.forEach(v => v(newArrow)); } } else if (input.type === 'PlayerJoin') { @@ -69,11 +71,6 @@ export class GameSystem { if (arrow.targetTime <= this._state.now) { // 伤害判定 let damagedPlayers = this._state.players.filter(v => { - // 不能伤害自己 - // if (v.id === arrow.fromPlayerId) { - // return false; - // } - return (v.pos.x - arrow.targetPos.x) * (v.pos.x - arrow.targetPos.x) + (v.pos.y - arrow.targetPos.y) * (v.pos.y - arrow.targetPos.y) <= gameConfig.arrowAttackRadius * gameConfig.arrowAttackRadius }); damagedPlayers.forEach(p => { @@ -81,11 +78,8 @@ export class GameSystem { p.dizzyEndTime = this._state.now + gameConfig.arrowDizzyTime; // Event - this.onDamage.forEach(h => h({ - fromPlayerId: arrow.fromPlayerId, - toPlayerId: p.id - })) }) + this._state.arrows.splice(i, 1); } } @@ -93,7 +87,7 @@ export class GameSystem { } // Events (Game Push) - onDamage: ((e: { fromPlayerId: number, toPlayerId: number }) => void)[] = []; + onNewArrow: ((arrow: ArrowState) => void)[] = []; } diff --git a/examples/cocos-creator-multiplayer/backend/src/shared/game/gameConfig.ts b/examples/cocos-creator-multiplayer/backend/src/shared/game/gameConfig.ts index 5b3fef2..7f1829b 100644 --- a/examples/cocos-creator-multiplayer/backend/src/shared/game/gameConfig.ts +++ b/examples/cocos-creator-multiplayer/backend/src/shared/game/gameConfig.ts @@ -1,7 +1,5 @@ export const gameConfig = { syncRate: 10, - // 网络延迟(同时在服务端和客户端生效,所以实际是双倍延迟) - networkLag: 200, // 攻击技能的冷却时间(毫秒) attackCD: 1000, diff --git a/examples/cocos-creator-multiplayer/frontend/assets/prefabs/Arrow/Arrow.ts b/examples/cocos-creator-multiplayer/frontend/assets/prefabs/Arrow/Arrow.ts index 4fc551c..cad1271 100644 --- a/examples/cocos-creator-multiplayer/frontend/assets/prefabs/Arrow/Arrow.ts +++ b/examples/cocos-creator-multiplayer/frontend/assets/prefabs/Arrow/Arrow.ts @@ -1,6 +1,7 @@ import { Component, Vec3, _decorator } from 'cc'; import { MathUtil } from '../../scripts/models/MathUtil'; +import { gameConfig } from '../../scripts/shared/game/gameConfig'; import { ArrowState } from '../../scripts/shared/game/state/ArrowState'; const { ccclass, property } = _decorator; @@ -27,7 +28,8 @@ export class Arrow extends Component { this._startPos.set(startPos); this._endPos.set(state.targetPos.x, 0, -state.targetPos.y); this._startTime = Date.now(); - this._endTime = this._startTime + state.targetTime - now; + // 箭的展示与判定相分离,展示就按固定的飞行时长展示 + this._endTime = this._startTime + gameConfig.arrowFlyTime; this._updatePosAndForward(0); } @@ -36,6 +38,10 @@ export class Arrow extends Component { //下一个目标位置 let percent = MathUtil.limit((Date.now() - this._startTime) / (this._endTime - this._startTime), 0, 1); this._updatePosAndForward(percent); + + if (percent >= 1) { + this.node.removeFromParent(); + } } private _updatePosAndForward(percent: number) { diff --git a/examples/cocos-creator-multiplayer/frontend/assets/scenes/GameScene/GameScene.ts b/examples/cocos-creator-multiplayer/frontend/assets/scenes/GameScene/GameScene.ts index 8356c1d..32832d5 100644 --- a/examples/cocos-creator-multiplayer/frontend/assets/scenes/GameScene/GameScene.ts +++ b/examples/cocos-creator-multiplayer/frontend/assets/scenes/GameScene/GameScene.ts @@ -6,6 +6,7 @@ import { Player } from '../../prefabs/Player/Player'; import { FollowCamera } from '../../scripts/components/FollowCamera'; import { GameManager } from '../../scripts/models/GameManager'; import { gameConfig } from '../../scripts/shared/game/gameConfig'; +import { ArrowState } from '../../scripts/shared/game/state/ArrowState'; const { ccclass, property } = _decorator; /** @@ -41,8 +42,8 @@ export class GameScene extends Component { gameManager!: GameManager; - private _playerInstances: { [playerId: number]: Player } = {}; - private _arrowInstances: { [arrowId: number]: Arrow } = {}; + private _playerInstances: { [playerId: number]: Player | undefined } = {}; + private _arrowInstances: { [arrowId: number]: Arrow | undefined } = {}; private _selfSpeed?: Vec2 = new Vec2(0, 0); onLoad() { @@ -61,10 +62,18 @@ export class GameScene extends Component { } this.gameManager = new GameManager(); + + // 监听数据状态事件 + this.gameManager.gameSystem.onNewArrow.push(v => { this._onNewArrow(v) }); + + // 断线一秒后重连 this.gameManager.client.flows.postDisconnectFlow.push(v => { - location.reload() + setTimeout(() => { + this.gameManager.join(); + }, 2000) return v; }); + this.gameManager.join(); } @@ -85,14 +94,13 @@ export class GameScene extends Component { } this._updatePlayers(); - this._updateArrows(); } private _updatePlayers() { // Update pos let playerStates = this.gameManager.state.players; for (let playerState of playerStates) { - let player: Player = this._playerInstances[playerState.id]; + let player = this._playerInstances[playerState.id]; if (!player) { let node = instantiate(this.prefabPlayer); this.players.addChild(node); @@ -119,33 +127,27 @@ export class GameScene extends Component { } } - private _updateArrows() { - // Update pos - let arrowStates = this.gameManager.state.arrows; - for (let arrowState of arrowStates) { - let arrow: Arrow = this._arrowInstances[arrowState.id]; - if (!arrow) { - let playerState = this.gameManager.state.players.find(v => v.id === arrowState.fromPlayerId); - if (!playerState) { - continue; - } - let playerNode = this._playerInstances[playerState.id].node; - - let node = instantiate(this.prefabArrow); - this.arrows.addChild(node); - arrow = this._arrowInstances[arrowState.id] = node.getComponent(Arrow)!; - arrow.init(arrowState, playerNode.position, this.gameManager.state.now); - } + private _onNewArrow(arrowState: ArrowState) { + let arrow = this._arrowInstances[arrowState.id]; + // 已经存在 + if (arrow) { + return; } - // Clear left players - for (let i = this.arrows.children.length - 1; i > -1; --i) { - let arrow = this.arrows.children[i].getComponent(Arrow)!; - if (!this.gameManager.state.arrows.find(v => v.id === arrow.id)) { - arrow.node.removeFromParent(); - delete this._arrowInstances[arrow.id]; - } + let playerState = this.gameManager.state.players.find(v => v.id === arrowState.fromPlayerId); + if (!playerState) { + return; } + let playerNode = this._playerInstances[playerState.id]?.node; + if (!playerNode) { + return; + } + + // 创建新的箭矢显示 + let node = instantiate(this.prefabArrow); + this.arrows.addChild(node); + arrow = this._arrowInstances[arrowState.id] = node.getComponent(Arrow)!; + arrow.init(arrowState, playerNode.position, this.gameManager.state.now); } onBtnAttack() { @@ -154,7 +156,11 @@ export class GameScene extends Component { return; } - let playerNode = this._playerInstances[this.gameManager.selfPlayerId].node; + let playerNode = this._playerInstances[this.gameManager.selfPlayerId]?.node; + if (!playerNode) { + return; + } + // 攻击落点偏移(表现层坐标) let sceneOffset = playerNode.forward.clone().normalize().multiplyScalar(gameConfig.arrowDistance); // 攻击落点(逻辑层坐标) diff --git a/examples/cocos-creator-multiplayer/frontend/assets/scripts/models/GameManager.ts b/examples/cocos-creator-multiplayer/frontend/assets/scripts/models/GameManager.ts index 5dd50ca..4351a88 100644 --- a/examples/cocos-creator-multiplayer/frontend/assets/scripts/models/GameManager.ts +++ b/examples/cocos-creator-multiplayer/frontend/assets/scripts/models/GameManager.ts @@ -1,5 +1,4 @@ import { WsClient } from "tsrpc-browser"; -import { gameConfig } from "../shared/game/gameConfig"; import { GameSystem, GameSystemState } from "../shared/game/GameSystem"; import { ClientInput, MsgClientInput } from "../shared/protocols/client/MsgClientInput"; import { MsgFrame } from "../shared/protocols/server/MsgFrame"; @@ -27,14 +26,15 @@ export class GameManager { });; client.listenMsg('server/Frame', msg => { this._onServerSync(msg) }); - // 模拟网络延迟 - if (gameConfig.networkLag) { + // 模拟网络延迟 可通过 URL 参数 ?lag=200 设置延迟 + let networkLag = parseInt(new URLSearchParams(location.search).get('lag') || '0') || 0; + if (networkLag) { client.flows.preRecvDataFlow.push(async v => { - await new Promise(rs => { setTimeout(rs, gameConfig.networkLag) }) + await new Promise(rs => { setTimeout(rs, networkLag) }) return v; }); client.flows.preSendDataFlow.push(async v => { - await new Promise(rs => { setTimeout(rs, gameConfig.networkLag) }) + await new Promise(rs => { setTimeout(rs, networkLag) }) return v; }); }