Improve movement

This commit is contained in:
schmelczerandras 2020-10-27 10:32:37 +01:00
parent 7e188c2b9a
commit c9eae3adeb
16 changed files with 227 additions and 279 deletions

View file

@ -10,6 +10,7 @@ import {
ReactsToCollision, ReactsToCollision,
reactsToCollision, reactsToCollision,
} from '../physics/physicals/reacts-to-collision'; } from '../physics/physicals/reacts-to-collision';
import { Physical } from '../physics/physicals/physical';
@serializesTo(Circle) @serializesTo(Circle)
export class CirclePhysical implements Circle, DynamicPhysical, ReactsToCollision { export class CirclePhysical implements Circle, DynamicPhysical, ReactsToCollision {
@ -18,6 +19,7 @@ export class CirclePhysical implements Circle, DynamicPhysical, ReactsToCollisio
public velocity = vec2.create(); public velocity = vec2.create();
private _boundingBox: BoundingBox; private _boundingBox: BoundingBox;
public lastNormal = vec2.fromValues(1, 0);
constructor( constructor(
private _center: vec2, private _center: vec2,
@ -95,38 +97,27 @@ export class CirclePhysical implements Circle, DynamicPhysical, ReactsToCollisio
public step(_: number) {} public step(_: number) {}
public step2(deltaTimeInSeconds: number): GameObject | undefined { public step2(
vec2.scale( deltaTimeInSeconds: number,
this.velocity, additionalCollider?: Physical,
this.velocity, ): { hitObject: GameObject | undefined; velocity: vec2 } {
Math.pow(settings.velocityAttenuation, deltaTimeInSeconds), let delta = vec2.scale(vec2.create(), this.velocity, deltaTimeInSeconds);
);
const delta = vec2.scale(vec2.create(), this.velocity, deltaTimeInSeconds);
this.radius += vec2.length(delta); this.radius += vec2.length(delta);
const intersecting = this.container.findIntersecting(this.boundingBox); const intersecting = this.container
.findIntersecting(this.boundingBox)
.filter((b) => b.gameObject !== this.gameObject && b.canCollide);
this.radius -= vec2.length(delta); this.radius -= vec2.length(delta);
const stepCount = Math.ceil(vec2.length(delta) / settings.physicsMaxStep); if (additionalCollider) {
vec2.scale(delta, delta, 1 / stepCount); intersecting.push(additionalCollider);
}
let lastHit: GameObject | undefined; let { normal, hitSurface, hitObject } = moveCircle(this, delta, intersecting);
for (let i = 0; i < stepCount; i++) {
const distance = vec2.scale(
vec2.create(),
this.velocity,
deltaTimeInSeconds / stepCount,
);
const { normal, hitSurface, hitObject } = moveCircle(
this,
vec2.clone(distance),
intersecting,
);
if (hitSurface) { if (hitSurface) {
vec2.copy(this.lastNormal, normal!);
vec2.subtract( vec2.subtract(
this.velocity, this.velocity,
this.velocity, this.velocity,
@ -137,37 +128,16 @@ export class CirclePhysical implements Circle, DynamicPhysical, ReactsToCollisio
), ),
); );
lastHit = hitObject; if (vec2.length(this.velocity) > 50) {
delta = vec2.scale(vec2.create(), this.velocity, deltaTimeInSeconds);
moveCircle(this, delta, intersecting);
} }
} }
return lastHit; const lastVelocity = vec2.clone(this.velocity);
} vec2.zero(this.velocity);
public tryMove(delta: vec2): GameObject | undefined { return { hitObject, velocity: lastVelocity };
this.radius += vec2.length(delta);
const intersecting = this.container.findIntersecting(this.boundingBox);
this.radius -= vec2.length(delta);
const stepCount = Math.ceil(vec2.length(delta) / settings.physicsMaxStep);
vec2.scale(delta, delta, 1 / stepCount);
let lastHit: GameObject | undefined;
for (let i = 0; i < stepCount; i++) {
const { tangent, hitSurface, hitObject } = moveCircle(
this,
vec2.clone(delta),
intersecting,
);
if (hitSurface) {
delta = vec2.scale(delta, tangent!, vec2.length(delta));
lastHit = hitObject;
}
}
return lastHit;
} }
public toArray(): Array<any> { public toArray(): Array<any> {

View file

@ -154,43 +154,15 @@ export class PlanetPhysical
} }
public getForce(position: vec2): vec2 { public getForce(position: vec2): vec2 {
const height = this.distance(position); const diff = vec2.subtract(vec2.create(), this.center, position);
const dist = Math.max(0, vec2.length(diff) - 600);
let closestIndex = 0; vec2.normalize(diff, diff);
this.vertices.forEach((v, i) => {
if (
vec2.distance(position, v) < vec2.distance(position, this.vertices[closestIndex])
) {
closestIndex = i;
}
});
const afterClosest = this.vertices[(closestIndex + 1) % this.vertices.length];
const closest = this.vertices[closestIndex];
const beforeClosest = this.vertices[
(closestIndex - 1 + this.vertices.length) % this.vertices.length
];
const diff = vec2.subtract(vec2.create(), position, closest);
const edge1 = vec2.subtract(vec2.create(), afterClosest, closest);
const edge2 = vec2.subtract(vec2.create(), closest, beforeClosest);
const normalizedDiff = vec2.normalize(vec2.create(), diff);
const currentEdge =
vec2.dot(vec2.normalize(vec2.create(), rotate90Deg(edge1)), normalizedDiff) >
vec2.dot(vec2.normalize(vec2.create(), rotate90Deg(edge2)), normalizedDiff)
? edge1
: edge2;
vec2.normalize(currentEdge, currentEdge);
const normal = rotateMinus90Deg(currentEdge);
const scale = clamp( const scale = clamp(
settings.maxGravityQ * ((settings.maxGravityDistance / height) ** 3 - 1), settings.maxGravityQ * ((settings.maxGravityDistance / dist) ** 1.5 - 1),
0, 0,
settings.maxGravityStrength, settings.maxGravityStrength,
); );
return vec2.scale(normal, normal, scale); return vec2.scale(diff, diff, scale);
} }
public get gameObject(): this { public get gameObject(): this {

View file

@ -70,10 +70,13 @@ export class PlayerCharacterPhysical
public static readonly boundRadius = public static readonly boundRadius =
(PlayerCharacterPhysical.headRadius + PlayerCharacterPhysical.feetRadius * 2) * 2; (PlayerCharacterPhysical.headRadius + PlayerCharacterPhysical.feetRadius * 2) * 2;
private timeSinceDying = 0;
private isDestroyed = false; private isDestroyed = false;
private timeSinceBorn = 0;
private hasJustBorn = true;
private direction = 0; private direction = 0;
private currentPlanet?: PlanetPhysical; private currentPlanet?: PlanetPhysical;
private lastMovementWasRelative = false;
private secondsSinceOnSurface = 1000; private secondsSinceOnSurface = 1000;
public head: CirclePhysical; public head: CirclePhysical;
@ -81,15 +84,8 @@ export class PlayerCharacterPhysical
public rightFoot: CirclePhysical; public rightFoot: CirclePhysical;
public bound: CirclePhysical; public bound: CirclePhysical;
public get isAlive(): boolean {
return !this.isDestroyed;
}
private movementActions: Array<MoveActionCommand> = []; private movementActions: Array<MoveActionCommand> = [];
private lastMovementAction: MoveActionCommand = new MoveActionCommand( private lastMovementAction: MoveActionCommand = new MoveActionCommand(vec2.create());
vec2.create(),
false,
);
constructor( constructor(
name: string, name: string,
@ -131,6 +127,10 @@ export class PlayerCharacterPhysical
); );
} }
public get isAlive(): boolean {
return !this.isDestroyed;
}
public handleMovementAction(c: MoveActionCommand) { public handleMovementAction(c: MoveActionCommand) {
this.movementActions.push(c); this.movementActions.push(c);
} }
@ -150,7 +150,7 @@ export class PlayerCharacterPhysical
this.health -= other.strength; this.health -= other.strength;
this.remoteCall('setHealth', this.health); this.remoteCall('setHealth', this.health);
if (this.health <= 0) { if (this.health <= 0) {
this.destroy(); this.kill();
other.originator.addKill(); other.originator.addKill();
} }
} }
@ -217,9 +217,6 @@ export class PlayerCharacterPhysical
vec2.scale(direction, direction, 1 / this.movementActions.length); vec2.scale(direction, direction, 1 / this.movementActions.length);
this.lastMovementWasRelative =
this.movementActions.find((a) => a.isCharacterRelative) !== undefined;
this.lastMovementAction = last(this.movementActions)!; this.lastMovementAction = last(this.movementActions)!;
this.movementActions = []; this.movementActions = [];
} }
@ -227,8 +224,35 @@ export class PlayerCharacterPhysical
return vec2.normalize(direction, direction); return vec2.normalize(direction, direction);
} }
private animateScaling(q: number) {
this.remoteCall(
'updateCircles',
new Circle(this.head.center, PlayerCharacterPhysical.headRadius * q),
new Circle(this.leftFoot.center, PlayerCharacterPhysical.feetRadius * q),
new Circle(this.rightFoot.center, PlayerCharacterPhysical.feetRadius * q),
);
}
public step(deltaTime: number) { public step(deltaTime: number) {
if ((this.secondsSinceOnSurface += deltaTime) > 1) { if (this.isDestroyed) {
if ((this.timeSinceDying += deltaTime) > settings.spawnDespawnTime) {
this.destroy();
} else {
this.animateScaling(1 - this.timeSinceDying / settings.spawnDespawnTime);
}
return;
}
if (this.hasJustBorn) {
if ((this.timeSinceBorn += deltaTime) > settings.spawnDespawnTime) {
this.hasJustBorn = false;
} else {
this.animateScaling(this.timeSinceBorn / settings.spawnDespawnTime);
}
return;
}
if ((this.secondsSinceOnSurface += deltaTime) > 0.5) {
this.currentPlanet = undefined; this.currentPlanet = undefined;
} }
@ -247,12 +271,11 @@ export class PlayerCharacterPhysical
), ),
), ),
); );
const feetCenter = vec2.add(
vec2.create(), const direction = this.averageAndResetMovementActions();
this.leftFoot.center, const movementForce = vec2.scale(direction, direction, settings.maxAcceleration);
this.rightFoot.center,
); if (!this.currentPlanet) {
vec2.scale(feetCenter, feetCenter, 0.5);
const leftFootGravity = forceAtPosition( const leftFootGravity = forceAtPosition(
this.leftFoot.center, this.leftFoot.center,
intersectingWithForcefield, intersectingWithForcefield,
@ -262,19 +285,10 @@ export class PlayerCharacterPhysical
intersectingWithForcefield, intersectingWithForcefield,
); );
const direction = this.averageAndResetMovementActions();
const movementForce = vec2.scale(direction, direction, settings.maxAcceleration);
if (!this.currentPlanet) {
this.applyForce(this.leftFoot, leftFootGravity, deltaTime); this.applyForce(this.leftFoot, leftFootGravity, deltaTime);
this.applyForce(this.rightFoot, rightFootGravity, deltaTime); this.applyForce(this.rightFoot, rightFootGravity, deltaTime);
const sumForce = vec2.subtract( const sumForce = vec2.subtract(vec2.create(), leftFootGravity, movementForce);
vec2.create(),
// the next line is intentional
vec2.add(vec2.create(), leftFootGravity, rightFootGravity),
movementForce,
);
this.setDirection( this.setDirection(
vec2.length(sumForce) === 0 ? vec2.fromValues(0, -1) : sumForce, vec2.length(sumForce) === 0 ? vec2.fromValues(0, -1) : sumForce,
@ -284,32 +298,41 @@ export class PlayerCharacterPhysical
const leftFootGravity = this.currentPlanet!.getForce(this.leftFoot.center); const leftFootGravity = this.currentPlanet!.getForce(this.leftFoot.center);
const rightFootGravity = this.currentPlanet!.getForce(this.rightFoot.center); const rightFootGravity = this.currentPlanet!.getForce(this.rightFoot.center);
if (this.lastMovementWasRelative) { vec2.add(leftFootGravity, leftFootGravity, rightFootGravity);
vec2.rotate(movementForce, movementForce, vec2.create(), this.direction); const gravity = vec2.scale(leftFootGravity, leftFootGravity, 0.5);
if (vec2.dot(movementForce, gravity) < -vec2.length(movementForce) * 0.8) {
vec2.scale(gravity, gravity, 0.35);
} }
if (vec2.dot(movementForce, leftFootGravity) < -vec2.length(movementForce) * 0.8) { const scaledLeftFootGravity = vec2.scale(
vec2.scale(leftFootGravity, leftFootGravity, 0.35); vec2.create(),
vec2.scale(rightFootGravity, rightFootGravity, 0.35); this.leftFoot.lastNormal,
} vec2.dot(this.leftFoot.lastNormal, gravity),
this.applyForce(this.leftFoot, leftFootGravity, deltaTime); );
this.applyForce(this.rightFoot, rightFootGravity, deltaTime); this.applyForce(this.leftFoot, scaledLeftFootGravity, deltaTime);
const headGravity = this.currentPlanet!.getForce(this.head.center); const scaledRightFootGravity = vec2.scale(
vec2.create(),
this.rightFoot.lastNormal,
vec2.dot(this.rightFoot.lastNormal, gravity),
);
if (vec2.length(headGravity) < vec2.length(leftFootGravity) / 2) { this.applyForce(this.rightFoot, scaledRightFootGravity, deltaTime);
if (vec2.length(gravity) <= 100) {
this.currentPlanet = undefined; this.currentPlanet = undefined;
} }
this.setDirection(headGravity, deltaTime); this.setDirection(gravity, deltaTime);
} }
this.applyForce(this.leftFoot, movementForce, deltaTime); this.applyForce(this.leftFoot, movementForce, deltaTime);
this.applyForce(this.rightFoot, movementForce, deltaTime); this.applyForce(this.rightFoot, movementForce, deltaTime);
this.keepPosture(deltaTime);
this.stepBodyPart(this.leftFoot, deltaTime); this.stepBodyPart(this.leftFoot, deltaTime);
this.stepBodyPart(this.rightFoot, deltaTime); this.stepBodyPart(this.rightFoot, deltaTime);
this.stepBodyPart(this.head, deltaTime);
this.keepPosture(deltaTime);
this.remoteCall('updateCircles', this.head, this.leftFoot, this.rightFoot); this.remoteCall('updateCircles', this.head, this.leftFoot, this.rightFoot);
} }
@ -323,45 +346,29 @@ export class PlayerCharacterPhysical
} }
private keepPosture(deltaTime: number) { private keepPosture(deltaTime: number) {
const center = this.center; let center = this.center;
this.springMove( this.springMove(
this.leftFoot, this.leftFoot,
center, center,
PlayerCharacterPhysical.leftFootOffset, PlayerCharacterPhysical.leftFootOffset,
deltaTime, deltaTime,
15000,
); );
this.springMove( this.springMove(
this.rightFoot, this.rightFoot,
center, center,
PlayerCharacterPhysical.rightFootOffset, PlayerCharacterPhysical.rightFootOffset,
deltaTime, deltaTime,
15000,
); );
/*
const feetDelta = vec2.subtract(
vec2.create(),
this.leftFoot.center,
this.rightFoot.center,
);
const desiredDistance = vec2.dist(
PlayerCharacterPhysical.desiredLeftFootOffset,
PlayerCharacterPhysical.desiredRightFootOffset,
);
const actualDistance = vec2.length(feetDelta);
const delta = vec2.normalize(feetDelta, feetDelta);
vec2.scale(delta, delta, Math.min(actualDistance / 2, deltaTime * 200));
let hitObject = this.rightFoot.tryMove(delta);
if (hitObject instanceof PlanetPhysical) {
this.secondsSinceOnSurface = 0;
this.currentPlanet = hitObject;
}
vec2.scale(delta, delta, -1);
hitObject = this.leftFoot.tryMove(delta);
if (hitObject instanceof PlanetPhysical) {
this.secondsSinceOnSurface = 0;
this.currentPlanet = hitObject;
}*/
this.springMove(this.head, center, PlayerCharacterPhysical.headOffset, deltaTime); this.springMove(
this.head,
center,
PlayerCharacterPhysical.headOffset,
deltaTime,
25000,
);
} }
private springMove( private springMove(
@ -369,28 +376,25 @@ export class PlayerCharacterPhysical
center: vec2, center: vec2,
offset: vec2, offset: vec2,
deltaTime: number, deltaTime: number,
strength: number,
) { ) {
const desiredPosition = vec2.add(vec2.create(), center, offset); const desiredPosition = vec2.add(vec2.create(), center, offset);
vec2.rotate(desiredPosition, desiredPosition, center, this.direction); vec2.rotate(desiredPosition, desiredPosition, center, this.direction);
const positionDelta = vec2.subtract(desiredPosition, desiredPosition, object.center); const positionDelta = vec2.subtract(vec2.create(), desiredPosition, object.center);
const positionDeltaDirection = vec2.normalize(vec2.create(), positionDelta); const positionDeltaDirection = vec2.normalize(vec2.create(), positionDelta);
const positionDeltaLength = vec2.length(positionDelta); const positionDeltaLength = vec2.length(positionDelta);
vec2.scale( vec2.scale(
positionDelta, positionDelta,
positionDeltaDirection, positionDeltaDirection,
Math.min(positionDeltaLength, (positionDeltaLength / 50) * deltaTime * 800), positionDeltaLength * deltaTime * strength,
); );
const hitObject = object.tryMove(positionDelta);
if (hitObject instanceof PlanetPhysical) { object.applyForce(positionDelta, deltaTime);
this.secondsSinceOnSurface = 0;
this.currentPlanet = hitObject;
}
} }
private stepBodyPart(part: CirclePhysical, deltaTime: number) { private stepBodyPart(part: CirclePhysical, deltaTime: number) {
const hitObject = part.step2(deltaTime); const { hitObject } = part.step2(deltaTime);
if (hitObject instanceof PlanetPhysical) { if (hitObject instanceof PlanetPhysical) {
this.secondsSinceOnSurface = 0; this.secondsSinceOnSurface = 0;
this.currentPlanet = hitObject; this.currentPlanet = hitObject;
@ -411,13 +415,14 @@ export class PlayerCharacterPhysical
); );
} }
public destroy() { public kill() {
if (!this.isDestroyed) {
this.isDestroyed = true; this.isDestroyed = true;
}
private destroy() {
this.container.removeObject(this); this.container.removeObject(this);
this.container.removeObject(this.head); this.container.removeObject(this.head);
this.container.removeObject(this.leftFoot); this.container.removeObject(this.leftFoot);
this.container.removeObject(this.rightFoot); this.container.removeObject(this.rightFoot);
} }
} }
}

