Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 22 additions & 1 deletion js/Controls.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,11 @@ export class Controls {
this.x = 0;
this.z = 0;

this.prevSpace = false;
this.prevGamepadFire = false;
this.prevT = false;
this.prevGamepadTransform = false;

// Touch state
this.touchActive = false;
this.touchDirX = 0;
Expand Down Expand Up @@ -112,6 +117,14 @@ export class Controls {
if ( this.keys[ 'KeyW' ] || this.keys[ 'ArrowUp' ] ) z += 1;
if ( this.keys[ 'KeyS' ] || this.keys[ 'ArrowDown' ] ) z -= 1;

const spaceDown = !! this.keys[ 'Space' ];
let fire = spaceDown && ! this.prevSpace;
this.prevSpace = spaceDown;

const tDown = !! this.keys[ 'KeyT' ];
let transform = tDown && ! this.prevT;
this.prevT = tDown;

// Gamepad

const gamepads = navigator.getGamepads();
Expand All @@ -128,6 +141,14 @@ export class Controls {

if ( rt > 0.1 || lt > 0.1 ) z = rt - lt;

const gpFire = gp.buttons[ 0 ] ? !! gp.buttons[ 0 ].pressed : false;
if ( gpFire && ! this.prevGamepadFire ) fire = true;
this.prevGamepadFire = gpFire;

const gpTransform = gp.buttons[ 1 ] ? !! gp.buttons[ 1 ].pressed : false;
if ( gpTransform && ! this.prevGamepadTransform ) transform = true;
this.prevGamepadTransform = gpTransform;

break;

}
Expand All @@ -152,7 +173,7 @@ export class Controls {
this.x = x;
this.z = z;

return { x, z, touchActive: this.touchActive };
return { x, z, touchActive: this.touchActive, fire, transform };

}

Expand Down
126 changes: 126 additions & 0 deletions js/HitFX.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
import * as THREE from 'three';

const RING_COUNT = 8;
const STAR_COUNT = 48;
const STARS_PER_BURST = 8;

const RING_LIFETIME = 0.45;
const RING_MAX_SCALE = 2.8;
const STAR_LIFETIME = 0.7;
const STAR_SPEED_MIN = 3;
const STAR_SPEED_RANGE = 3;
const STAR_UP_MIN = 2.5;
const STAR_UP_RANGE = 2;
const STAR_GRAVITY = 11;

const _ringGeom = new THREE.TorusGeometry( 0.4, 0.07, 8, 28 );
const _starGeom = new THREE.IcosahedronGeometry( 0.15, 0 );

export class HitFX {

constructor( scene ) {

this.scene = scene;

this.rings = [];
for ( let i = 0; i < RING_COUNT; i ++ ) {

const mat = new THREE.MeshBasicMaterial( { color: 0xffee55, transparent: true, opacity: 0, depthWrite: false } );
const mesh = new THREE.Mesh( _ringGeom, mat );
mesh.rotation.x = - Math.PI / 2;
mesh.visible = false;
scene.add( mesh );
this.rings.push( { mesh, material: mat, life: 0 } );

}
this.ringIndex = 0;

this.stars = [];
for ( let i = 0; i < STAR_COUNT; i ++ ) {

const mat = new THREE.MeshStandardMaterial( {
color: 0xfff29a, emissive: 0xffcc33, emissiveIntensity: 1.2,
roughness: 0.3, transparent: true, opacity: 0,
} );
const mesh = new THREE.Mesh( _starGeom, mat );
mesh.visible = false;
scene.add( mesh );
this.stars.push( {
mesh, material: mat, life: 0,
vx: 0, vy: 0, vz: 0,
rotX: 0, rotY: 0,
} );

}
this.starIndex = 0;

}

burst( x, y, z ) {

const ring = this.rings[ this.ringIndex ];
this.ringIndex = ( this.ringIndex + 1 ) % RING_COUNT;
ring.mesh.visible = true;
ring.mesh.position.set( x, y + 0.1, z );
ring.mesh.scale.setScalar( 0.2 );
ring.material.opacity = 1;
ring.life = RING_LIFETIME;

for ( let i = 0; i < STARS_PER_BURST; i ++ ) {

const star = this.stars[ this.starIndex ];
this.starIndex = ( this.starIndex + 1 ) % STAR_COUNT;

const angle = ( i / STARS_PER_BURST ) * Math.PI * 2 + Math.random() * 0.4;
const speed = STAR_SPEED_MIN + Math.random() * STAR_SPEED_RANGE;

star.mesh.visible = true;
star.mesh.position.set( x, y + 0.3, z );
star.mesh.scale.setScalar( 0.8 + Math.random() * 0.6 );
star.material.opacity = 1;
star.vx = Math.cos( angle ) * speed;
star.vz = Math.sin( angle ) * speed;
star.vy = STAR_UP_MIN + Math.random() * STAR_UP_RANGE;
star.rotX = ( Math.random() - 0.5 ) * 20;
star.rotY = ( Math.random() - 0.5 ) * 20;
star.life = STAR_LIFETIME;

}

}

update( dt ) {

for ( const r of this.rings ) {

if ( r.life <= 0 ) continue;
r.life -= dt;
const prog = 1 - Math.max( 0, r.life / RING_LIFETIME );
r.mesh.scale.setScalar( 0.2 + prog * RING_MAX_SCALE );
r.material.opacity = Math.max( 0, 1 - prog );
if ( r.life <= 0 ) r.mesh.visible = false;

}

for ( const s of this.stars ) {

if ( s.life <= 0 ) continue;
s.life -= dt;

s.mesh.position.x += s.vx * dt;
s.mesh.position.y += s.vy * dt;
s.mesh.position.z += s.vz * dt;
s.vy -= STAR_GRAVITY * dt;

s.mesh.rotation.x += s.rotX * dt;
s.mesh.rotation.y += s.rotY * dt;

s.material.opacity = Math.max( 0, s.life / STAR_LIFETIME );

if ( s.life <= 0 ) s.mesh.visible = false;

}

}

}
90 changes: 90 additions & 0 deletions js/MuzzleFlash.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
import * as THREE from 'three';

const POOL_SIZE = 8;
const LIFETIME = 0.11;

const _coreGeom = new THREE.IcosahedronGeometry( 0.22, 0 );
const _flareGeom = new THREE.PlaneGeometry( 0.9, 0.9 );

export class MuzzleFlash {

constructor( scene ) {

this.scene = scene;
this.cores = [];
this.flares = [];
this.index = 0;

for ( let i = 0; i < POOL_SIZE; i ++ ) {

const coreMat = new THREE.MeshBasicMaterial( {
color: 0xffc266, transparent: true, opacity: 0, depthWrite: false,
} );
const core = new THREE.Mesh( _coreGeom, coreMat );
core.visible = false;
scene.add( core );

const flareMat = new THREE.MeshBasicMaterial( {
color: 0xfff1b0, transparent: true, opacity: 0, depthWrite: false, depthTest: false,
} );
const flare = new THREE.Mesh( _flareGeom, flareMat );
flare.visible = false;
scene.add( flare );

this.cores.push( { mesh: core, material: coreMat, life: 0 } );
this.flares.push( { mesh: flare, material: flareMat, life: 0 } );

}

}

burst( x, y, z ) {

const i = this.index;
this.index = ( this.index + 1 ) % POOL_SIZE;

const core = this.cores[ i ];
core.mesh.visible = true;
core.mesh.position.set( x, y, z );
core.mesh.scale.setScalar( 1 );
core.material.opacity = 1;
core.life = LIFETIME;

const flare = this.flares[ i ];
flare.mesh.visible = true;
flare.mesh.position.set( x, y, z );
flare.mesh.rotation.z = Math.random() * Math.PI;
flare.mesh.scale.setScalar( 1.2 );
flare.material.opacity = 1;
flare.life = LIFETIME;

}

update( dt, cameraQuat ) {

for ( const c of this.cores ) {

if ( c.life <= 0 ) continue;
c.life -= dt;
const t = Math.max( 0, c.life / LIFETIME );
c.mesh.scale.setScalar( 0.4 + t * 0.8 );
c.material.opacity = t;
if ( c.life <= 0 ) c.mesh.visible = false;

}

for ( const f of this.flares ) {

if ( f.life <= 0 ) continue;
f.life -= dt;
const t = Math.max( 0, f.life / LIFETIME );
if ( cameraQuat ) f.mesh.quaternion.copy( cameraQuat );
f.mesh.scale.setScalar( 0.5 + ( 1 - t ) * 1.6 );
f.material.opacity = t * 0.9;
if ( f.life <= 0 ) f.mesh.visible = false;

}

}

}
Loading