diff --git a/examples/cocos-creator-multiplayer/backend/src/index.ts b/examples/cocos-creator-multiplayer/backend/src/index.ts index 46a6315..76d656e 100644 --- a/examples/cocos-creator-multiplayer/backend/src/index.ts +++ b/examples/cocos-creator-multiplayer/backend/src/index.ts @@ -2,13 +2,13 @@ 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 export const server = new WsServer(serviceProto, { port: 3000, - // Remove this to use binary mode (remove from the client too) - // json: true + json: true }); // 断开连接后退出房间 @@ -22,14 +22,16 @@ server.flows.postDisconnectFlow.push(v => { }) // 模拟网络延迟 -server.flows.preRecvDataFlow.push(async v => { - await new Promise(rs => { setTimeout(rs, 300) }) - return v; -}) -server.flows.preSendDataFlow.push(async v => { - await new Promise(rs => { setTimeout(rs, 300) }) - 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/models/Room.ts b/examples/cocos-creator-multiplayer/backend/src/models/Room.ts index e60e3e4..3937a64 100644 --- a/examples/cocos-creator-multiplayer/backend/src/models/Room.ts +++ b/examples/cocos-creator-multiplayer/backend/src/models/Room.ts @@ -1,4 +1,5 @@ import { WsConnection, WsServer } from "tsrpc"; +import { gameConfig } from "../shared/game/gameConfig"; import { GameSystem, GameSystemInput, PlayerJoin } from "../shared/game/GameSystem"; import { ReqJoin } from "../shared/protocols/PtlJoin"; import { ServiceType } from "../shared/protocols/serviceProto"; @@ -9,7 +10,7 @@ import { ServiceType } from "../shared/protocols/serviceProto"; export class Room { // 次数/秒 - syncRate = 3; + syncRate = gameConfig.syncRate; nextPlayerId = 1; gameSystem = new GameSystem(); @@ -32,8 +33,8 @@ export class Room { playerId: this.nextPlayerId++, // 初始位置随机 pos: { - x: Math.random() * 10, - y: Math.random() * 10 + x: Math.random() * 10 - 5, + y: Math.random() * 10 - 5 } } this.applyInput(input); 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 71b2b39..4068dfd 100644 --- a/examples/cocos-creator-multiplayer/backend/src/shared/game/gameConfig.ts +++ b/examples/cocos-creator-multiplayer/backend/src/shared/game/gameConfig.ts @@ -1,4 +1,8 @@ export const gameConfig = { + syncRate: 10, + // 网络延迟 + networkLag: 0, + // 攻击技能的冷却时间(毫秒) attackCD: 1000, diff --git a/examples/cocos-creator-multiplayer/frontend/assets/3d/soldier/Materials_soldier01.meta b/examples/cocos-creator-multiplayer/frontend/assets/3d/soldier/Materials_soldier01.meta deleted file mode 100644 index 6adfd90..0000000 --- a/examples/cocos-creator-multiplayer/frontend/assets/3d/soldier/Materials_soldier01.meta +++ /dev/null @@ -1,12 +0,0 @@ -{ - "ver": "1.1.0", - "importer": "directory", - "imported": true, - "uuid": "40e7bad4-3fa9-45c6-9d50-ce41c40e8174", - "files": [], - "subMetas": {}, - "userData": { - "compressionType": {}, - "isRemoteBundle": {} - } -} diff --git a/examples/cocos-creator-multiplayer/frontend/assets/3d/soldier/Materials_soldier01/Material #7.mtl b/examples/cocos-creator-multiplayer/frontend/assets/3d/soldier/Materials_soldier01/Material #7.mtl deleted file mode 100644 index 3c9ca99..0000000 --- a/examples/cocos-creator-multiplayer/frontend/assets/3d/soldier/Materials_soldier01/Material #7.mtl +++ /dev/null @@ -1,43 +0,0 @@ -{ - "__type__": "cc.Material", - "_name": "Material #7", - "_objFlags": 0, - "_native": "", - "_effectAsset": { - "__uuid__": "1baf0fc9-befa-459c-8bdd-af1a450a0319", - "__expectedType__": "cc.EffectAsset" - }, - "_techIdx": 0, - "_defines": [ - { - "USE_ALBEDO_MAP": true - } - ], - "_states": [ - { - "rasterizerState": {}, - "blendState": { - "targets": [ - {} - ] - }, - "depthStencilState": {} - } - ], - "_props": [ - { - "mainTexture": { - "__uuid__": "57d4c4ef-3199-4596-bf79-7c065964ca9c@291d4", - "__expectedType__": "cc.Texture2D" - }, - "albedoScale": { - "__type__": "cc.Vec3", - "x": 1, - "y": 1, - "z": 1 - }, - "metallic": 0.400000005960464, - "roughness": 0.70710676908493 - } - ] -} diff --git a/examples/cocos-creator-multiplayer/frontend/assets/3d/soldier/Materials_soldier01/Material #7.mtl.meta b/examples/cocos-creator-multiplayer/frontend/assets/3d/soldier/Materials_soldier01/Material #7.mtl.meta deleted file mode 100644 index b0c1efc..0000000 --- a/examples/cocos-creator-multiplayer/frontend/assets/3d/soldier/Materials_soldier01/Material #7.mtl.meta +++ /dev/null @@ -1,11 +0,0 @@ -{ - "ver": "1.0.9", - "importer": "material", - "imported": true, - "uuid": "bc711895-84b3-4729-85f8-978c46347d19", - "files": [ - ".json" - ], - "subMetas": {}, - "userData": {} -} diff --git a/examples/cocos-creator-multiplayer/frontend/assets/3d/soldier/blue.mtl b/examples/cocos-creator-multiplayer/frontend/assets/3d/soldier/blue.mtl deleted file mode 100644 index 85eb1c6..0000000 --- a/examples/cocos-creator-multiplayer/frontend/assets/3d/soldier/blue.mtl +++ /dev/null @@ -1,48 +0,0 @@ -{ - "__type__": "cc.Material", - "_name": "", - "_objFlags": 0, - "_native": "", - "_effectAsset": { - "__uuid__": "1baf0fc9-befa-459c-8bdd-af1a450a0319", - "__expectedType__": "cc.EffectAsset" - }, - "_techIdx": 0, - "_defines": [ - { - "USE_INSTANCING": true, - "USE_ALBEDO_MAP": true - }, - {}, - {}, - {} - ], - "_states": [ - { - "rasterizerState": {}, - "depthStencilState": {}, - "blendState": { - "targets": [ - {} - ] - } - }, - {}, - {}, - {} - ], - "_props": [ - { - "alphaThreshold": 0, - "roughness": 0.70710676908493, - "metallic": 0.400000005960464, - "mainTexture": { - "__uuid__": "e90ad74a-b668-4e2c-bf91-51a2a91b02aa@6c48a", - "__expectedType__": "cc.Texture2D" - } - }, - {}, - {}, - {} - ] -} diff --git a/examples/cocos-creator-multiplayer/frontend/assets/3d/soldier/blue.mtl.meta b/examples/cocos-creator-multiplayer/frontend/assets/3d/soldier/blue.mtl.meta deleted file mode 100644 index 7e4e674..0000000 --- a/examples/cocos-creator-multiplayer/frontend/assets/3d/soldier/blue.mtl.meta +++ /dev/null @@ -1,11 +0,0 @@ -{ - "ver": "1.0.9", - "importer": "material", - "imported": true, - "uuid": "225b969b-eb6a-4489-a5a5-08efbcf38b0e", - "files": [ - ".json" - ], - "subMetas": {}, - "userData": {} -} diff --git a/examples/cocos-creator-multiplayer/frontend/assets/3d/soldier/mat_soldier.mtl b/examples/cocos-creator-multiplayer/frontend/assets/3d/soldier/mat_soldier.mtl index bd5c6e7..e7a8289 100644 --- a/examples/cocos-creator-multiplayer/frontend/assets/3d/soldier/mat_soldier.mtl +++ b/examples/cocos-creator-multiplayer/frontend/assets/3d/soldier/mat_soldier.mtl @@ -10,7 +10,6 @@ "_techIdx": 0, "_defines": [ { - "USE_INSTANCING": true, "USE_TEXTURE": true } ], diff --git a/examples/cocos-creator-multiplayer/frontend/assets/3d/soldier/red.mtl b/examples/cocos-creator-multiplayer/frontend/assets/3d/soldier/red.mtl deleted file mode 100644 index 826c11e..0000000 --- a/examples/cocos-creator-multiplayer/frontend/assets/3d/soldier/red.mtl +++ /dev/null @@ -1,48 +0,0 @@ -{ - "__type__": "cc.Material", - "_name": "", - "_objFlags": 0, - "_native": "", - "_effectAsset": { - "__uuid__": "1baf0fc9-befa-459c-8bdd-af1a450a0319", - "__expectedType__": "cc.EffectAsset" - }, - "_techIdx": 0, - "_defines": [ - { - "USE_INSTANCING": true, - "USE_ALBEDO_MAP": true - }, - {}, - {}, - {} - ], - "_states": [ - { - "rasterizerState": {}, - "depthStencilState": {}, - "blendState": { - "targets": [ - {} - ] - } - }, - {}, - {}, - {} - ], - "_props": [ - { - "alphaThreshold": 0, - "roughness": 0.70710676908493, - "metallic": 0.400000005960464, - "mainTexture": { - "__uuid__": "f35bcb71-cd72-443c-94a9-5482c2d63d66@6c48a", - "__expectedType__": "cc.Texture2D" - } - }, - {}, - {}, - {} - ] -} diff --git a/examples/cocos-creator-multiplayer/frontend/assets/3d/soldier/red.mtl.meta b/examples/cocos-creator-multiplayer/frontend/assets/3d/soldier/red.mtl.meta deleted file mode 100644 index 9602215..0000000 --- a/examples/cocos-creator-multiplayer/frontend/assets/3d/soldier/red.mtl.meta +++ /dev/null @@ -1,11 +0,0 @@ -{ - "ver": "1.0.9", - "importer": "material", - "imported": true, - "uuid": "411ae6be-9581-422b-9425-36c8e71651cb", - "files": [ - ".json" - ], - "subMetas": {}, - "userData": {} -} diff --git a/examples/cocos-creator-multiplayer/frontend/assets/prefabs/Player/Player.prefab b/examples/cocos-creator-multiplayer/frontend/assets/prefabs/Player/Player.prefab index 9cb5d59..b92766a 100644 --- a/examples/cocos-creator-multiplayer/frontend/assets/prefabs/Player/Player.prefab +++ b/examples/cocos-creator-multiplayer/frontend/assets/prefabs/Player/Player.prefab @@ -23,14 +23,11 @@ "_active": true, "_components": [ { - "__id__": 31 - }, - { - "__id__": 33 + "__id__": 35 } ], "_prefab": { - "__id__": 35 + "__id__": 36 }, "_lpos": { "__type__": "cc.Vec3", @@ -62,7 +59,7 @@ }, { "__type__": "cc.Node", - "_name": "RootNode", + "_name": "soldier01", "_objFlags": 0, "_parent": { "__id__": 1 @@ -70,27 +67,76 @@ "_children": [ { "__id__": 3 + } + ], + "_active": true, + "_components": [ + { + "__id__": 32 + } + ], + "_prefab": { + "__id__": 34 + }, + "_lpos": { + "__type__": "cc.Vec3", + "x": 0, + "y": 0, + "z": 0 + }, + "_lrot": { + "__type__": "cc.Quat", + "x": 0, + "y": 1, + "z": 0, + "w": 6.123233995736766e-17 + }, + "_lscale": { + "__type__": "cc.Vec3", + "x": 1, + "y": 1, + "z": 1 + }, + "_layer": 1073741824, + "_euler": { + "__type__": "cc.Vec3", + "x": 0, + "y": 180, + "z": 0 + }, + "_id": "" + }, + { + "__type__": "cc.Node", + "_name": "RootNode", + "_objFlags": 0, + "_parent": { + "__id__": 2 + }, + "_children": [ + { + "__id__": 4 }, { - "__id__": 17 + "__id__": 18 }, { - "__id__": 19 + "__id__": 20 }, { - "__id__": 21 + "__id__": 22 }, { - "__id__": 26 + "__id__": 27 }, { - "__id__": 28 + "__id__": 29 } ], "_active": true, "_components": [], "_prefab": { - "__id__": 30 + "__id__": 31 }, "_lpos": { "__type__": "cc.Vec3", @@ -125,29 +171,29 @@ "_name": "Dummy01", "_objFlags": 0, "_parent": { - "__id__": 2 + "__id__": 3 }, "_children": [ { - "__id__": 4 + "__id__": 5 }, { - "__id__": 8 + "__id__": 9 }, { - "__id__": 10 + "__id__": 11 }, { - "__id__": 12 + "__id__": 13 }, { - "__id__": 14 + "__id__": 15 } ], "_active": true, "_components": [], "_prefab": { - "__id__": 16 + "__id__": 17 }, "_lpos": { "__type__": "cc.Vec3", @@ -182,17 +228,17 @@ "_name": "Body", "_objFlags": 0, "_parent": { - "__id__": 3 + "__id__": 4 }, "_children": [ { - "__id__": 5 + "__id__": 6 } ], "_active": true, "_components": [], "_prefab": { - "__id__": 7 + "__id__": 8 }, "_lpos": { "__type__": "cc.Vec3", @@ -227,13 +273,13 @@ "_name": "head", "_objFlags": 0, "_parent": { - "__id__": 4 + "__id__": 5 }, "_children": [], "_active": true, "_components": [], "_prefab": { - "__id__": 6 + "__id__": 7 }, "_lpos": { "__type__": "cc.Vec3", @@ -288,13 +334,13 @@ "_name": "legRight", "_objFlags": 0, "_parent": { - "__id__": 3 + "__id__": 4 }, "_children": [], "_active": true, "_components": [], "_prefab": { - "__id__": 9 + "__id__": 10 }, "_lpos": { "__type__": "cc.Vec3", @@ -339,13 +385,13 @@ "_name": "legLeft", "_objFlags": 0, "_parent": { - "__id__": 3 + "__id__": 4 }, "_children": [], "_active": true, "_components": [], "_prefab": { - "__id__": 11 + "__id__": 12 }, "_lpos": { "__type__": "cc.Vec3", @@ -390,13 +436,13 @@ "_name": "handRight", "_objFlags": 0, "_parent": { - "__id__": 3 + "__id__": 4 }, "_children": [], "_active": true, "_components": [], "_prefab": { - "__id__": 13 + "__id__": 14 }, "_lpos": { "__type__": "cc.Vec3", @@ -441,13 +487,13 @@ "_name": "handLeft", "_objFlags": 0, "_parent": { - "__id__": 3 + "__id__": 4 }, "_children": [], "_active": true, "_components": [], "_prefab": { - "__id__": 15 + "__id__": 16 }, "_lpos": { "__type__": "cc.Vec3", @@ -502,13 +548,13 @@ "_name": "Bip001", "_objFlags": 0, "_parent": { - "__id__": 2 + "__id__": 3 }, "_children": [], "_active": true, "_components": [], "_prefab": { - "__id__": 18 + "__id__": 19 }, "_lpos": { "__type__": "cc.Vec3", @@ -553,13 +599,13 @@ "_name": "mixamorig:HeadTop_End", "_objFlags": 0, "_parent": { - "__id__": 2 + "__id__": 3 }, "_children": [], "_active": true, "_components": [], "_prefab": { - "__id__": 20 + "__id__": 21 }, "_lpos": { "__type__": "cc.Vec3", @@ -604,17 +650,17 @@ "_name": "soldier01", "_objFlags": 0, "_parent": { - "__id__": 2 + "__id__": 3 }, "_children": [], "_active": true, "_components": [ { - "__id__": 22 + "__id__": 23 } ], "_prefab": { - "__id__": 25 + "__id__": 26 }, "_lpos": { "__type__": "cc.Vec3", @@ -649,11 +695,11 @@ "_name": "", "_objFlags": 0, "node": { - "__id__": 21 + "__id__": 22 }, "_enabled": true, "__prefab": { - "__id__": 23 + "__id__": 24 }, "_materials": [ { @@ -663,7 +709,7 @@ ], "_visFlags": 0, "lightmapSettings": { - "__id__": 24 + "__id__": 25 }, "_mesh": { "__uuid__": "57d4c4ef-3199-4596-bf79-7c065964ca9c@6868c", @@ -677,7 +723,7 @@ "__expectedType__": "cc.Skeleton" }, "_skinningRoot": { - "__id__": 1 + "__id__": 2 }, "_id": "" }, @@ -716,13 +762,13 @@ "_name": "Bip001(__autogen 4)", "_objFlags": 0, "_parent": { - "__id__": 2 + "__id__": 3 }, "_children": [], "_active": true, "_components": [], "_prefab": { - "__id__": 27 + "__id__": 28 }, "_lpos": { "__type__": "cc.Vec3", @@ -767,13 +813,13 @@ "_name": "mixamorig:HeadTop_End(__autogen 5)", "_objFlags": 0, "_parent": { - "__id__": 2 + "__id__": 3 }, "_children": [], "_active": true, "_components": [], "_prefab": { - "__id__": 29 + "__id__": 30 }, "_lpos": { "__type__": "cc.Vec3", @@ -828,13 +874,13 @@ "_name": "", "_objFlags": 0, "node": { - "__id__": 1 + "__id__": 2 }, "_enabled": true, "__prefab": { - "__id__": 32 + "__id__": 33 }, - "playOnLoad": false, + "playOnLoad": true, "_clips": [ { "__uuid__": "57d4c4ef-3199-4596-bf79-7c065964ca9c@1f586", @@ -869,24 +915,41 @@ "__type__": "cc.CompPrefabInfo", "fileId": "71bamidCpRLIBqyoKSaXUX" }, + { + "__type__": "cc.PrefabInfo", + "root": { + "__id__": 1 + }, + "asset": { + "__id__": 0 + }, + "fileId": "71qfN53ihYmaSuBoR11P0t" + }, { "__type__": "720b7a4EnZJjYpzLWJrmvT/", "_name": "", "_objFlags": 0, - "__editorExtras__": {}, "node": { "__id__": 1 }, "_enabled": true, - "__prefab": { - "__id__": 34 + "__prefab": null, + "ani": { + "__id__": 32 + }, + "mesh": { + "__id__": 23 + }, + "texSelf": { + "__uuid__": "e90ad74a-b668-4e2c-bf91-51a2a91b02aa@6c48a", + "__expectedType__": "cc.Texture2D" + }, + "texOther": { + "__uuid__": "f35bcb71-cd72-443c-94a9-5482c2d63d66@6c48a", + "__expectedType__": "cc.Texture2D" }, "_id": "" }, - { - "__type__": "cc.CompPrefabInfo", - "fileId": "fczHMULW9AAIJRjvl2FJiZ" - }, { "__type__": "cc.PrefabInfo", "root": { @@ -895,6 +958,33 @@ "asset": { "__id__": 0 }, - "fileId": "71qfN53ihYmaSuBoR11P0t" + "fileId": "087WztcUlE+rBZWXq8jy1C", + "targetOverrides": [ + { + "__id__": 37 + } + ] + }, + { + "__type__": "cc.TargetOverrideInfo", + "source": { + "__id__": 35 + }, + "sourceInfo": null, + "propertyPath": [ + "ani" + ], + "target": { + "__id__": 2 + }, + "targetInfo": { + "__id__": 38 + } + }, + { + "__type__": "cc.TargetInfo", + "localID": [ + "71bamidCpRLIBqyoKSaXUX" + ] } ] \ No newline at end of file diff --git a/examples/cocos-creator-multiplayer/frontend/assets/prefabs/Player/Player.prefab.meta b/examples/cocos-creator-multiplayer/frontend/assets/prefabs/Player/Player.prefab.meta index 845f068..0ae5138 100644 --- a/examples/cocos-creator-multiplayer/frontend/assets/prefabs/Player/Player.prefab.meta +++ b/examples/cocos-creator-multiplayer/frontend/assets/prefabs/Player/Player.prefab.meta @@ -2,7 +2,7 @@ "ver": "1.1.32", "importer": "prefab", "imported": true, - "uuid": "28c95a70-5b80-4b72-8f7b-93e2cefacc98", + "uuid": "809957ef-4f3f-4527-87cc-cb3223f7500a", "files": [ ".json" ], diff --git a/examples/cocos-creator-multiplayer/frontend/assets/prefabs/Player/Player.ts b/examples/cocos-creator-multiplayer/frontend/assets/prefabs/Player/Player.ts index 0431579..df4fcda 100644 --- a/examples/cocos-creator-multiplayer/frontend/assets/prefabs/Player/Player.ts +++ b/examples/cocos-creator-multiplayer/frontend/assets/prefabs/Player/Player.ts @@ -1,22 +1,88 @@ -import { Component, _decorator } from 'cc'; +import { Component, MeshRenderer, SkeletalAnimation, Texture2D, tween, Tween, Vec3, _decorator } from 'cc'; +import { gameConfig } from '../../scripts/shared/game/gameConfig'; import { PlayerState } from '../../scripts/shared/game/state/PlayerState'; const { ccclass, property } = _decorator; @ccclass('Player') export class Player extends Component { + @property(SkeletalAnimation) + ani!: SkeletalAnimation; + + @property(MeshRenderer) + mesh!: MeshRenderer; + @property(Texture2D) + texSelf!: Texture2D; + @property(Texture2D) + texOther!: Texture2D; + playerId!: number; isSelf = false; - init(state: PlayerState) { + private _tweens: Tween[] = []; + private _targetPos = new Vec3; + + init(state: PlayerState, isSelf: boolean) { this.playerId = state.id; - this.setPos(state.pos); + this.isSelf = isSelf; + this.mesh.material!.setProperty('mainTexture', this.isSelf ? this.texSelf : this.texOther); } // 把 GameSystem 空间映射到游戏空间 - setPos(pos: { x: number, y: number }) { - this.node.setPosition(pos.y, 0, pos.x); + updateSelf(state: PlayerState) { + // 更新位置 + this._targetPos.set(state.pos.x, 0, -state.pos.y); + if (!this.node.position.equals(this._targetPos)) { + this.setAni('run'); + // 朝向 + let newForward = this._targetPos.clone().subtract(this.node.position).normalize(); + this.node.forward = new Vec3(newForward); + // 位置 + this.node.setPosition(this._targetPos); + } + else { + this.setAni('idle'); + } + } + updateOther(state: PlayerState) { + // 更新位置 + let newPos = new Vec3(state.pos.x, 0, -state.pos.y); + if (!this._targetPos.equals(newPos)) { + // 清理 Tween + this._tweens?.forEach(v => v.stop()); + this._tweens = []; + this.node.setPosition(this._targetPos); + + // 插值朝向 + let newForward = new Vec3(newPos).subtract(this.node.position).normalize(); + this._tweens.push(tween({ forward: this.node.forward }).to(0.1, { forward: newForward }, { + onUpdate: (v: any) => { + this.node.forward = new Vec3(v.forward); + } + }).start()) + + // 新的位置 + this._targetPos.set(newPos) + this.setAni('run'); + this._tweens.push(tween(this.node).to(1 / gameConfig.syncRate, { + position: this._targetPos + }).call(() => { + this.setAni('idle') + }).start()); + } } -} \ No newline at end of file + setAni(ani: string) { + if (this.ani.getState(ani)?.isPlaying) { + return; + } + + this.ani.crossFade(ani, 0.1); + } + +} + +function Texture(Texture: any) { + throw new Error('Function not implemented.'); +} diff --git a/examples/cocos-creator-multiplayer/frontend/assets/scenes/GameScene/GameScene.scene b/examples/cocos-creator-multiplayer/frontend/assets/scenes/GameScene/GameScene.scene index 3af1c4f..ffa8349 100644 --- a/examples/cocos-creator-multiplayer/frontend/assets/scenes/GameScene/GameScene.scene +++ b/examples/cocos-creator-multiplayer/frontend/assets/scenes/GameScene/GameScene.scene @@ -61,16 +61,16 @@ "_prefab": null, "_lpos": { "__type__": "cc.Vec3", - "x": -35.352258452125874, + "x": 0, "y": 18.714107575144862, - "z": 5.2735593669694936e-15 + "z": 35.352258452125874 }, "_lrot": { "__type__": "cc.Quat", - "x": -0.17043586674820901, - "y": -0.686259145895912, - "z": -0.170435866748209, - "w": 0.6862591458959121 + "x": -0.2410327142701308, + "y": 0, + "z": 0, + "w": 0.9705169914285754 }, "_lscale": { "__type__": "cc.Vec3", @@ -82,7 +82,7 @@ "_euler": { "__type__": "cc.Vec3", "x": -27.895, - "y": -90, + "y": 0, "z": 0 }, "_id": "c9DMICJLFO5IeO07EPon7U" @@ -820,7 +820,7 @@ "__prefab": null, "joyStick": null, "prefabPlayer": { - "__uuid__": "28c95a70-5b80-4b72-8f7b-93e2cefacc98", + "__uuid__": "809957ef-4f3f-4527-87cc-cb3223f7500a", "__expectedType__": "cc.Prefab" }, "players": { 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 125b943..560c105 100644 --- a/examples/cocos-creator-multiplayer/frontend/assets/scenes/GameScene/GameScene.ts +++ b/examples/cocos-creator-multiplayer/frontend/assets/scenes/GameScene/GameScene.ts @@ -1,12 +1,10 @@ import { Component, instantiate, Node, Prefab, Vec2, _decorator } from 'cc'; -import { WsClient } from 'tsrpc-browser'; import { Joystick } from '../../prefabs/Joystick/Joystick'; 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 { serviceProto, ServiceType } from '../../scripts/shared/protocols/serviceProto'; const { ccclass, property } = _decorator; /** @@ -36,8 +34,7 @@ export class GameScene extends Component { @property(FollowCamera) camera: FollowCamera = null as any; - client!: WsClient; - gameManager!: GameManager; + gameManager = new GameManager(); private _playerInstances: { [playerId: number]: Player } = {}; private _selfSpeed?: Vec2 = new Vec2(0, 0); @@ -57,20 +54,16 @@ export class GameScene extends Component { } } - this.client = new WsClient(serviceProto, { - server: 'ws://127.0.0.1:3000', - logger: console - }); - this.client.flows.postDisconnectFlow.push(v => { + this.gameManager.client.flows.postDisconnectFlow.push(v => { location.reload() return v; }) - this.gameManager = new GameManager(this.client); this.gameManager.join(); } update(dt: number) { + // Send Inputs if (this._selfSpeed && this._selfSpeed.lengthSqr()) { this._selfSpeed.normalize().multiplyScalar(gameConfig.moveSpeed); @@ -84,32 +77,34 @@ export class GameScene extends Component { }) } - let gameState = this.gameManager.state; - - // console.log('update', gameState.players.length) - // Update pos - for (let playerState of gameState.players) { + let playerStates = this.gameManager.state.players; + for (let playerState of playerStates) { let player: Player = this._playerInstances[playerState.id]; if (!player) { let node = instantiate(this.prefabPlayer); this.players.addChild(node); - player = node.getComponent(Player)!; - player.init(playerState); + player = this._playerInstances[playerState.id] = node.getComponent(Player)!; + player.init(playerState, playerState.id === this.gameManager.selfPlayerId) + // 摄像机拍摄自己 if (playerState.id === this.gameManager.selfPlayerId) { this.camera.focusTarget = node; } + } - this._playerInstances[playerState.id] = player; - } - else { - // console.log('setPos', playerState.id, playerState.pos.x, playerState.pos.y) - player.setPos(playerState.pos); - } + // 自己不插值(本地预测),插值其它人 + player.isSelf ? player.updateSelf(playerState) : player.updateOther(playerState); } - // Clear left players + // Clear left players + for (let i = this.players.children.length - 1; i > -1; --i) { + let player = this.players.children[i].getComponent(Player)!; + if (!this.gameManager.state.players.find(v => v.id === player.playerId)) { + player.node.removeFromParent(); + delete this._playerInstances[player.playerId]; + } + } } } \ No newline at end of file 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 478cd39..57a390f 100644 --- a/examples/cocos-creator-multiplayer/frontend/assets/scripts/models/GameManager.ts +++ b/examples/cocos-creator-multiplayer/frontend/assets/scripts/models/GameManager.ts @@ -1,8 +1,9 @@ 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"; -import { ServiceType } from "../shared/protocols/serviceProto"; +import { serviceProto, ServiceType } from "../shared/protocols/serviceProto"; export class GameManager { @@ -17,10 +18,27 @@ export class GameManager { return this.gameSystem.state; } - constructor(client: WsClient) { - this.client = client; + constructor() { + let client = this.client = new WsClient(serviceProto, { + server: `ws://${location.hostname}:3000`, + json: true, + // logger: console + });; client.listenMsg('server/Frame', msg => { this._onServerSync(msg) }); + // 模拟网络延迟 + if (gameConfig.networkLag) { + client.flows.preRecvDataFlow.push(async v => { + await new Promise(rs => { setTimeout(rs, gameConfig.networkLag) }) + return v; + }); + client.flows.preSendDataFlow.push(async v => { + await new Promise(rs => { setTimeout(rs, gameConfig.networkLag) }) + return v; + }); + } + + (window as any).gm = this; }