View file

@ -114,10 +114,19 @@ export class ProjectilePhysical
} }
}); });
vec2.add(this.velocity, this.velocity, vec2.scale(vec2.create(), gravity, deltaTime)); vec2.add(
this.velocity,
this.velocity,
vec2.scale(
vec2.create(),
gravity,
deltaTime * settings.gravityScalingForProjectiles,
),
);
this.object.velocity = this.velocity; vec2.copy(this.object.velocity, this.velocity);
this.object.step2(deltaTime); const { velocity } = this.object.step2(deltaTime);
vec2.copy(this.velocity, velocity);
this.remoteCall('setCenter', this.center); this.remoteCall('setCenter', this.center);
} }

View file

@ -17,26 +17,24 @@ export const moveCircle = (
tangent?: vec2; tangent?: vec2;
hitObject?: GameObject; hitObject?: GameObject;
} => { } => {
const nextCircle = new Circle(vec2.clone(circle.center), circle.radius); const direction = vec2.normalize(vec2.create(), delta);
vec2.add(nextCircle.center, nextCircle.center, delta); const deltaLength = vec2.length(delta);
let travelled = 0;
possibleIntersectors = possibleIntersectors.filter( let rayEnd = vec2.create();
(b) => b.gameObject !== circle.gameObject && b.canCollide, let prevMinDistance = 0;
while (travelled < deltaLength) {
travelled += prevMinDistance;
vec2.add(
rayEnd,
circle.center,
vec2.scale(vec2.create(), direction, Math.min(travelled, deltaLength)),
); );
const sdfAtCenter = evaluateSdf(nextCircle.center, possibleIntersectors); const minDistance = evaluateSdf(rayEnd, possibleIntersectors);
if (sdfAtCenter > nextCircle.radius) {
circle.center = vec2.add(circle.center, circle.center, delta);
return {
realDelta: delta,
hitSurface: false,
};
}
if (minDistance < circle.radius) {
const intersecting = possibleIntersectors.find( const intersecting = possibleIntersectors.find(
(i) => i.distance(nextCircle.center) <= circle.radius, (i) => i.distance(rayEnd) <= circle.radius,
)!; )!;
if (ignoreCollision) { if (ignoreCollision) {
@ -51,14 +49,28 @@ export const moveCircle = (
} }
} }
vec2.add(
rayEnd,
circle.center,
vec2.scale(vec2.create(), direction, travelled - prevMinDistance),
);
vec2.copy(circle.center, rayEnd);
const dx = const dx =
evaluateSdf(vec2.add(vec2.create(), nextCircle.center, vec2.fromValues(0.01, 0)), [ evaluateSdf(vec2.add(vec2.create(), rayEnd, vec2.fromValues(0.01, 0)), [
intersecting, intersecting,
]) - sdfAtCenter; ]) -
evaluateSdf(vec2.add(vec2.create(), rayEnd, vec2.fromValues(-0.01, 0)), [
intersecting,
]);
const dy = const dy =
evaluateSdf(vec2.add(vec2.create(), nextCircle.center, vec2.fromValues(0, 0.01)), [ evaluateSdf(vec2.add(vec2.create(), rayEnd, vec2.fromValues(0, 0.01)), [
intersecting, intersecting,
]) - sdfAtCenter; ]) -
evaluateSdf(vec2.add(vec2.create(), rayEnd, vec2.fromValues(0, -0.01)), [
intersecting,
]);
const normal = vec2.fromValues(dx, dy); const normal = vec2.fromValues(dx, dy);
vec2.normalize(normal, normal); vec2.normalize(normal, normal);
const rotatedNormal = rotate90Deg(normal); const rotatedNormal = rotate90Deg(normal);
@ -72,4 +84,15 @@ export const moveCircle = (
? vec2.scale(rotatedNormal, rotatedNormal, -1) ? vec2.scale(rotatedNormal, rotatedNormal, -1)
: rotatedNormal, : rotatedNormal,
}; };
}
prevMinDistance = minDistance;
}
vec2.add(circle.center, circle.center, delta);
return {
realDelta: delta,
hitSurface: false,
};
}; };

View file

@ -196,7 +196,7 @@ export class NPC extends PlayerBase {
this.timeSinceLastFindShootTarget = 0; this.timeSinceLastFindShootTarget = 0;
} }
this.character?.handleMovementAction(new MoveActionCommand(this.direction, false)); this.character?.handleMovementAction(new MoveActionCommand(this.direction));
} }
protected createCharacter() { protected createCharacter() {

View file

@ -69,6 +69,6 @@ export abstract class PlayerBase extends CommandReceiver {
} }
public destroy() { public destroy() {
this.character?.destroy(); this.character?.kill();
} }
} }

View file

@ -9,7 +9,6 @@
"downlevelIteration": true, "downlevelIteration": true,
"moduleResolution": "node", "moduleResolution": "node",
"module": "commonjs", "module": "commonjs",
"composite": true,
"lib": ["dom", "es2017"], "lib": ["dom", "es2017"],
"typeRoots": ["./types", "./node_modules/@types"] "typeRoots": ["./types", "./node_modules/@types"]
}, },

View file

@ -63,15 +63,6 @@
<div id="settings-container"> <div id="settings-container">
<section id="settings"> <section id="settings">
<label for="enable-relative-movement">
<input id="enable-relative-movement" type="checkbox" />
<img
title="Make movement be relative to the gravitational field"
alt="a dashed circle"
src="../static/circle.svg"
/>
</label>
<label for="enable-vibration"> <label for="enable-vibration">
<input id="enable-vibration" type="checkbox" /> <input id="enable-vibration" type="checkbox" />
<img <img

