优化 Arrow 同步
This commit is contained in:
parent
50c8976712
commit
906c355348
@ -46,13 +46,8 @@ export class GameSystem {
|
||||
this._state.arrows.push({
|
||||
id: this._state.nextArrowId++,
|
||||
fromPlayerId: input.playerId,
|
||||
startPos: { ...player.pos },
|
||||
startTime: this._state.now,
|
||||
targetPos: {
|
||||
x: player.pos.x + input.offset.x,
|
||||
y: player.pos.y + input.offset.y
|
||||
},
|
||||
targetTime: this._state.now + gameConfig.arrowFlyTime
|
||||
targetPos: { ...input.targetPos },
|
||||
targetTime: input.targetTime
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -112,7 +107,10 @@ export interface PlayerMove {
|
||||
export interface PlayerAttack {
|
||||
type: 'PlayerAttack',
|
||||
playerId: number,
|
||||
offset: { x: number, y: number },
|
||||
// 落点坐标
|
||||
targetPos: { x: number, y: number },
|
||||
// 落点时间(游戏时间)
|
||||
targetTime: number
|
||||
}
|
||||
export interface PlayerJoin {
|
||||
type: 'PlayerJoin',
|
||||
|
@ -1,7 +1,7 @@
|
||||
export const gameConfig = {
|
||||
syncRate: 10,
|
||||
// 网络延迟
|
||||
networkLag: 0,
|
||||
// 网络延迟(同时在服务端和客户端生效,所以实际是双倍延迟)
|
||||
networkLag: 200,
|
||||
|
||||
// 攻击技能的冷却时间(毫秒)
|
||||
attackCD: 1000,
|
||||
@ -10,6 +10,6 @@ export const gameConfig = {
|
||||
|
||||
arrowFlyTime: 500,
|
||||
arrowDistance: 8,
|
||||
arrowAttackRadius: 3,
|
||||
arrowAttackRadius: 2,
|
||||
arrowDizzyTime: 1000
|
||||
}
|
@ -1,8 +1,6 @@
|
||||
export type ArrowState = {
|
||||
id: number,
|
||||
fromPlayerId: number,
|
||||
startTime: number,
|
||||
startPos: { x: number, y: number },
|
||||
targetTime: number,
|
||||
targetPos: { x: number, y: number }
|
||||
}
|
@ -17,7 +17,6 @@ export interface ServiceType {
|
||||
}
|
||||
|
||||
export const serviceProto: ServiceProto<ServiceType> = {
|
||||
"version": 2,
|
||||
"services": [
|
||||
{
|
||||
"id": 0,
|
||||
@ -159,8 +158,8 @@ export const serviceProto: ServiceProto<ServiceType> = {
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 3,
|
||||
"name": "offset",
|
||||
"id": 2,
|
||||
"name": "targetPos",
|
||||
"type": {
|
||||
"type": "Interface",
|
||||
"properties": [
|
||||
@ -180,6 +179,13 @@ export const serviceProto: ServiceProto<ServiceType> = {
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 3,
|
||||
"name": "targetTime",
|
||||
"type": {
|
||||
"type": "Number"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
@ -228,7 +234,7 @@ export const serviceProto: ServiceProto<ServiceType> = {
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 4,
|
||||
"id": 2,
|
||||
"name": "arrows",
|
||||
"type": {
|
||||
"type": "Array",
|
||||
@ -309,43 +315,13 @@ export const serviceProto: ServiceProto<ServiceType> = {
|
||||
},
|
||||
{
|
||||
"id": 2,
|
||||
"name": "startTime",
|
||||
"type": {
|
||||
"type": "Number"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 3,
|
||||
"name": "startPos",
|
||||
"type": {
|
||||
"type": "Interface",
|
||||
"properties": [
|
||||
{
|
||||
"id": 0,
|
||||
"name": "x",
|
||||
"type": {
|
||||
"type": "Number"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 1,
|
||||
"name": "y",
|
||||
"type": {
|
||||
"type": "Number"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 4,
|
||||
"name": "targetTime",
|
||||
"type": {
|
||||
"type": "Number"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 5,
|
||||
"id": 3,
|
||||
"name": "targetPos",
|
||||
"type": {
|
||||
"type": "Interface",
|
||||
|
@ -12,31 +12,44 @@ export class Arrow extends Component {
|
||||
id!: number;
|
||||
state!: ArrowState;
|
||||
|
||||
// 开始位置(场景坐标)
|
||||
private _startPos = new Vec3;
|
||||
// 落点位置(场景坐标)
|
||||
private _endPos = new Vec3;
|
||||
// 开始时间(场景时间)
|
||||
private _startTime = 0;
|
||||
// 结束时间(场景时间)
|
||||
private _endTime = 0;
|
||||
|
||||
init(state: ArrowState) {
|
||||
init(state: ArrowState, startPos: Vec3, now: number) {
|
||||
this.id = state.id;
|
||||
this.state = state;
|
||||
this._startPos.set(state.startPos.x, 0, -state.startPos.y);
|
||||
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._updatePosAndForward(0);
|
||||
}
|
||||
|
||||
updateState(state: ArrowState, now: number) {
|
||||
let percent = MathUtil.limit((now - state.startTime) / (state.targetTime - state.startTime), 0, 1);
|
||||
|
||||
update() {
|
||||
//下一个目标位置
|
||||
let newPos = this._startPos.clone().lerp(this._endPos, percent)
|
||||
//下一个目标位置的高度
|
||||
newPos.y = ARROW_TOP * Math.cos(percent * Math.PI - Math.PI / 2);
|
||||
let percent = MathUtil.limit((Date.now() - this._startTime) / (this._endTime - this._startTime), 0, 1);
|
||||
this._updatePosAndForward(percent);
|
||||
}
|
||||
|
||||
private _updatePosAndForward(percent: number) {
|
||||
let nextPos = this._getPos(percent);
|
||||
this.node.position = nextPos;
|
||||
|
||||
//武器朝向下一个目标位置, 形成曲线飞行的感觉
|
||||
let newForward = newPos.clone().subtract(this.node.position).normalize();
|
||||
if (!newForward.equals(Vec3.ZERO)) {
|
||||
this.node.forward = newForward;
|
||||
let lastPos = this._getPos(percent - 0.01)
|
||||
this.node.forward = nextPos.clone().subtract(lastPos).normalize();
|
||||
}
|
||||
|
||||
this.node.position = newPos;
|
||||
private _getPos(percent: number) {
|
||||
let pos = this._startPos.clone().lerp(this._endPos, percent)
|
||||
pos.y = ARROW_TOP * Math.cos(percent * Math.PI - Math.PI / 2);
|
||||
return pos;
|
||||
}
|
||||
|
||||
}
|
@ -1,5 +1,6 @@
|
||||
|
||||
import { Component, MeshRenderer, SkeletalAnimation, Texture2D, tween, Tween, Vec3, _decorator } from 'cc';
|
||||
import { Component, MeshRenderer, SkeletalAnimation, Texture2D, tween, Vec3, _decorator } from 'cc';
|
||||
import { TweenPool } from '../../scripts/models/TweenPool';
|
||||
import { gameConfig } from '../../scripts/shared/game/gameConfig';
|
||||
import { PlayerState } from '../../scripts/shared/game/state/PlayerState';
|
||||
const { ccclass, property } = _decorator;
|
||||
@ -22,7 +23,7 @@ export class Player extends Component {
|
||||
state!: PlayerState;
|
||||
now: number = 0;
|
||||
|
||||
private _tweens: Tween<any>[] = [];
|
||||
private _tweens = new TweenPool;
|
||||
private _targetPos = new Vec3;
|
||||
|
||||
start() {
|
||||
@ -73,14 +74,12 @@ export class Player extends Component {
|
||||
// 更新位置
|
||||
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._tweens.clear();
|
||||
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 }, {
|
||||
this._tweens.add(tween({ forward: this.node.forward }).to(0.1, { forward: newForward }, {
|
||||
onUpdate: (v: any) => {
|
||||
this.node.forward = new Vec3(v.forward);
|
||||
}
|
||||
@ -89,7 +88,7 @@ export class Player extends Component {
|
||||
// 新的位置
|
||||
this._targetPos.set(newPos)
|
||||
this.setAni('run');
|
||||
this._tweens.push(tween(this.node).to(1 / gameConfig.syncRate, {
|
||||
this._tweens.add(tween(this.node).to(1 / gameConfig.syncRate, {
|
||||
position: this._targetPos
|
||||
}).call(() => {
|
||||
this.setAni('idle')
|
||||
|
@ -39,7 +39,7 @@ export class GameScene extends Component {
|
||||
@property(FollowCamera)
|
||||
camera: FollowCamera = null as any;
|
||||
|
||||
gameManager = new GameManager();
|
||||
gameManager!: GameManager;
|
||||
|
||||
private _playerInstances: { [playerId: number]: Player } = {};
|
||||
private _arrowInstances: { [arrowId: number]: Arrow } = {};
|
||||
@ -60,11 +60,11 @@ export class GameScene extends Component {
|
||||
}
|
||||
}
|
||||
|
||||
this.gameManager = new GameManager();
|
||||
this.gameManager.client.flows.postDisconnectFlow.push(v => {
|
||||
location.reload()
|
||||
return v;
|
||||
})
|
||||
|
||||
});
|
||||
this.gameManager.join();
|
||||
}
|
||||
|
||||
@ -125,13 +125,17 @@ export class GameScene extends Component {
|
||||
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)
|
||||
arrow.init(arrowState, playerNode.position, this.gameManager.state.now);
|
||||
}
|
||||
|
||||
arrow.updateState(arrowState, this.gameManager.state.now);
|
||||
}
|
||||
|
||||
// Clear left players
|
||||
@ -145,11 +149,21 @@ export class GameScene extends Component {
|
||||
}
|
||||
|
||||
onBtnAttack() {
|
||||
let offset = this._playerInstances[this.gameManager.selfPlayerId].node.forward.clone().normalize().multiplyScalar(gameConfig.arrowDistance);
|
||||
this.gameManager.sendClientInput({
|
||||
type: 'PlayerAttack',
|
||||
offset: { x: offset.x, y: -offset.z }
|
||||
})
|
||||
let playerState = this.gameManager.state.players.find(v => v.id === this.gameManager.selfPlayerId);
|
||||
if (!playerState) {
|
||||
return;
|
||||
}
|
||||
|
||||
let playerNode = this._playerInstances[this.gameManager.selfPlayerId].node;
|
||||
// 攻击落点偏移(表现层坐标)
|
||||
let sceneOffset = playerNode.forward.clone().normalize().multiplyScalar(gameConfig.arrowDistance);
|
||||
// 攻击落点(逻辑层坐标)
|
||||
let targetPos = new Vec2(playerState.pos.x, playerState.pos.y).add2f(sceneOffset.x, -sceneOffset.z);
|
||||
this.gameManager.sendClientInput({
|
||||
type: 'PlayerAttack',
|
||||
// 显示坐标 —> 逻辑坐标
|
||||
targetPos: { x: targetPos.x, y: targetPos.y },
|
||||
targetTime: this.gameManager.state.now + gameConfig.arrowFlyTime
|
||||
})
|
||||
}
|
||||
}
|
@ -23,7 +23,7 @@ export class GameManager {
|
||||
let client = this.client = new WsClient(serviceProto, {
|
||||
server: `ws://${location.hostname}:3000`,
|
||||
json: true,
|
||||
// logger: console
|
||||
logger: console
|
||||
});;
|
||||
client.listenMsg('server/Frame', msg => { this._onServerSync(msg) });
|
||||
|
||||
@ -96,12 +96,18 @@ export class GameManager {
|
||||
return;
|
||||
}
|
||||
|
||||
console.log('sendClientInput', input, this.state.now);
|
||||
|
||||
let msg: MsgClientInput = {
|
||||
sn: ++this.lastSN,
|
||||
inputs: [input]
|
||||
}
|
||||
this.pendingInputMsgs.push(msg);
|
||||
this.client.sendMsg('client/ClientInput', msg);
|
||||
this.client.sendMsg('client/ClientInput', msg).then(v => {
|
||||
if (!v.isSucc) {
|
||||
console.error('xxxxxxxxxxxxxx', msg, v)
|
||||
}
|
||||
});
|
||||
|
||||
// 预测
|
||||
this.gameSystem.applyInput({
|
||||
|
@ -0,0 +1,16 @@
|
||||
import { Tween } from "cc";
|
||||
|
||||
export class TweenPool {
|
||||
|
||||
private _tweens: Tween<any>[] = [];
|
||||
|
||||
add(tween: Tween<any>) {
|
||||
this._tweens.push(tween);
|
||||
}
|
||||
|
||||
clear() {
|
||||
this._tweens?.forEach(v => v.stop());
|
||||
this._tweens = [];
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,9 @@
|
||||
{
|
||||
"ver": "4.0.22",
|
||||
"importer": "typescript",
|
||||
"imported": true,
|
||||
"uuid": "11f8c73c-26e5-4da9-b5b9-4e9223b2c79a",
|
||||
"files": [],
|
||||
"subMetas": {},
|
||||
"userData": {}
|
||||
}
|
@ -7,6 +7,6 @@
|
||||
"version": "3.3.2"
|
||||
},
|
||||
"dependencies": {
|
||||
"tsrpc-browser": "^3.1.2"
|
||||
"tsrpc-browser": "^3.1.3"
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user