|
@@ -74,7 +74,7 @@
|
|
|
|
|
|
const GRAVITY = 30;
|
|
const GRAVITY = 30;
|
|
|
|
|
|
- const NUM_SPHERES = 20;
|
|
|
|
|
|
+ const NUM_SPHERES = 100;
|
|
const SPHERE_RADIUS = 0.2;
|
|
const SPHERE_RADIUS = 0.2;
|
|
|
|
|
|
const STEPS_PER_FRAME = 5;
|
|
const STEPS_PER_FRAME = 5;
|
|
@@ -105,9 +105,14 @@
|
|
const playerDirection = new THREE.Vector3();
|
|
const playerDirection = new THREE.Vector3();
|
|
|
|
|
|
let playerOnFloor = false;
|
|
let playerOnFloor = false;
|
|
|
|
+ let mouseTime = 0;
|
|
|
|
|
|
const keyStates = {};
|
|
const keyStates = {};
|
|
|
|
|
|
|
|
+ const t_vector = new THREE.Vector3();
|
|
|
|
+ const t_vector2 = new THREE.Vector3();
|
|
|
|
+ const t_vector3 = new THREE.Vector3();
|
|
|
|
+
|
|
document.addEventListener( 'keydown', ( event ) => {
|
|
document.addEventListener( 'keydown', ( event ) => {
|
|
|
|
|
|
keyStates[ event.code ] = true;
|
|
keyStates[ event.code ] = true;
|
|
@@ -124,6 +129,14 @@
|
|
|
|
|
|
document.body.requestPointerLock();
|
|
document.body.requestPointerLock();
|
|
|
|
|
|
|
|
+ mouseTime = performance.now();
|
|
|
|
+
|
|
|
|
+ } );
|
|
|
|
+
|
|
|
|
+ document.addEventListener( 'mouseup', () => {
|
|
|
|
+
|
|
|
|
+ throwBall();
|
|
|
|
+
|
|
} );
|
|
} );
|
|
|
|
|
|
document.body.addEventListener( 'mousemove', ( event ) => {
|
|
document.body.addEventListener( 'mousemove', ( event ) => {
|
|
@@ -148,20 +161,26 @@
|
|
|
|
|
|
}
|
|
}
|
|
|
|
|
|
- document.addEventListener( 'click', () => {
|
|
|
|
|
|
+ function throwBall() {
|
|
|
|
|
|
const sphere = spheres[ sphereIdx ];
|
|
const sphere = spheres[ sphereIdx ];
|
|
|
|
|
|
camera.getWorldDirection( playerDirection );
|
|
camera.getWorldDirection( playerDirection );
|
|
|
|
|
|
- sphere.collider.center.copy( playerCollider.end );
|
|
|
|
- sphere.velocity.copy( playerDirection ).multiplyScalar( 30 );
|
|
|
|
|
|
+ sphere.collider.center.copy( playerCollider.end ).addScaledVector( playerDirection, playerCollider.radius * 1.5 );
|
|
|
|
+
|
|
|
|
+ // throw the ball with more force if we hold the button longer, and if we move forward
|
|
|
|
+
|
|
|
|
+ let impulse = 15 + 30 * ( 1 - Math.exp( ( mouseTime - performance.now() ) * 0.001 ));
|
|
|
|
+
|
|
|
|
+ sphere.velocity.copy( playerDirection ).multiplyScalar( impulse );
|
|
|
|
+ sphere.velocity.addScaledVector( playerVelocity, 2 );
|
|
|
|
|
|
sphereIdx = ( sphereIdx + 1 ) % spheres.length;
|
|
sphereIdx = ( sphereIdx + 1 ) % spheres.length;
|
|
|
|
|
|
- } );
|
|
|
|
|
|
+ }
|
|
|
|
|
|
- function playerCollitions() {
|
|
|
|
|
|
+ function playerCollisions() {
|
|
|
|
|
|
const result = worldOctree.capsuleIntersect( playerCollider );
|
|
const result = worldOctree.capsuleIntersect( playerCollider );
|
|
|
|
|
|
@@ -185,33 +204,67 @@
|
|
|
|
|
|
function updatePlayer( deltaTime ) {
|
|
function updatePlayer( deltaTime ) {
|
|
|
|
|
|
- if ( playerOnFloor ) {
|
|
|
|
-
|
|
|
|
- const damping = Math.exp( - 3 * deltaTime ) - 1;
|
|
|
|
- playerVelocity.addScaledVector( playerVelocity, damping );
|
|
|
|
|
|
+ let damping = Math.exp( - 4 * deltaTime ) - 1;
|
|
|
|
|
|
- } else {
|
|
|
|
|
|
+ if ( ! playerOnFloor ) {
|
|
|
|
|
|
playerVelocity.y -= GRAVITY * deltaTime;
|
|
playerVelocity.y -= GRAVITY * deltaTime;
|
|
|
|
|
|
|
|
+ // small air resistance
|
|
|
|
+ damping *= 0.1;
|
|
|
|
+
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ playerVelocity.addScaledVector( playerVelocity, damping );
|
|
|
|
+
|
|
const deltaPosition = playerVelocity.clone().multiplyScalar( deltaTime );
|
|
const deltaPosition = playerVelocity.clone().multiplyScalar( deltaTime );
|
|
playerCollider.translate( deltaPosition );
|
|
playerCollider.translate( deltaPosition );
|
|
|
|
|
|
- playerCollitions();
|
|
|
|
|
|
+ playerCollisions();
|
|
|
|
|
|
camera.position.copy( playerCollider.end );
|
|
camera.position.copy( playerCollider.end );
|
|
|
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ function playerSphereCollision( sphere ) {
|
|
|
|
+
|
|
|
|
+ const center = t_vector.addVectors( playerCollider.start, playerCollider.end ).multiplyScalar( 0.5 );
|
|
|
|
+
|
|
|
|
+ const sphere_center = sphere.collider.center;
|
|
|
|
+
|
|
|
|
+ const r = playerCollider.radius + sphere.collider.radius;
|
|
|
|
+ const r2 = r * r;
|
|
|
|
+
|
|
|
|
+ // approximation: player = 3 spheres
|
|
|
|
+
|
|
|
|
+ for ( let point of [ playerCollider.start, playerCollider.end, center ] ) {
|
|
|
|
+
|
|
|
|
+ const d2 = point.distanceToSquared( sphere_center );
|
|
|
|
+
|
|
|
|
+ if ( d2 < r2 ) {
|
|
|
|
+
|
|
|
|
+ const normal = t_vector.subVectors( point, sphere_center ).normalize();
|
|
|
|
+ const v1 = t_vector2.copy( normal ).multiplyScalar( normal.dot( playerVelocity ) );
|
|
|
|
+ const v2 = t_vector3.copy( normal ).multiplyScalar( normal.dot( sphere.velocity ) );
|
|
|
|
+
|
|
|
|
+ playerVelocity.add( v2 ).sub( v1 );
|
|
|
|
+ sphere.velocity.add( v1 ).sub( v2 );
|
|
|
|
+
|
|
|
|
+ const d = ( r - Math.sqrt( d2 ) ) / 2;
|
|
|
|
+ sphere_center.addScaledVector( normal, - d );
|
|
|
|
+
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
function spheresCollisions() {
|
|
function spheresCollisions() {
|
|
|
|
|
|
- for ( let i = 0; i < spheres.length; i ++ ) {
|
|
|
|
|
|
+ for ( let i = 0, length = spheres.length; i < length; i ++ ) {
|
|
|
|
|
|
const s1 = spheres[ i ];
|
|
const s1 = spheres[ i ];
|
|
|
|
|
|
- for ( let j = i + 1; j < spheres.length; j ++ ) {
|
|
|
|
|
|
+ for ( let j = i + 1; j < length; j ++ ) {
|
|
|
|
|
|
const s2 = spheres[ j ];
|
|
const s2 = spheres[ j ];
|
|
|
|
|
|
@@ -221,9 +274,10 @@
|
|
|
|
|
|
if ( d2 < r2 ) {
|
|
if ( d2 < r2 ) {
|
|
|
|
|
|
- const normal = s1.collider.clone().center.sub( s2.collider.center ).normalize();
|
|
|
|
- const v1 = normal.clone().multiplyScalar( normal.dot( s1.velocity ) );
|
|
|
|
- const v2 = normal.clone().multiplyScalar( normal.dot( s2.velocity ) );
|
|
|
|
|
|
+ const normal = t_vector.subVectors( s1.collider.center, s2.collider.center ).normalize();
|
|
|
|
+ const v1 = t_vector2.copy( normal ).multiplyScalar( normal.dot( s1.velocity ) );
|
|
|
|
+ const v2 = t_vector3.copy( normal ).multiplyScalar( normal.dot( s2.velocity ) );
|
|
|
|
+
|
|
s1.velocity.add( v2 ).sub( v1 );
|
|
s1.velocity.add( v2 ).sub( v1 );
|
|
s2.velocity.add( v1 ).sub( v2 );
|
|
s2.velocity.add( v1 ).sub( v2 );
|
|
|
|
|
|
@@ -242,7 +296,7 @@
|
|
|
|
|
|
function updateSpheres( deltaTime ) {
|
|
function updateSpheres( deltaTime ) {
|
|
|
|
|
|
- spheres.forEach( sphere =>{
|
|
|
|
|
|
+ spheres.forEach( sphere => {
|
|
|
|
|
|
sphere.collider.center.addScaledVector( sphere.velocity, deltaTime );
|
|
sphere.collider.center.addScaledVector( sphere.velocity, deltaTime );
|
|
|
|
|
|
@@ -262,11 +316,17 @@
|
|
const damping = Math.exp( - 1.5 * deltaTime ) - 1;
|
|
const damping = Math.exp( - 1.5 * deltaTime ) - 1;
|
|
sphere.velocity.addScaledVector( sphere.velocity, damping );
|
|
sphere.velocity.addScaledVector( sphere.velocity, damping );
|
|
|
|
|
|
- spheresCollisions();
|
|
|
|
|
|
+ playerSphereCollision( sphere );
|
|
|
|
+
|
|
|
|
+ } );
|
|
|
|
+
|
|
|
|
+ spheresCollisions();
|
|
|
|
+
|
|
|
|
+ for ( let sphere of spheres ) {
|
|
|
|
|
|
sphere.mesh.position.copy( sphere.collider.center );
|
|
sphere.mesh.position.copy( sphere.collider.center );
|
|
|
|
|
|
- } );
|
|
|
|
|
|
+ }
|
|
|
|
|
|
}
|
|
}
|
|
|
|
|
|
@@ -293,33 +353,34 @@
|
|
|
|
|
|
function controls( deltaTime ) {
|
|
function controls( deltaTime ) {
|
|
|
|
|
|
- const speed = 25;
|
|
|
|
|
|
+ // gives a bit of air control
|
|
|
|
+ let speedDelta = deltaTime * ( playerOnFloor ? 25 : 8 );
|
|
|
|
|
|
- if ( playerOnFloor ) {
|
|
|
|
|
|
+ if ( keyStates[ 'KeyW' ] ) {
|
|
|
|
|
|
- if ( keyStates[ 'KeyW' ] ) {
|
|
|
|
|
|
+ playerVelocity.add( getForwardVector().multiplyScalar( speedDelta ) );
|
|
|
|
|
|
- playerVelocity.add( getForwardVector().multiplyScalar( speed * deltaTime ) );
|
|
|
|
|
|
+ }
|
|
|
|
|
|
- }
|
|
|
|
|
|
+ if ( keyStates[ 'KeyS' ] ) {
|
|
|
|
|
|
- if ( keyStates[ 'KeyS' ] ) {
|
|
|
|
|
|
+ playerVelocity.add( getForwardVector().multiplyScalar( - speedDelta ) );
|
|
|
|
|
|
- playerVelocity.add( getForwardVector().multiplyScalar( - speed * deltaTime ) );
|
|
|
|
|
|
+ }
|
|
|
|
|
|
- }
|
|
|
|
|
|
+ if ( keyStates[ 'KeyA' ] ) {
|
|
|
|
|
|
- if ( keyStates[ 'KeyA' ] ) {
|
|
|
|
|
|
+ playerVelocity.add( getSideVector().multiplyScalar( - speedDelta ) );
|
|
|
|
|
|
- playerVelocity.add( getSideVector().multiplyScalar( - speed * deltaTime ) );
|
|
|
|
|
|
+ }
|
|
|
|
|
|
- }
|
|
|
|
|
|
+ if ( keyStates[ 'KeyD' ] ) {
|
|
|
|
|
|
- if ( keyStates[ 'KeyD' ] ) {
|
|
|
|
|
|
+ playerVelocity.add( getSideVector().multiplyScalar( speedDelta ) );
|
|
|
|
|
|
- playerVelocity.add( getSideVector().multiplyScalar( speed * deltaTime ) );
|
|
|
|
|
|
+ }
|
|
|
|
|
|
- }
|
|
|
|
|
|
+ if ( playerOnFloor ) {
|
|
|
|
|
|
if ( keyStates[ 'Space' ] ) {
|
|
if ( keyStates[ 'Space' ] ) {
|
|
|
|
|