View file

@ -48,9 +48,6 @@ const toggleSettingsButton = document.querySelector(
const minimize = document.querySelector('#minimize') as HTMLElement; const minimize = document.querySelector('#minimize') as HTMLElement;
const maximize = document.querySelector('#maximize') as HTMLElement; const maximize = document.querySelector('#maximize') as HTMLElement;
const logoutButton = document.querySelector('#logout') as HTMLElement; const logoutButton = document.querySelector('#logout') as HTMLElement;
const enableRelativeMovement = document.querySelector(
'#enable-relative-movement',
) as HTMLInputElement;
const enableSounds = document.querySelector('#enable-sounds') as HTMLInputElement; const enableSounds = document.querySelector('#enable-sounds') as HTMLInputElement;
const enableMusic = document.querySelector('#enable-music') as HTMLInputElement; const enableMusic = document.querySelector('#enable-music') as HTMLInputElement;
const enableVibration = document.querySelector('#enable-vibration') as HTMLInputElement; const enableVibration = document.querySelector('#enable-vibration') as HTMLInputElement;
@ -123,7 +120,6 @@ const main = async () => {
serverContainer.addEventListener('scroll', applyServerContainerShadows); serverContainer.addEventListener('scroll', applyServerContainerShadows);
OptionsHandler.initialize({ OptionsHandler.initialize({
relativeMovementEnabled: enableRelativeMovement,
soundsEnabled: enableSounds, soundsEnabled: enableSounds,
vibrationEnabled: enableVibration, vibrationEnabled: enableVibration,
musicEnabled: enableMusic, musicEnabled: enableMusic,

View file

@ -35,9 +35,7 @@ export class KeyboardListener extends CommandGenerator {
if (vec2.squaredLength(movement) > 0) { if (vec2.squaredLength(movement) > 0) {
vec2.normalize(movement, movement); vec2.normalize(movement, movement);
} }
this.sendCommandToSubcribers( this.sendCommandToSubcribers(new MoveActionCommand(movement));
new MoveActionCommand(movement, OptionsHandler.options.relativeMovementEnabled),
);
} }
private normalize(key: string): string { private normalize(key: string): string {

View file

@ -75,18 +75,10 @@ export class TouchJoystickListener extends CommandGenerator {
vec2.set(movement, movement.x, -movement.y); vec2.set(movement, movement.x, -movement.y);
if (length > 10) { if (length > 10) {
this.sendCommandToSubcribers( this.sendCommandToSubcribers(
new MoveActionCommand( new MoveActionCommand(vec2.normalize(movement, movement)),
vec2.normalize(movement, movement),
OptionsHandler.options.relativeMovementEnabled,
),
); );
} else { } else {
this.sendCommandToSubcribers( this.sendCommandToSubcribers(new MoveActionCommand(vec2.create()));
new MoveActionCommand(
vec2.create(),
OptionsHandler.options.relativeMovementEnabled,
),
);
} }
}); });
@ -104,12 +96,7 @@ export class TouchJoystickListener extends CommandGenerator {
} else if (event.touches.length === 0) { } else if (event.touches.length === 0) {
this.isJoystickActive = false; this.isJoystickActive = false;
this.joystick.parentElement?.removeChild(this.joystick); this.joystick.parentElement?.removeChild(this.joystick);
this.sendCommandToSubcribers( this.sendCommandToSubcribers(new MoveActionCommand(vec2.create()));
new MoveActionCommand(
vec2.create(),
OptionsHandler.options.relativeMovementEnabled,
),
);
} }
}); });
} }

