Improve netcode and simplify #4
3 changed files with 10 additions and 61 deletions
Remove more
commit
47603c058e
|
|
@ -13,7 +13,7 @@
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "npx webpack --mode production",
|
"build": "npx webpack --mode production",
|
||||||
"dev": "concurrently --kill-others-on-fail \"webpack --mode development -w\" \"nodemon --legacy-watch dist/main.js\"",
|
"dev": "concurrently --kill-others-on-fail \"webpack --mode development -w\" \"nodemon --legacy-watch --delay 500ms dist/main.js\"",
|
||||||
"try-build": "npm run build && cd dist && node main.js && cd -"
|
"try-build": "npm run build && cd dist && node main.js && cd -"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,6 @@ import {
|
||||||
CharacterMovementState,
|
CharacterMovementState,
|
||||||
PhysicsBody,
|
PhysicsBody,
|
||||||
settings,
|
settings,
|
||||||
followVec2,
|
|
||||||
stepCharacterMovement,
|
stepCharacterMovement,
|
||||||
applyLeapImpulse,
|
applyLeapImpulse,
|
||||||
tickPlanetDetachment,
|
tickPlanetDetachment,
|
||||||
|
|
@ -27,12 +26,6 @@ export const clientTimeMs = (): number => frameTimeMs;
|
||||||
|
|
||||||
const maxReplayMs = 400;
|
const maxReplayMs = 400;
|
||||||
|
|
||||||
// Only a correction has to fade; the character's own motion is carried straight through, so this
|
|
||||||
// buys smoothing without costing responsiveness.
|
|
||||||
const smoothSeconds = 0.06;
|
|
||||||
|
|
||||||
const snapDistance = 250;
|
|
||||||
|
|
||||||
const upwards = vec2.fromValues(0, 1);
|
const upwards = vec2.fromValues(0, 1);
|
||||||
|
|
||||||
const makeBody = (center: vec2, radius: number, normal: vec2): PhysicsBody => ({
|
const makeBody = (center: vec2, radius: number, normal: vec2): PhysicsBody => ({
|
||||||
|
|
@ -59,8 +52,6 @@ export class LocalCharacterPredictor {
|
||||||
private renderHead = new Circle(vec2.create(), headRadius);
|
private renderHead = new Circle(vec2.create(), headRadius);
|
||||||
private renderLeftFoot = new Circle(vec2.create(), feetRadius);
|
private renderLeftFoot = new Circle(vec2.create(), feetRadius);
|
||||||
private renderRightFoot = new Circle(vec2.create(), feetRadius);
|
private renderRightFoot = new Circle(vec2.create(), feetRadius);
|
||||||
private previousTargets = [vec2.create(), vec2.create(), vec2.create()];
|
|
||||||
private hasRender = false;
|
|
||||||
|
|
||||||
public get head(): Circle {
|
public get head(): Circle {
|
||||||
return this.renderHead;
|
return this.renderHead;
|
||||||
|
|
@ -122,7 +113,6 @@ export class LocalCharacterPredictor {
|
||||||
this.replayAnchorMs = undefined;
|
this.replayAnchorMs = undefined;
|
||||||
this.movement = undefined;
|
this.movement = undefined;
|
||||||
this.currentStrength = settings.playerMaxStrength;
|
this.currentStrength = settings.playerMaxStrength;
|
||||||
this.hasRender = false;
|
|
||||||
this.alive = true;
|
this.alive = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -141,21 +131,21 @@ export class LocalCharacterPredictor {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public update(planets: Array<PlanetView>, frameSeconds: number): boolean {
|
public update(planets: Array<PlanetView>): boolean {
|
||||||
if (!this.alive || !this.canPredict || this.isAnimatingInOrOut) {
|
if (!this.alive || !this.canPredict || this.isAnimatingInOrOut) {
|
||||||
this.hasRender = false;
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.world.sync(planets);
|
this.world.sync(planets);
|
||||||
const predicted = this.simulate();
|
const predicted = this.simulate();
|
||||||
|
|
||||||
if (!this.hasRender) {
|
vec2.copy(this.renderHead.center, predicted.head.center);
|
||||||
this.snapRenderTo(predicted);
|
this.renderHead.radius = predicted.head.radius;
|
||||||
this.hasRender = true;
|
vec2.copy(this.renderLeftFoot.center, predicted.leftFoot.center);
|
||||||
} else {
|
this.renderLeftFoot.radius = predicted.leftFoot.radius;
|
||||||
this.followRenderTo(predicted, frameSeconds);
|
vec2.copy(this.renderRightFoot.center, predicted.rightFoot.center);
|
||||||
}
|
this.renderRightFoot.radius = predicted.rightFoot.radius;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -193,7 +183,6 @@ export class LocalCharacterPredictor {
|
||||||
let t = startMs;
|
let t = startMs;
|
||||||
for (let i = 0; i < steps; i++) {
|
for (let i = 0; i < steps; i++) {
|
||||||
const input = this.inputHistory.directionAt(t);
|
const input = this.inputHistory.directionAt(t);
|
||||||
// Planets first: the server's container holds them ahead of every character.
|
|
||||||
this.world.advance(stepSeconds);
|
this.world.advance(stepSeconds);
|
||||||
tickPlanetDetachment(state, stepSeconds);
|
tickPlanetDetachment(state, stepSeconds);
|
||||||
stepCharacterMovement(state, this.world, input, stepSeconds);
|
stepCharacterMovement(state, this.world, input, stepSeconds);
|
||||||
|
|
@ -216,8 +205,6 @@ export class LocalCharacterPredictor {
|
||||||
t += stepMs;
|
t += stepMs;
|
||||||
}
|
}
|
||||||
|
|
||||||
// A shorter final step would not integrate like the server's fixed one, so take a whole
|
|
||||||
// step and read the pose part-way along it instead.
|
|
||||||
if (partialStep > 0) {
|
if (partialStep > 0) {
|
||||||
const from = [state.head, state.leftFoot, state.rightFoot].map((b) =>
|
const from = [state.head, state.leftFoot, state.rightFoot].map((b) =>
|
||||||
vec2.clone(b.center),
|
vec2.clone(b.center),
|
||||||
|
|
@ -237,44 +224,6 @@ export class LocalCharacterPredictor {
|
||||||
|
|
||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
|
|
||||||
private snapRenderTo(state: CharacterMovementState): void {
|
|
||||||
this.previousTargets = [state.head, state.leftFoot, state.rightFoot].map((b) =>
|
|
||||||
vec2.clone(b.center),
|
|
||||||
);
|
|
||||||
this.renderHead = new Circle(vec2.clone(state.head.center), state.head.radius);
|
|
||||||
this.renderLeftFoot = new Circle(
|
|
||||||
vec2.clone(state.leftFoot.center),
|
|
||||||
state.leftFoot.radius,
|
|
||||||
);
|
|
||||||
this.renderRightFoot = new Circle(
|
|
||||||
vec2.clone(state.rightFoot.center),
|
|
||||||
state.rightFoot.radius,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
private followRenderTo(state: CharacterMovementState, frameSeconds: number): void {
|
|
||||||
this.followPart(this.renderHead, state.head, 0, frameSeconds);
|
|
||||||
this.followPart(this.renderLeftFoot, state.leftFoot, 1, frameSeconds);
|
|
||||||
this.followPart(this.renderRightFoot, state.rightFoot, 2, frameSeconds);
|
|
||||||
}
|
|
||||||
|
|
||||||
private followPart(
|
|
||||||
render: Circle,
|
|
||||||
target: PhysicsBody,
|
|
||||||
index: number,
|
|
||||||
frameSeconds: number,
|
|
||||||
): void {
|
|
||||||
followVec2(
|
|
||||||
render.center,
|
|
||||||
target.center,
|
|
||||||
this.previousTargets[index],
|
|
||||||
frameSeconds,
|
|
||||||
smoothSeconds,
|
|
||||||
snapDistance,
|
|
||||||
);
|
|
||||||
render.radius = target.radius;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export const localCharacterPredictor = new LocalCharacterPredictor();
|
export const localCharacterPredictor = new LocalCharacterPredictor();
|
||||||
|
|
|
||||||
|
|
@ -109,7 +109,7 @@ export class GameObjectContainer extends CommandReceiver {
|
||||||
localCharacterPredictor.setStrength(
|
localCharacterPredictor.setStrength(
|
||||||
player.strengthFraction * settings.playerMaxStrength,
|
player.strengthFraction * settings.playerMaxStrength,
|
||||||
);
|
);
|
||||||
if (localCharacterPredictor.update(this.planets, deltaTimeInSeconds)) {
|
if (localCharacterPredictor.update(this.planets)) {
|
||||||
player.head = localCharacterPredictor.head;
|
player.head = localCharacterPredictor.head;
|
||||||
player.leftFoot = localCharacterPredictor.leftFoot;
|
player.leftFoot = localCharacterPredictor.leftFoot;
|
||||||
player.rightFoot = localCharacterPredictor.rightFoot;
|
player.rightFoot = localCharacterPredictor.rightFoot;
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue