移动实现 不同颜色皮肤实现

This commit is contained in:
k8w 2021-12-02 23:18:31 +08:00
parent 23186001db
commit 03aeff62c5
17 changed files with 289 additions and 298 deletions

View File

@ -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);

View File

@ -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);

View File

@ -1,4 +1,8 @@
export const gameConfig = {
syncRate: 10,
// 网络延迟
networkLag: 0,
// 攻击技能的冷却时间(毫秒)
attackCD: 1000,

View File

@ -1,12 +0,0 @@
{
"ver": "1.1.0",
"importer": "directory",
"imported": true,
"uuid": "40e7bad4-3fa9-45c6-9d50-ce41c40e8174",
"files": [],
"subMetas": {},
"userData": {
"compressionType": {},
"isRemoteBundle": {}
}
}

View File

@ -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
}
]
}

View File

@ -1,11 +0,0 @@
{
"ver": "1.0.9",
"importer": "material",
"imported": true,
"uuid": "bc711895-84b3-4729-85f8-978c46347d19",
"files": [
".json"
],
"subMetas": {},
"userData": {}
}

View File

@ -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"
}
},
{},
{},
{}
]
}

View File

@ -1,11 +0,0 @@
{
"ver": "1.0.9",
"importer": "material",
"imported": true,
"uuid": "225b969b-eb6a-4489-a5a5-08efbcf38b0e",
"files": [
".json"
],
"subMetas": {},
"userData": {}
}

View File

@ -10,7 +10,6 @@
"_techIdx": 0,
"_defines": [
{
"USE_INSTANCING": true,
"USE_TEXTURE": true
}
],

View File

@ -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"
}
},
{},
{},
{}
]
}

View File

@ -1,11 +0,0 @@
{
"ver": "1.0.9",
"importer": "material",
"imported": true,
"uuid": "411ae6be-9581-422b-9425-36c8e71651cb",
"files": [
".json"
],
"subMetas": {},
"userData": {}
}

View File

@ -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"
]
}
]

View File

@ -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"
],

View File

@ -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<any>[] = [];
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());
}
}
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.');
}

View File

@ -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": {

View File

@ -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<ServiceType>;
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
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];
}
}
}
}

View File

@ -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<ServiceType>) {
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;
}