242 lines
8.3 KiB
JavaScript
242 lines
8.3 KiB
JavaScript
var gameState = require('./gamestate');
|
|
|
|
const PHYSICS_FPS = 30;
|
|
var GRAVITY = 9.81; // m/s^2
|
|
GRAVITY/=3;
|
|
const GRAVITY_MS = GRAVITY / 1000; // m/ms^2
|
|
const HOVER_PROP_SPEED = 3;
|
|
const MIN_PROP_SPEED = -3;
|
|
const MAX_PROP_SPEED = 10;
|
|
const DRONE_THRUST_PER_PROP_SPEED = GRAVITY_MS / HOVER_PROP_SPEED;
|
|
const PROP_CTRL_MS = 9000; // Time to go from min to max by holding down the control
|
|
const PROP_CHANGE_PER_MS = (MAX_PROP_SPEED - MIN_PROP_SPEED) / PROP_CTRL_MS;
|
|
const PHYSICS_MS = Math.round(1000 / PHYSICS_FPS);
|
|
const JUMP_VELOCITY = 1; // m/s
|
|
const DRONE_SENSITIVITY = 0.01; // rad/s/s
|
|
const DRONE_SENSITIVITY_MS = DRONE_SENSITIVITY/1000; // rad/s/ms
|
|
const DRONE_STABILITY_FACTOR = 2/3;
|
|
var BABYLON = null;
|
|
import('@babylonjs/core').then(i=>BABYLON=i);
|
|
|
|
// Update game state at approximately 30 FPS
|
|
setInterval(updateGameState, PHYSICS_MS);
|
|
|
|
function updateGameState() {
|
|
var deltaT = PHYSICS_MS;
|
|
gameState.timestamp = Date.now();
|
|
|
|
// Apply control physics
|
|
Object.values(gameState.clients).forEach(client => applyControlPhysics(client, deltaT));
|
|
|
|
// Apply physics to all models
|
|
Object.values(gameState.models).forEach(model => {
|
|
if (model.type === 'human') applyHumanPhysics(model, deltaT);
|
|
if (model.type === 'drone') applyDronePhysics(model, deltaT);
|
|
});
|
|
|
|
}
|
|
|
|
function applyControlPhysics(client, deltaT) {
|
|
const model = gameState.models[client.controllingId];
|
|
//console.log('control model',model);
|
|
if (!model) return;
|
|
if (model.type === 'human') applyHumanControlPhysics(client, model, deltaT);
|
|
if (model.type === 'drone') applyDroneControlPhysics(client, model, deltaT);
|
|
}
|
|
|
|
function applyHumanPhysics(model, deltaT) {
|
|
// Apply gravity
|
|
model.dz -= GRAVITY_MS * deltaT;
|
|
|
|
// Apply movement
|
|
model.x += model.dx * deltaT;
|
|
model.y += model.dy * deltaT;
|
|
model.z += model.dz * deltaT;
|
|
|
|
|
|
// Apply walk slowing
|
|
var backthrust = 0.1*GRAVITY_MS;
|
|
var velocity = Math.sqrt(model.dx*model.dx+model.dy*model.dy+model.dz*model.dz);
|
|
var nx = model.dx/velocity;
|
|
var ny = model.dy/velocity;
|
|
var nz = model.dz/velocity;
|
|
var backx = -nx*deltaT*backthrust;
|
|
var backy = -ny*deltaT*backthrust;
|
|
var backz = -nz*deltaT*backthrust;
|
|
if(backx>model.dx) {
|
|
model.dx=0;
|
|
model.dy=0;
|
|
model.dz=0;
|
|
}
|
|
else {
|
|
model.dx=backx;
|
|
model.dy=backy;
|
|
model.dz=backz;
|
|
}
|
|
|
|
// Prevent going below ground
|
|
if (model.z < 1.8) {
|
|
model.z = 1.8;
|
|
model.dz = 0;
|
|
}
|
|
}
|
|
|
|
function applyDronePhysics(model, deltaT) {
|
|
// Apply gravity
|
|
//console.log('pre physics',model);
|
|
|
|
// Apply thrust
|
|
if(BABYLON) {
|
|
model.dz -= GRAVITY_MS * deltaT;
|
|
const thrust = model.propSpeed * DRONE_THRUST_PER_PROP_SPEED;
|
|
const thrustVector = new BABYLON.Vector3(0, thrust, 0);
|
|
const rotationMatrix = BABYLON.Matrix.RotationYawPitchRoll(model.yaw, model.pitch, model.roll);
|
|
const rotatedThrustVector = BABYLON.Vector3.TransformNormal(thrustVector, rotationMatrix);
|
|
//console.log('prop vector',rotatedThrustVector);
|
|
|
|
model.dx += rotatedThrustVector.x * deltaT;
|
|
model.dy += rotatedThrustVector.z * deltaT;
|
|
model.dz += rotatedThrustVector.y * deltaT;
|
|
//console.log({gravity:GRAVITY_MS*deltaT,thrust:rotatedThrustVector.y*deltaT});
|
|
}
|
|
|
|
// Apply movement
|
|
model.x += model.dx * deltaT;
|
|
model.y += model.dy * deltaT;
|
|
model.z += model.dz * deltaT;
|
|
model.pitch += model.dpitch * deltaT;
|
|
model.yaw += model.dyaw * deltaT;
|
|
model.roll += model.droll * deltaT;
|
|
//console.log({dronedz:model.dz});
|
|
|
|
// Apply drag (air resistance)
|
|
//const dragFactor = 0.1 * deltaT;
|
|
//model.dx *= (1 - dragFactor);
|
|
//model.dy *= (1 - dragFactor);
|
|
//model.dz *= (1 - dragFactor);
|
|
//model.dpitch *= (1 - dragFactor);
|
|
//model.dyaw *= (1 - dragFactor);
|
|
//model.droll *= (1 - dragFactor);
|
|
//Apply linear drag
|
|
var velocity = Math.sqrt(model.dx*model.dx+model.dy*model.dy+model.dz*model.dz);
|
|
if(velocity) {
|
|
var drag_coef=500;
|
|
var dragForce = drag_coef*velocity*velocity;
|
|
var dragForce_ms = dragForce/1000;
|
|
var nx = model.dx/velocity;
|
|
var ny = model.dy/velocity;
|
|
var nz = model.dz/velocity;
|
|
model.dx-=nx*dragForce_ms*deltaT;
|
|
model.dy-=ny*dragForce_ms*deltaT;
|
|
model.dz-=nz*dragForce_ms*deltaT;
|
|
//console.log({velocity,drag_coef,dragForce,nx,ny,nz,deltaT});
|
|
}
|
|
//Apply rotational drag
|
|
const rotationSpeed = DRONE_STABILITY_FACTOR * DRONE_SENSITIVITY_MS * deltaT;
|
|
if(model.droll>0) {
|
|
model.droll-=rotationSpeed;
|
|
if(model.droll<0) model.droll=0;
|
|
}
|
|
else if(model.droll<0) {
|
|
model.droll+=rotationSpeed;
|
|
if(model.droll>0) model.droll=0;
|
|
}
|
|
if(model.dpitch>0) {
|
|
model.dpitch-=rotationSpeed;
|
|
if(model.dpitch<0) model.dpitch=0;
|
|
}
|
|
else if(model.dpitch<0) {
|
|
model.dpitch+=rotationSpeed;
|
|
if(model.dpitch>0) model.dpitch=0;
|
|
}
|
|
if(model.dyaw>0) {
|
|
model.dyaw-=rotationSpeed;
|
|
if(model.dyaw<0) model.dyaw=0;
|
|
}
|
|
else if(model.dyaw<0) {
|
|
model.dyaw+=rotationSpeed;
|
|
if(model.dyaw>0) model.dyaw=0;
|
|
}
|
|
// Apply prop speed centering
|
|
if(model.propSpeed>HOVER_PROP_SPEED) {
|
|
model.propSpeed-=PROP_CHANGE_PER_MS*DRONE_STABILITY_FACTOR*deltaT;
|
|
if(model.propSpeed<HOVER_PROP_SPEED) model.propSpeed=HOVER_PROP_SPEED;
|
|
}
|
|
else if(model.propSpeed<HOVER_PROP_SPEED) {
|
|
model.propSpeed+=PROP_CHANGE_PER_MS*DRONE_STABILITY_FACTOR*deltaT;
|
|
if(model.propSpeed>HOVER_PROP_SPEED) model.propSpeed=HOVER_PROP_SPEED;
|
|
}
|
|
|
|
// Prevent going below ground
|
|
if (model.z < 0) {
|
|
//console.log('Hit ground',{z:model.z,dz:model.dz});
|
|
model.z = 0;
|
|
model.dz = 0;
|
|
}
|
|
//console.log('post physics',model);
|
|
}
|
|
|
|
function applyHumanControlPhysics(client, model, deltaT) {
|
|
//console.log('applyHumanControlPhysics');
|
|
var walkthrust_ms = 0.3*GRAVITY_MS
|
|
var radpersec = Math.PI;
|
|
var radperms = radpersec/1000;
|
|
|
|
|
|
if (client.controlState['w']) {
|
|
model.dx += Math.sin(model.yaw)*walkthrust_ms*deltaT;
|
|
model.dy += Math.cos(model.yaw)*walkthrust_ms*deltaT;
|
|
}
|
|
if (client.controlState['s']) {
|
|
model.dx -= Math.sin(model.yaw)*walkthrust_ms*deltaT;
|
|
model.dy -= Math.cos(model.yaw)*walkthrust_ms*deltaT;
|
|
}
|
|
if (client.controlState['a']) {
|
|
model.dx -= Math.cos(model.yaw)*walkthrust_ms*deltaT;
|
|
model.dy += Math.sin(model.yaw)*walkthrust_ms*deltaT;
|
|
}
|
|
if (client.controlState['d']) {
|
|
model.dx += Math.cos(model.yaw)*walkthrust_ms*deltaT;
|
|
model.dy -= Math.sin(model.yaw)*walkthrust_ms*deltaT;
|
|
}
|
|
if (client.controlState['ArrowLeft']) model.yaw -= radperms*deltaT;
|
|
if (client.controlState['ArrowRight']) model.yaw += radperms*deltaT;
|
|
if (client.controlState['ArrowUp']) model.pitch -= radperms*deltaT;
|
|
if (client.controlState['ArrowDown']) model.pitch += radperms*deltaT;
|
|
if (client.controlState['j'] && Math.abs(model.z-1.8)<0.1) {
|
|
model.dz = JUMP_VELOCITY;
|
|
}
|
|
}
|
|
|
|
function applyDroneControlPhysics(client, model, deltaT) {
|
|
const rotationSpeed = DRONE_SENSITIVITY_MS * deltaT; // 1 rad/s
|
|
|
|
if (client.controlState['d']) model.droll -= rotationSpeed;
|
|
if (client.controlState['a']) model.droll += rotationSpeed;
|
|
if (client.controlState['s']) model.dpitch -= rotationSpeed;
|
|
if (client.controlState['w']) model.dpitch += rotationSpeed;
|
|
if (client.controlState['q']) model.dyaw -= rotationSpeed;
|
|
if (client.controlState['e']) model.dyaw += rotationSpeed;
|
|
if (client.controlState['ArrowLeft']) model.dyaw -= rotationSpeed;
|
|
if (client.controlState['ArrowRight']) model.dyaw += rotationSpeed;
|
|
if (client.controlState['f']) {
|
|
model.roll = model.pitch = 0;
|
|
model.droll = model.dpitch = model.dyaw = 0;
|
|
}
|
|
if (client.controlState['g']) {
|
|
model.dx = model.dy = model.dz = 0;
|
|
model.roll = model.pitch = 0;
|
|
model.droll = model.dpitch = model.dyaw = 0;
|
|
}
|
|
if (client.controlState['ArrowUp']) {
|
|
model.propSpeed += PROP_CHANGE_PER_MS * deltaT;
|
|
if (model.propSpeed > MAX_PROP_SPEED) model.propSpeed = MAX_PROP_SPEED;
|
|
}
|
|
if (client.controlState['ArrowDown']) {
|
|
model.propSpeed -= PROP_CHANGE_PER_MS * deltaT;
|
|
if (model.propSpeed < MIN_PROP_SPEED) model.propSpeed = MIN_PROP_SPEED;
|
|
}
|
|
}
|
|
|
|
module.exports = {updateGameState };
|