View file

@ -2,6 +2,7 @@ import { ServerInformation, serverInformationEndpoint, TransportEvents } from 's
import io from 'socket.io-client'; import io from 'socket.io-client';
import { Configuration } from './config/configuration'; import { Configuration } from './config/configuration';
import parser from 'socket.io-msgpack-parser'; import parser from 'socket.io-msgpack-parser';
import { SoundHandler, Sounds } from './sound-handler';
export type PlayerDecision = { export type PlayerDecision = {
playerName: string; playerName: string;
@ -26,6 +27,7 @@ export class JoinFormHandler {
}); });
form.onsubmit = (e) => { form.onsubmit = (e) => {
SoundHandler.play(Sounds.click);
const result: PlayerDecision = (Array.from( const result: PlayerDecision = (Array.from(
(new FormData(form) as any).entries(), (new FormData(form) as any).entries(),
) as Array<[string, any]>).reduce((result, [name, value]) => { ) as Array<[string, any]>).reduce((result, [name, value]) => {
@ -113,6 +115,7 @@ class ServerChooserOption {
this.inputElement.name = 'server'; this.inputElement.name = 'server';
this.inputElement.checked = isFirst; this.inputElement.checked = isFirst;
this.labelElement.htmlFor = url; this.labelElement.htmlFor = url;
this.labelElement.onclick = () => SoundHandler.play(Sounds.click);
this.divElement.appendChild(this.inputElement); this.divElement.appendChild(this.inputElement);
this.divElement.appendChild(this.labelElement); this.divElement.appendChild(this.labelElement);
this.labelElement.appendChild(this.serverNameElement); this.labelElement.appendChild(this.serverNameElement);

View file

@ -3,7 +3,6 @@ import { SoundHandler, Sounds } from './sound-handler';
interface Options { interface Options {
vibrationEnabled: boolean; vibrationEnabled: boolean;
soundsEnabled: boolean; soundsEnabled: boolean;
relativeMovementEnabled: boolean;
musicEnabled: boolean; musicEnabled: boolean;
} }
@ -12,7 +11,6 @@ export abstract class OptionsHandler {
private static _options: Options = { private static _options: Options = {
vibrationEnabled: true, vibrationEnabled: true,
soundsEnabled: true, soundsEnabled: true,
relativeMovementEnabled: false,
musicEnabled: true, musicEnabled: true,
}; };

View file

@ -4,14 +4,11 @@ import { Command } from '../command';
@serializable @serializable
export class MoveActionCommand extends Command { export class MoveActionCommand extends Command {
public constructor( public constructor(public readonly direction: vec2) {
public readonly direction: vec2,
public readonly isCharacterRelative: boolean,
) {
super(); super();
} }
public toArray(): Array<any> { public toArray(): Array<any> {
return [this.direction, this.isCharacterRelative]; return [this.direction];
} }
} }

View file

@ -12,10 +12,11 @@ const redPlanetColor = redColorDim;
export const settings = { export const settings = {
lightCutoffDistance: 600, lightCutoffDistance: 600,
physicsMaxStep: 8,
maxVelocityX: 1000, maxVelocityX: 1000,
maxVelocityY: 1000, maxVelocityY: 1000,
radiusSteps: 500, radiusSteps: 500,
gravityScalingForProjectiles: 5,
spawnDespawnTime: 0.7,
worldRadius: 10000, worldRadius: 10000,
objectsOnCircleLength: 0.002, objectsOnCircleLength: 0.002,
planetEdgeCount: 7, planetEdgeCount: 7,
@ -23,19 +24,19 @@ export const settings = {
loseControlTimeInSeconds: 24, loseControlTimeInSeconds: 24,
planetPointGenerationInterval: 1.5, planetPointGenerationInterval: 1.5,
planetPointGenerationValue: 1, planetPointGenerationValue: 1,
maxGravityDistance: 700, maxGravityDistance: 800,
maxGravityQ: 180, maxGravityQ: 10000,
planetControlThreshold: 0.2, planetControlThreshold: 0.2,
playerMaxHealth: 100, playerMaxHealth: 100,
maxGravityStrength: 10000, maxGravityStrength: 20000,
maxAcceleration: 10000, maxAcceleration: 60000,
playerMaxStrength: 80, playerMaxStrength: 80,
endGameDeltaScaling: 4, endGameDeltaScaling: 4,
playerDiedTimeout: 5, playerDiedTimeout: 5,
playerStrengthRegenerationPerSeconds: 60, playerStrengthRegenerationPerSeconds: 80,
projectileMaxStrength: 40, projectileMaxStrength: 40,
projectileSpeed: 4000, projectileSpeed: 4000,
projectileMaxBounceCount: 1, projectileMaxBounceCount: 2,
projectileTimeout: 3, projectileTimeout: 3,
projectileFadeSpeed: 20, projectileFadeSpeed: 20,
projectileCreationInterval: 0.1, projectileCreationInterval: 0.1,
@ -45,6 +46,7 @@ export const settings = {
declaPlanetColor, declaPlanetColor,
npcNames: [ npcNames: [
'Adam', 'Adam',
'Andrew',
'Clarence', 'Clarence',
'Elliot', 'Elliot',
'Elmer', 'Elmer',
@ -66,7 +68,6 @@ export const settings = {
'Niles', 'Niles',
'Oliver', 'Oliver',
'Blaise', 'Blaise',
'Andrew',
'Opie', 'Opie',
'Ryan', 'Ryan',
'Toby', 'Toby',
@ -112,6 +113,5 @@ export const settings = {
paletteDim: [declaColorDim, neutralColor, redColorDim], paletteDim: [declaColorDim, neutralColor, redColorDim],
targetPhysicsDeltaTimeInMilliseconds: 20, targetPhysicsDeltaTimeInMilliseconds: 20,
minPhysicsSleepTime: 4, minPhysicsSleepTime: 4,
velocityAttenuation: 0.25,
inViewAreaSize: 1920 * 1080 * 3, inViewAreaSize: 1920 * 1080 * 3,
}; };