Skip to content

Instantly share code, notes, and snippets.

@crsnbrt
Created January 4, 2016 15:28
Show Gist options
  • Save crsnbrt/0816594ae999eb08b095 to your computer and use it in GitHub Desktop.
Save crsnbrt/0816594ae999eb08b095 to your computer and use it in GitHub Desktop.
Death Star Trench Run
nav.grid
.col-1-4
ul.controls-left
li: button.btn-pause.active
li: button.btn-audio
.col-1-2.center.banner
h1 Death Star Trench Run
a(target="_blank", href="https://www.youtube.com/watch?v=SEEOcqbeclg") For full effect, open this in another tab
p
.control: <strong>Move:</strong> WASD or ARROWS
.control: <strong>Laser:</strong> SPACE
.control: <strong>Computer:</strong> TAB
p Fend off the rebels until their base is within firing range! Press Any Key To Start!
.col-1-4
ul.controls-right.right
li: button.btn-laser
li: button.btn-computer <i></i>
.computer.hidden
.hits.left
img(src="data:image/png;base64,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")
.x &times;
.xwing-count.number.number-first 0
.xwing-count.number.number-last 0
.counter
span.number.number-0 0
span.number.number-1 0
span.number.number-2 0
span.number.number-3 0
span.number.number-4 0
span.number.number-5 0
var scene, camera, renderer, controls,
light, shadowLight, backLight, trench,
stars, yavin, moon, lasers, laser_lights,
beam, xwing, fighters;
//=========================================
// SETTINGS
//=========================================
var KEYS = {
W: {code: '87', pressed: false},
A: {code: '65', pressed: false},
S: {code: '83', pressed: false},
D: {code: '68', pressed: false},
P: {code: '80', pressed: false},
up: {code: '38', pressed: false},
down: {code: '40', pressed: false},
left: {code: '37', pressed: false},
right: {code: '39', pressed: false},
space: {code: '32', pressed: false},
tab: {code: '9', pressed: false}
};
var SOUNDS = {
tiefighter: new Audio("https://s3.amazonaws.com/cbritt/codepen/fighter.mp3"),
computer: new Audio("https://s3.amazonaws.com/cbritt/codepen/computer.mp3"),
explosion: new Audio("https://s3.amazonaws.com/cbritt/codepen/exp-2.mp3"),
xwing: new Audio("https://s3.amazonaws.com/cbritt/codepen/xwing.mp3"),
laser: new Audio("https://s3.amazonaws.com/cbritt/codepen/laser.mp3"),
lasers: []
};
var MODES = {
COMPUTER: 'computer',
SOLID: 'solid',
LOSE: 'lose',
WIN: 'win'
};
var COLORS = {
WHITE: 0xffffff,
WHITE_BLUE: 0xDBE6FF,
BLACK: 0x000000,
GREY: 0x999999,
RED: 0xFF3333,
GREEN: 0x00FF00,
BLUE: 0x2E70FF,
YELLOW: 0xE0B300,
DARK_GREY: 0x0e0e11,
ORANGE: 0xFFAD14,
BRIGHT_RED: 0xFF0000
};
var TEXTURES = {
moon: "https://s3.amazonaws.com/cbritt/codepen/ZQhIPUP.jpg",
stars: "https://s3.amazonaws.com/cbritt/codepen/Y1ocEbG.png",
yavin: "https://s3.amazonaws.com/cbritt/codepen/m41aDH8.jpg"
};
var CONFIG = {
debug: false,
paused: true,
audio: true,
speed: 3,
fov: 90,
view_distance: 5000,
mode: MODES.SOLID,
fog_density: 0.01,
module_count: 40,
module_width: 5,
module_height: 50,
module_length: 51,
trench_width: 40,
detail_max: 20,
detail_offset: 4,
laser_speed: 14,
laser_max: 8,
laser_length: 50,
laser_color: COLORS.GREEN,
laser_beam_offset: 1.5,
strafe_speed: 0.7,
strafe_max: 12,
fighter_max: 1,
fighter_speed: 3,
xwing_remaining: 30,
xwing_hits: 0,
explosion_speed: 3,
explosion_radius: 50,
explosion_particles: 200,
explosion_color: COLORS.ORANGE,
moon_speed: 0.1,
beam_speed: 100,
counter: 5400
};
CONFIG._counter = CONFIG.counter;
CONFIG.trench_length = CONFIG.module_count*CONFIG.module_length;
CONFIG.camera_start = new THREE.Vector3(0, CONFIG.module_height/4, CONFIG.trench_length-CONFIG.module_length);
CONFIG.laser_light_start = CONFIG.trench_length+CONFIG.laser_length/2;
//=========================================
// SETUP
//=========================================
//create scene, renderer, and camera
function setupScene() {
THREE.ImageUtils.crossOrigin = '';
var ratio = window.innerWidth / window.innerHeight;
scene = new THREE.Scene();
scene.fog = new THREE.FogExp2( COLORS.DARK_GREY, CONFIG.fog_density );
renderer = new THREE.WebGLRenderer({alpha: true,antialias: true, premultipliedAlpha: false });
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.shadowMapEnabled = true;
renderer.shadowMapType = THREE.PCFSoftShadowMap;
document.body.appendChild(renderer.domElement);
camera = new THREE.PerspectiveCamera( CONFIG.fov, ratio, 1, CONFIG.view_distance);
camera.position.set(CONFIG.camera_start.x, CONFIG.camera_start.y, CONFIG.camera_start.z);
camera.lookAt(new THREE.Vector3(0, camera.position.y, 0));
controls = new THREE.OrbitControls( camera, renderer.domElement );
controls.enableDamping = true;
controls.dampingFactor = 0.25;
controls.enabled = false;
stats = new Stats();
stats.domElement.style.position = 'absolute';
stats.domElement.style.top = '0px';
window.addEventListener('resize', resize, false);
if(CONFIG.debug){
controls.enabled = true;
document.body.appendChild( stats.domElement );
}
}
//window resize event
function resize() {
renderer.setSize(window.innerWidth, window.innerHeight);
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
}
//create lights
function setupLights() {
//global illumination light
light = new THREE.HemisphereLight(COLORS.WHITE_BLUE, COLORS.WHITE_BLUE, 0.4);
//primary shadow light
shadowLight = new THREE.DirectionalLight(COLORS.WHITE, 0.5);
shadowLight.position.set(-CONFIG.trench_width-50, CONFIG.module_height+30, CONFIG.trench_length/2);
shadowLight.target.position.set(0, CONFIG.module_height/2-20, CONFIG.trench_length/2);
shadowLight.target.updateMatrixWorld();
shadowLight.shadowCameraFar = 150;
shadowLight.shadowCameraLeft = -(CONFIG.trench_length/2+CONFIG.module_length);
shadowLight.shadowCameraRight = (CONFIG.trench_length/2+CONFIG.module_length);
shadowLight.shadowCameraBottom = -20;
shadowLight.shadowCameraTop = 50;
shadowLight.shadowDarkness = 0.85;
shadowLight.shadowBias = 0.005;
shadowLight.castShadow = true;
//secondary shadow light
backLight = new THREE.DirectionalLight(COLORS.WHITE, 0.4);
backLight.position.set(50, CONFIG.module_height, CONFIG.trench_length/2);
backLight.target.position.set(0, 0, CONFIG.trench_length/2);
backLight.target.updateMatrixWorld();
//laser lights need to be setup before the lasers are created at runtime
//to render properly, their positions are set to out of frame
laser_lights = [];
for(var i=0;i<CONFIG.laser_max;i++){
var l = new THREE.PointLight(CONFIG.laser_color, 1, 40);
l.position.set(0, 10, CONFIG.laser_light_start);
laser_lights.push(l);
scene.add(l);
}
scene.add(light, backLight, shadowLight);
}
//preload sounds so multiple versions can be played
function setupSounds(){
for(var i=0;i<CONFIG.laser_max;i++){
var sound = new Audio('https://dl.dropboxusercontent.com/u/3656711/blaster.mp3');
sound.volume = 0.2;
SOUNDS.lasers.push(sound);
}
SOUNDS.computer.volume = 0.5;
SOUNDS.computer.loop = true;
SOUNDS.tiefighter.volume = 0.3;
SOUNDS.xwing.volume = 0.4;
SOUNDS.explosion.volume = 0.3;
}
//=========================================
// OBJECTS
//=========================================
//main trench
var Trench = function(){
this.child_index = CONFIG.module_count-1;
this.group = new THREE.Object3D();
this.dx = 0;
for(i=0;i<CONFIG.module_count;i++){
var section = new THREE.Object3D();
var mod = new Module(i*CONFIG.module_length);
section.add(mod.group);
section.add(mod.lights);
section.add(mod.edges);
this.group.add(section);
}
scene.add(this.group);
this.update = function(){
this.group.position.z += CONFIG.speed;
this.dx += CONFIG.speed;
//one modules' distance travled
//shift the furthest module to the beginning
//note: module length needs to be divisible by speed or the trench will slowly shift back
if(this.dx >= CONFIG.module_length){
var mod = this.group.children[this.child_index];
mod.position.z -= CONFIG.trench_length;
//mod.position.z = 0;
mod.updateMatrix();
this.child_index = (this.child_index <= 0) ? CONFIG.module_count-1 : this.child_index-1;
this.dx = 0;
}
};
};
//trench section
var Module = function(z) {
this.parts = [];
//shorthand
var w = CONFIG.module_width;
var h = CONFIG.module_height;
var l = CONFIG.module_length;
var t = CONFIG.trench_width;
var w_half = w/2;
var h_half = h/2;
var l_half = l/2;
var t_half = t/2;
//basic trench walls
var left = getBoxMesh(w, h, l, -t_half - w_half, h_half, z );
var right = getBoxMesh(w, h, l, t_half + w_half, h_half, z );
var bottom = getBoxMesh(t, w, l, 0, -w_half, z );
//detail left wall
var detail_left = getDetail(l, h-w);
detail_left.position.set(t_half, h_half, z);
detail_left.rotation.y = Math.PI/2;
//detail right wall
var detail_right = getDetail(l, h-w);
detail_right.position.set(-t_half, h_half, z);
detail_right.rotation.y = -Math.PI/2;
//detail bottom
var detail_bottom = getDetail(l, t);
detail_bottom.position.set(0, 0, z);
detail_bottom.rotation.x = Math.PI/2;
//detail lights
this.lights = getDetailLights(l, t);
this.lights.position.set(0, 0, z);
//merged mesh
this.parts.push(left, right, bottom, detail_left, detail_right, detail_bottom);
this.group = mergeMeshes(this.parts);
this.edges = new THREE.EdgesHelper( this.group.clone(), COLORS.YELLOW );
this.edges.visible = false;
this.group.name = "solid";
this.edges.name = "edges";
this.lights.name = "lights";
};
//the planet yavin
var Yavin = function(){
var yavin_geometry = new THREE.SphereGeometry(2000, 32, 32);
var yavin_texture = THREE.ImageUtils.loadTexture(TEXTURES.yavin);
yavin_texture.repeat.set( 2, 2 );
var yavin_material = new THREE.MeshPhongMaterial({ map: yavin_texture, fog: false });
this.group = new THREE.Mesh(yavin_geometry, yavin_material);
this.group.position.set(-1200, 2400, 800);
this.group.rotation.z = Math.PI/4;
this.group.rotation.x = Math.PI/3;
scene.add(this.group);
};
//yavin 4
var Moon = function(){
var moon_geometry = new THREE.SphereGeometry(300, 32, 32);
var moon_texture = THREE.ImageUtils.loadTexture(TEXTURES.moon);
var moon_material = new THREE.MeshPhongMaterial({map: moon_texture,fog: false });
this.group = new THREE.Mesh(moon_geometry, moon_material);
this.group.position.set(200, 1800, -700);
this.group.rotation.x = -Math.PI/2;
this.group.rotation.z = -Math.PI/2;
scene.add(this.group);
this.update = function(){
if(!this.explosion){
this.group.position.x += CONFIG.moon_speed;
this.group.position.y -= CONFIG.moon_speed/2;
}
else{
this.explosion.update();
}
};
this.explode = function(){
scene.remove(this.group);
this.explosion = new Explosion(this.group.position.clone(), {
particles: 500,
radius: 1000,
size: 10,
speed: 15,
mute: true
}, function(){
win();
});
};
};
//background stars
var Starfield = function(){
var stars_geometry = new THREE.SphereGeometry(CONFIG.view_distance/2, 32, 32);
var stars_texture = THREE.ImageUtils.loadTexture(TEXTURES.stars);
var stars_material = new THREE.MeshBasicMaterial({ map: stars_texture, side: THREE.BackSide, fog:false });
this.group = new THREE.Mesh(stars_geometry, stars_material);
scene.add(this.group);
};
//single laser burst
var Laser = function(){
this.group = new THREE.Object3D();
//z is bigger than the x, y threshold
//to account for the speed of the laser
var hit_threshold = 2.5;
var hit_threshold_z = 15;
this.start = new THREE.Vector3(camera.position.x, camera.position.y - 5, 0);
var material = new THREE.LineBasicMaterial({ color: CONFIG.laser_color, linewidth: 2, fog:false });
var geometry = new THREE.Geometry();
geometry.vertices.push(new THREE.Vector3(0, this.start.y, 0));
geometry.vertices.push(new THREE.Vector3(0, this.start.y, CONFIG.laser_length));
//A laser contains two beams
var line_l = new THREE.Line(geometry, material);
var line_r = new THREE.Line(geometry, material);
line_l.position.x = this.start.x - CONFIG.laser_beam_offset;
line_r.position.x = this.start.x + CONFIG.laser_beam_offset;
this.group.add(line_l, line_r);
this.group.position.z = CONFIG.trench_length;
this.update = function(){
this.group.position.z -= CONFIG.laser_speed;
};
//collision detection with xwing
this.hit = function(){
//no xwing to hit
if(!xwing) return false;
//xwing currently exploding
if(xwing.explosion && xwing.explosion.exploding) return false;
//chcek collsion
var dx = Math.abs(this.start.x - xwing.group.position.x);
var dy = Math.abs(this.start.y - xwing.group.position.y);
var dz = Math.abs(this.group.position.z - xwing.group.position.z);
//console.log("diff: "+dx.toFixed(1)+"-"+dy.toFixed(1)+"-"+dz );
if(dx <= hit_threshold && dy <= hit_threshold && dz <= hit_threshold_z ){
registerHit();
return true;
}
};
};
//create lasers dynamically
var LaserSpawner = function(){
this.items = [];
this.fire = function(){
if(this.items.length<CONFIG.laser_max && !KEYS.space.pressed && !CONFIG.paused){
var laser = new Laser();
//get first available laser light.
var light = _.find(laser_lights, function(l){
return l.position.z >= CONFIG.laser_light_start;
});
scene.add(laser.group);
this.items.push({
laser: laser,
light: light
});
if(CONFIG.audio) {
var sound = _.find(SOUNDS.lasers, function(s){
return s.paused;
});
sound.play();
}
}
};
this.update = function(){
var self = this;
self.items.forEach(function(item){
if(item.laser.hit()){
item.light.position.z = CONFIG.laser_light_start;
scene.remove(item.laser.group);
_.pull(self.items, item);
xwing.explode();
}
else if(item.laser.group.position.z < 0){
item.light.position.z = CONFIG.laser_light_start;
scene.remove(item.laser.group);
_.pull(self.items, item);
}
else{
item.laser.update();
if(CONFIG.mode != MODES.COMPUTER){
item.light.position.z -= CONFIG.laser_speed;
}
}
});
};
};
//Tie fighter
var TieFighter = function(start, end, squad){
this.parts = [];
//movement vector
this.dir = start.clone().sub(end).normalize().multiplyScalar(CONFIG.fighter_speed);
var geometry = new THREE.SphereGeometry( 2, 32, 32 );
var material = new THREE.MeshLambertMaterial( {color: 0x999999, fog:false } );
var core = new THREE.Mesh( geometry, material );
//struts
var s1 = getBoxMesh(5, 1, 1, -2, 0, 0);
var s2 = getBoxMesh(5, 1, 1, 2, 0, 0);
//wings
var w1 = getBoxMesh(0.5, 12, 7, 5, 0, 0);
var w2 = getBoxMesh(0.5, 12, 7, -5, 0, 0);
//merged
this.parts.push(core, s1, s2, w1, w2);
this.mesh = mergeMeshes(this.parts);
this.group = this.mesh;
//add more fighters flying in unison
if(squad){
this.mesh_2 = this.mesh.clone();
this.mesh_3 = this.mesh_2.clone();
if(chance(80)){
this.mesh_2.position.x += _.random(-20, 20);
this.mesh_2.position.y += _.random(20, 50);
this.mesh_2.position.z += _.random(-20, 20);
this.group.add(this.mesh_2);
}
if(chance(50)){
this.mesh_3.position.x += _.random(30, 50);
this.mesh_3.position.y += _.random(-5, 5);
this.mesh_3.position.z += _.random(-50, -60);
this.group.add(this.mesh_3);
}
}
this.group.position.set(start.x, start.y, start.z);
this.group.lookAt(end);
this.update = function(){
this.group.position.x -= this.dir.x;
this.group.position.z -= this.dir.z;
};
};
//random tiefighters flying overhead
var TieFighterSpawner = function(){
this.items = [];
this.locked = false;
var x_threshold = 150;
var max_z = CONFIG.trench_length-100;
var min_z = CONFIG.trench_length-400;
this.spawn = function(){
var dir = Math.round(Math.random()) * 2 - 1;
var y = CONFIG.module_height*2;
var start = new THREE.Vector3(dir*x_threshold, y, _.random(min_z, max_z));
var end = new THREE.Vector3(-dir*x_threshold, y, _.random(min_z, max_z));
var fighter = new TieFighter(start, end, true);
this.items.push(fighter);
scene.add(fighter.group);
if(CONFIG.audio && !CONFIG.paused && CONFIG.mode != MODES.COMPUTER) SOUNDS.tiefighter.play();
this.locked = false;
};
this.update = function(){
var self = this;
if(self.items.length < CONFIG.fighter_max && !self.locked && CONFIG.mode != MODES.COMPUTER){
self.locked = true;
setTimeout(function(){ self.spawn(); }, _.random(5000, 10000));
}
self.items.forEach(function(fighter){
if(Math.abs(fighter.group.position.x) > x_threshold){
scene.remove(fighter.group);
_.pull(self.items, fighter);
}
else{
fighter.update();
}
});
};
};
//xwing fighter
var Xwing = function(){
var z = CONFIG.trench_length-130;
var y = 15;
var x = 0;
this.parts = [];
this.inside_trench = true;
this.strafe_active = false;
this.strafe_dir = new THREE.Vector3(x, y, z);
this.strafe_end = new THREE.Vector3(x, y, z);
this.strafe_start = new THREE.Vector3(x, y, z);
//core
var core = getBoxMesh(2, 2, 10, 0, 0, -5);
//wings
var wing_1 = getBoxMesh(12, 0.3, 1.3, 0, 0, -0.7);
var wing_2 = getBoxMesh(12, 0.3, 1.3, 0, 0, -0.7);
wing_1.rotation.z = Math.PI/7;
wing_2.rotation.z = -Math.PI/7;
//guns
var g1 = getCylinderMesh(0.3, 0.3, 4, -5.6, 2.5, -2);
var g2 = getCylinderMesh(0.3, 0.3, 4, -5.6, -2.5, -2);
var g3 = getCylinderMesh(0.3, 0.3, 4, 5.6, 2.5, -2);
var g4 = getCylinderMesh(0.3, 0.3, 4, 5.6, -2.5, -2);
//engines
var e1 = getCylinderMesh(0.7, 0.7, 2, -1.5, -1, -1);
var e2 = getCylinderMesh(0.7, 0.7, 2, -1.5, 1, -1);
var e3 = getCylinderMesh(0.7, 0.7, 2, 1.5, -1, -1);
var e4 = getCylinderMesh(0.7, 0.7, 2, 1.5, 1, -1);
//lights
var l1 = new THREE.PointLight(COLORS.RED, 35, 0.7);
var l2 = new THREE.PointLight(COLORS.RED, 35, 0.7);
var l3 = new THREE.PointLight(COLORS.RED, 35, 0.7);
var l4 = new THREE.PointLight(COLORS.RED, 35, 0.7);
l1.position.set(1.5, 1, 0.3);
l2.position.set(1.5, -1, 0.3);
l3.position.set(-1.5, 1, 0.3);
l4.position.set(-1.5, -1, 0.3);
//shadow of xwing in trench
var shadow = new THREE.SpotLight(COLORS.WHITE, 10);
shadow.onlyShadow = true;
shadow.castShadow = true;
shadow.position.set(x, y+35, z);
shadow.target.position.set(x,y,z);
shadow.target.updateMatrixWorld();
//shadow.shadowCameraVisible = true;
shadow.shadowDarkness = 0.7;
shadow.shadowCameraNear = 1;
shadow.shadowCameraFar = 80;
scene.add(shadow);
//lights
this.lights = new THREE.Object3D();
this.lights.add( l1, l2, l3, l4);
this.lights.name = "lights";
//merged mesh
this.parts.push(core, wing_1, wing_2);
this.parts.push(g1, g2, g3, g4);
this.parts.push(e1, e2, e3, e4);
this.mesh = mergeMeshes(this.parts);
this.mesh.receiveShadow = false;
this.mesh.material = new THREE.MeshLambertMaterial({ color: 0xcccccc });
this.mesh.name = "mesh";
//edges
this.edges = new THREE.EdgesHelper( this.mesh.clone(), COLORS.BRIGHT_RED );
this.edges.visible = false;
this.edges.name = "edges";
//group
this.group = new THREE.Object3D();
this.group.add(this.mesh, this.lights, this.edges);
this.group.position.set(x, y, z);
scene.add(this.group);
this.update = function(){
if(this.explosion){
this.explosion.update();
}
if(!this.inside_trench){
this.enterTrench();
}
else{
this.strafe();
}
};
//move arround randomly on the x y axis
this.strafe = function(){
var max_x = 14;
var max_y = 20;
//no destination, get a new point
if(!this.strafe_active){
this.strafe_start = new THREE.Vector3(this.group.position.x, this.group.position.y, this.group.position.z);
this.strafe_end.x = x - max_x + (Math.random()*(max_x*2));
this.strafe_end.y = (y-5) + (Math.random()*(max_y*2));
this.strafe_active = true;
}
//move to the point
else{
var strafe_speed = this.getSpeed(this.group.position, this.strafe_end);
this.strafe_dir = this.strafe_start.clone().sub(this.strafe_end).normalize().multiplyScalar(strafe_speed);
this.group.position.x -= this.strafe_dir.x;
this.group.position.y -= this.strafe_dir.y;
this.group.position.z = z;
var xdiff = Math.abs(this.group.position.x - this.strafe_end.x) < 2;
var ydiff = Math.abs(this.group.position.y - this.strafe_end.y) < 2;
var xbound = Math.abs(this.group.position.x) > x+max_x;
var ybound = Math.abs(this.group.position.y) > y+max_y;
//arrived or out of bounds
if((xdiff && ydiff) || xbound || ybound){
this.strafe_active = false;
}
}
};
//get speed for sudo smooth movement
this.getSpeed = function(prev_vector, next_vector){
var x = Math.abs(prev_vector.x - next_vector.x);
var y = Math.abs(prev_vector.y - next_vector.y);
var greatest =( x > y ? x : y).toFixed(2);
//increase this for a smoother strafe movement
var speedModifier = 16;
var speed = (greatest/speedModifier).toFixed(2);
return speed;
};
//move from outside to inside trench
this.enterTrench = function(){
if(this.group.position.y < y){
this.inside_trench = true;
return;
}
this.group.position.y-=0.4;
this.group.position.z-=0.43;
};
//when explosion occurs reset to starting position outside trench
this.reset = function(){
this.group.position.y += (CONFIG.module_height+CONFIG.module_height/4);
this.group.position.z += 70;
this.group.position.x = 0;
if(CONFIG.audio && !CONFIG.paused) SOUNDS.xwing.play();
this.inside_trench = false;
};
//create exposion at location of xwing
this.explode = function(){
if(this.explosion) scene.remove(this.explosion.particles);
this.explosion = new Explosion(new THREE.Vector3(this.group.position.x, this.group.position.y, this.group.position.z));
this.reset();
};
};
//create an explosion: http://codepen.io/Xanmia/pen/DoljI
var Explosion = function(position, opts, cb){
cb = cb || function(){};
opts = opts || {};
var radius = opts.radius || CONFIG.explosion_radius;
var particles = opts.particles || CONFIG.explosion_particles;
var speed = opts.speed || CONFIG.explosion_speed;
var size = opts.size || 0.4;
var mute = opts.mute || false;
this.dirs = [];
var material = new THREE.PointCloudMaterial({ size: size, color: CONFIG.explosion_color, fog: false});
var geometry = new THREE.Geometry();
for (var i=0;i<particles;i++){
var vertex = new THREE.Vector3();
vertex.x = position.x;
vertex.y = position.y;
vertex.z = position.z;
geometry.vertices.push(vertex);
this.dirs.push({
x:(Math.random() * speed)-(speed/2),
y:(Math.random() * speed)-(speed/2),
z:(Math.random() * speed)-(speed/2)
});
}
this.exploding = true;
this.particles = new THREE.PointCloud(geometry, material);
scene.add(this.particles);
if(CONFIG.audio && !mute) SOUNDS.explosion.play();
this.update = function(){
if (this.exploding){
var pCount = particles;
while(pCount--) {
var particle = this.particles.geometry.vertices[pCount];
var dx = Math.abs(particle.x) > Math.abs(position.x) + radius;
var dy = Math.abs(particle.y) > Math.abs(position.y) + radius;
var dz = Math.abs(particle.z) > Math.abs(position.z) + radius;
if(dx || dy || dz){
this.exploding = false;
cb();
return;
}
particle.x += this.dirs[pCount].x;
particle.y += this.dirs[pCount].y;
particle.z += this.dirs[pCount].z;
}
this.particles.geometry.verticesNeedUpdate = true;
}
else{
scene.remove(this.particles);
}
};
};
//fire the superweapon
var Beam = function(){
//create the full beam out of frame
//and them translate it rather than scaling
this.firing = true;
this.end = moon.group.position.clone();
this.start = new THREE.Vector3(400,0,CONFIG.trench_length/2);
//total lenght of the beam
this.dist = (this.start.clone().sub(this.end).length())*2;
//current distance to the moon
this.dist_to_moon = 100000000;
//direction vector
this.dir = this.start.clone().sub(this.end).normalize();
//reset the beam behing the start point at creation
this.initial_scale = this.dir.clone().multiplyScalar(this.dist/2);
//how much to translate by at each frame
this.update_scale = this.dir.clone().multiplyScalar(CONFIG.beam_speed);
var material = new THREE.MeshBasicMaterial({ color: COLORS.GREEN, fog:false });
var geom = new THREE.CylinderGeometry(7, 7, this.dist);
this.mesh = new THREE.Mesh(geom, material);
this.mesh.rotation.x = Math.PI/2;
this.group = new THREE.Object3D();
this.group.add(this.mesh);
this.group.position.x = this.start.x+this.initial_scale.x;
this.group.position.y = this.start.y+this.initial_scale.y;
this.group.position.z = this.start.z+this.initial_scale.z;
this.group.lookAt(this.end);
scene.add(this.group);
if(CONFIG.audio) SOUNDS.laser.play();
this.update = function(){
if(!this.firing) return;
if(this.hit()){
this.firing = false;
scene.remove(this.group);
moon.explode();
}
else{
this.group.position.x -= this.update_scale.x;
this.group.position.y -= this.update_scale.y;
this.group.position.z -= this.update_scale.z;
}
};
this.hit = function(){
var old_dist = this.dist_to_moon;
this.dist_to_moon = this.group.position.clone().sub(this.end).length() + (this.dist/2);
return (this.dist_to_moon > old_dist);
};
};
//=========================================
// HELPER FUNCTIONS
//=========================================
//returns true percent of the time
function chance(percent){
return (Math.random() < percent/100.0);
}
//create a box mesh with a geometry and material
function getBoxMesh(w, h, l, x, y, z) {
var material = new THREE.MeshLambertMaterial({ color: COLORS.WHITE, fog:false });
var geom = new THREE.BoxGeometry(w, h, l);
var mesh = new THREE.Mesh(geom, material);
mesh.position.set(x || 0, y || 0, z || 0);
mesh.receiveShadow = true;
mesh.castShadow = true;
return mesh;
}
//create a Cylinder mesh with a geometry and material
function getCylinderMesh(t, b, l, x, y, z) {
var material = new THREE.MeshLambertMaterial({ color: COLORS.WHITE, fog:false });
var geom = new THREE.CylinderGeometry(t, b, l);
var mesh = new THREE.Mesh(geom, material);
mesh.rotation.x = Math.PI/2;
mesh.position.set(x || 0, y || 0, z || 0);
mesh.receiveShadow = true;
mesh.castShadow = true;
return mesh;
}
//merge geometries of meshes
function mergeMeshes (meshes) {
var material = meshes[0].material;
var combined = new THREE.Geometry();
for (var i = 0; i < meshes.length; i++) {
meshes[i].updateMatrix();
combined.merge(meshes[i].geometry, meshes[i].matrix);
}
var mesh = new THREE.Mesh(combined, material);
mesh.castShadow = true;
mesh.receiveShadow = true;
return mesh;
}
//add detail (smaller boxes) to the walls
function getDetail(lw, lh){
var i, w, h, x, y;
var details = [];
//add some big details
for(i=0;i<CONFIG.detail_max;i++){
var depth = _.random(CONFIG.detail_offset/4, CONFIG.detail_offset, true);
w = _.random(lw/5, lw/3*2, true);
h = _.random(lh/5, lh/3*2, true);
x = _.random(-lw+w/2, lw-w/2, true);
y = _.random(-(lh)+(h/2), (lh/2)-(h/6), true);
details.push(getBoxMesh(w, h, depth, x, y, -depth/2));
}
//add some smaller details
for(i=0;i<CONFIG.detail_max;i++){
var depth = _.random(CONFIG.detail_offset, CONFIG.detail_offset+1, true);
w = _.random(lw/15, lw/12, true);
h = _.random(lh/10, lh/5, true);
x = _.random(-lw+w/2, lw-w/2, true);
y = _.random(-(lh)+(h/2), (lh/2)-(h/4), true);
details.push(getBoxMesh(w, h, depth, x, y, -depth/2));
}
return mergeMeshes(details);
}
//add detail light particles on the modules
function getDetailLights(lw, lh){
var x, y, z;
var geom = new THREE.Geometry();
var max = 20;
//left
for(var i=0; i<max;i++){
var x = (-lh/2)+CONFIG.detail_offset;
var y = _.random(0, CONFIG.module_height-5, true);
var z = _.random(-lh/2, lh/2, true);
geom.vertices.push(new THREE.Vector3( x, y, z));
}
//right
for(var i=0; i<max;i++){
x = (lh/2)-CONFIG.detail_offset;
y = _.random(0, CONFIG.module_height-5, true);
z = _.random(-lh/2, lh/2, true);
geom.vertices.push(new THREE.Vector3( x, y, z));
}
//bottom
for(var i=0; i<max;i++){
x = _.random(-lw/2, lw/2, true);
y = CONFIG.detail_offset-1;
z = _.random(-lh/2, lh/2, true);
geom.vertices.push(new THREE.Vector3( x, y, z));
}
var material = new THREE.PointCloudMaterial({ color: 0xCCCCCC, size:0.1 });
var dots = new THREE.PointCloud( geom, material );
return dots;
}
//update the distance value for computer mode
function updateCounter(){
if(CONFIG.speed){
CONFIG.counter = CONFIG.counter < 0 ? 0 : CONFIG.counter-1;
//time out fire the weapon
if(CONFIG.counter < 1 && !beam) { beam = new Beam(); }
var num = ("000000"+CONFIG.counter);
//force monospace by updating each character
//in a seperate element with fixed width
num = num.substr(num.length-6).split("");
for(var i=0;i<num.length;i++){
document.querySelector('.number-'+i).innerHTML = num[i];
}
}
}
//toggle modes
function toggleMode(){
if(CONFIG.paused) togglePause();
document.querySelector('body').classList.toggle('mode-computer');
document.querySelector('.computer').classList.toggle('hidden');
document.querySelector('.btn-computer').classList.toggle('active');
mode = (CONFIG.mode == MODES.SOLID) ? MODES.COMPUTER : MODES.SOLID;
CONFIG.mode = mode;
stars.group.visible = !stars.group.visible;
for(var i=0;i<trench.group.children.length;i++){
var solid = trench.group.children[i].getObjectByName("solid");
var edges = trench.group.children[i].getObjectByName("edges");
var lights = trench.group.children[i].getObjectByName("lights");
edges.visible = !edges.visible;
lights.visible = !lights.visible;
}
if(mode == MODES.COMPUTER){
if(CONFIG.audio && !CONFIG.paused) SOUNDS.computer.play();
scene.fog.density = 0;
scene.remove(light, shadowLight, backLight);
CONFIG.laser_color = COLORS.BRIGHT_RED;
CONFIG.explosion_color = COLORS.BRIGHT_RED;
}
else {
SOUNDS.computer.pause();
scene.fog.density = CONFIG.fog_density;
scene.add(light, shadowLight, backLight);
CONFIG.laser_color = COLORS.GREEN;
CONFIG.explosion_color = COLORS.ORANGE;
}
var xm = xwing.group.getObjectByName("mesh");
var xl = xwing.group.getObjectByName("lights");
var xe = xwing.group.getObjectByName("edges");
xm.visible = !xm.visible;
xl.visible = !xl.visible;
xe.visible = !xe.visible;
}
//mute or unmute audio
function toggleAudio(){
document.querySelector('.btn-audio').classList.toggle('active');
if(CONFIG.mode == MODES.COMPUTER && !CONFIG.paused) toggleSound(SOUNDS.computer);
CONFIG.audio = !CONFIG.audio;
}
//pause the speed
function togglePause(){
document.querySelector('html').classList.toggle('paused');
document.querySelector('.btn-pause').classList.toggle('active');
if(CONFIG.mode == MODES.COMPUTER && CONFIG.audio) toggleSound(SOUNDS.computer);
CONFIG.paused = !CONFIG.paused;
}
//toggle a sound file
function toggleSound(sound){
if(sound.paused) sound.play();
else sound.pause();
}
//move camera arround in the trench on the xy axies
function strafe(){
var up = KEYS.up.pressed || KEYS.W.pressed;
var down = KEYS.down.pressed || KEYS.S.pressed;
var left = KEYS.left.pressed || KEYS.A.pressed;
var right = KEYS.right.pressed || KEYS.D.pressed;
if(up && camera.position.y < CONFIG.camera_start.y + CONFIG.strafe_max+7){
camera.position.y += CONFIG.strafe_speed;
}
if(down && camera.position.y > CONFIG.camera_start.y-2){
camera.position.y -= CONFIG.strafe_speed;
}
if(left && camera.position.x > CONFIG.camera_start.x - CONFIG.strafe_max){
camera.position.x -= CONFIG.strafe_speed;
}
if(right && camera.position.x < CONFIG.camera_start.x + CONFIG.strafe_max){
camera.position.x += CONFIG.strafe_speed;
}
}
//turn on debug mode with orbit camera and stats
function enableDebug(){
CONFIG.debug = KEYS.P.pressed ? !CONFIG.debug : CONFIG.debug;
document.body.appendChild( stats.domElement );
controls.enabled = true;
}
//register a laser hit on an xwing
function registerHit(){
CONFIG.xwing_hits++;
//var remaining = CONFIG.xwing_remaining - CONFIG.xwing_hits;
//if(remaining < 1) win();
if(CONFIG.xwing_hits > 9){
document.querySelector('.xwing-count.number-first').innerHTML = CONFIG.xwing_hits.toString().charAt(0);
document.querySelector('.xwing-count.number-last').innerHTML = CONFIG.xwing_hits.toString().charAt(1);
}
else{
document.querySelector('.xwing-count.number-last').innerHTML = CONFIG.xwing_hits;
}
}
//win state all fighters destroyed
function win(){
reset();
}
//reset to starting conditions
function reset(){
//reset it
}
//=========================================
// EVENTS
//=========================================
//pause button click
document.querySelector('.btn-pause').addEventListener('click', function() {
togglePause();
});
//computer button click
document.querySelector('.btn-computer').addEventListener('click', function() {
toggleMode();
});
//laser button click
document.querySelector('.btn-laser').addEventListener('click', function() {
lasers.fire();
});
//audio button click
document.querySelector('.btn-audio').addEventListener('click', function() {
toggleAudio();
});
//keydown events
document.onkeydown = function(e){
//unpause once any key is pressed
if(CONFIG.paused) togglePause();
//fire laser
if (e.keyCode == KEYS.space.code) {
e.preventDefault();
lasers.fire();
}
//enable debug
if (e.keyCode == KEYS.P.code) {
enableDebug();
}
//toggle computer mode
if (e.keyCode == KEYS.tab.code) {
e.preventDefault();
toggleMode();
}
//if the pressed key is in the KEYS obj set its pressed val to true
var matches = _.pick(KEYS, function(val){ return val.code == e.keyCode; });
if (Object.keys(matches).length){
KEYS[Object.keys(matches)[0]].pressed = true;
}
};
//keyup events
document.onkeyup = function(e){
//if the pressed key is in the KEYS obj set its pressed val to false
var matches = _.pick(KEYS, function(val){ return val.code == e.keyCode; });
if (Object.keys(matches).length){
KEYS[Object.keys(matches)[0]].pressed = false;
}
};
//=========================================
// MAIN
//=========================================
//initial setup
var init = function(){
setupScene();
setupLights();
setupSounds();
trench = new Trench();
stars = new Starfield();
yavin = new Yavin();
moon = new Moon();
lasers = new LaserSpawner();
fighters = new TieFighterSpawner();
xwing = new Xwing();
};
//main animation loop
var render = function() {
requestAnimationFrame(render);
renderer.render(scene, camera);
stats.update();
controls.update();
if(!CONFIG.paused){
strafe();
updateCounter();
if(moon) moon.update();
if(trench) trench.update();
if(trench) lasers.update();
if(fighters) fighters.update();
if(xwing) xwing.update();
if(beam) beam.update();
}
};
//run it
init();
render();
<script src="//cdnjs.cloudflare.com/ajax/libs/lodash.js/3.5.0/lodash.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/three.js/r70/three.min.js"></script>
<script src="http://threejs.org/examples/js/controls/OrbitControls.js"></script>
<script src="http://threejs.org/examples/js/libs/stats.min.js"></script>
$red:#F21313;
$green:#00FF6E;
$yellow:#E0B300;
$button:rgba(white, 0.8);
$button-bg:rgba(white, 0.2);
$button-active:rgba($red, 1);
$button-active-bg:rgba($red, 0.5);
$text:#555;
$pad: 10px;
.hidden{ display:none; }
.left{float:left;}
.right{float:right;}
.center{text-align: center;}
.sudo{
content:"";
position:absolute;
display:block;
}
*, *:before, *:after{
outline:none;
box-sizing: border-box;
}
//GRID
//===============================
.grid:after{
content: "";
display: table;
clear: both;
}
[class*='col-'] {
float: left;
padding-right: $pad;
.grid &:last-of-type {
padding-right: 0;
}
}
.col-2-3 { width: 66.66%; }
.col-1-3 { width: 33.33%; }
.col-1-2 { width: 50%; }
.col-1-4 { width: 25%; }
.col-1-8 { width: 12.5%; }
.grid-pad {
padding: $pad 0 $pad $pad;
[class*='col-']:last-of-type {
padding-right: $pad;
}
}
body{
font-family:helvetica, arial, sans-serif;
background: black;
overflow:hidden;
font-size: 14px;
color:$text;
padding:0px;
margin:0px;
}
//NAV
//===============================
nav{
top:0;
left:0;
width:100%;
padding:15px;
position:absolute;
transition:all 0.3s;
background: transparent;
border-bottom-width:3px;
border-bottom-style:solid;
border-bottom-color:transparent;
}
nav ul{ list-style:none; padding:0; margin:0; }
nav ul li {display:inline-block; vertical-align: top; }
.paused nav {background:rgba(black, 0.9); border-bottom-color:#111;}
.controls-right li{ margin-left:15px; }
.controls-left li{ margin-right:15px; }
.banner{ opacity:0; transition:all 0.3s;}
.paused .banner{ opacity:1; }
.banner h1{ margin:0 0 5px 0; font-size: 24px;}
.banner p{ margin:0; }
.banner .control{display: inline-block; padding:0 10px;}
.banner a{
display: inline-block;
text-decoration: none;
background:$text;
color:black;
padding:8px 15px;
border-radius:4px;
margin:12px 0;
&:hover{background:darken($text, 10%);}
}
//BUTTONS
//===============================
button{
width:53px;
height:53px;
display:block;
cursor: pointer;
position:relative;
border-width:3px;
border-radius: 2px;
border-style:solid;
border-color:$button;
background:$button-bg;
transition:all 0.2s;
&:before, &:after, i:after, i:before{
transition:all 0.2s;
border-color:$button;
background:$button;
}
}
button.active, button:active{
border-color:$button-active;
background:$button-active-bg;
&:before, &:after, i:after, i:before{
border-color:$button-active;
background:$button-active;
}
}
//BUTTON ICONS
//===============================
.btn-pause:before{
@extend .sudo;
width:10px;
height:25px;
top:11px;
left:26px;
}
.btn-pause:after{
@extend .sudo;
width:10px;
height:25px;
top:11px;
left:11px;
border:none;
}
.btn-pause.active:before{
display:none;
}
.btn-pause.active:after{
@extend .sudo;
width: 0;
height: 0;
top: 9px;
left: 13px;
border-style: solid;
border-width: 15px 0 15px 25px;
background:transparent;
border-color: transparent transparent transparent $button-active;
}
.btn-computer:before{
@extend .sudo;
height:10px;
width:30px;
left:9px;
top:9px;
}
.btn-computer:after{
@extend .sudo;
height:10px;
width:30px;
left:9px;
top:30px;
}
.btn-computer i:before{
@extend .sudo;
height:5px;
width:5px;
left:9px;
top:22px;
}
.btn-computer i:after{
@extend .sudo;
height:5px;
width:5px;
left:34px;
top:22px;
}
.btn-laser:before{
@extend .sudo;
width:30px;
height:15px;
bottom:10px;
left:9px;
border-width:1px;
border-radius: 0 0 30px 30px;
}
.btn-laser:after{
@extend .sudo;
width:0;
height:0;
top: 9px;
left: 17px;
border-style: solid;
border-width: 0 7.5px 10px 7.5px;
background:transparent;
border-color: transparent transparent $button transparent;
}
.btn-laser:active:after{
background:transparent;
border-color: transparent transparent $button-active transparent;
}
.btn-audio:before{
@extend .sudo;
width: 10px;
height: 15px;
bottom: 16px;
left: 7px;
border-radius: 10px 0 0 10px;
}
.btn-audio:after{
@extend .sudo;
width: 0;
height: 35px;
top: 6px;
left: 20px;
border-style: solid;
border-width: 10px 17px 10px 0;
background:transparent;
border-color: transparent $button transparent transparent;
}
.btn-audio.active:after, .btn-audio:active:after{
background:transparent;
border-color: transparent $button-active transparent transparent !important;
}
//TARGETING COMPUTER
//===============================
.computer > *{
position:absolute;
background:black;
padding:15px 25px 6px 25px;
font-size: 24px;
color:red;
bottom:0;
}
.computer .label{
margin-bottom:10px;
}
.computer .hits{
left:0;
border-top: 2px solid $yellow;
border-right: 2px solid $yellow;
border-top-right-radius:10px;
> *{display: inline-block; vertical-align: top;}
.x{ margin:0 10px; font-size:20px; margin-top:15px;}
.xwing-count{ margin-top: 3px; position:relative;}
.xwing-count:before{
@extend .sudo;
background:black;
height:5px;
width:100%;
top:46%;
}
}
.computer .counter{
right:0;
text-align: center;
border-top: 2px solid $yellow;
border-left: 2px solid $yellow;
border-top-left-radius:10px;
}
.computer .counter:after{
@extend .sudo;
background: black;
height:5px;
width:100%;
top:50%;
left:0;
}
.computer .number{
width:35px;
text-align: center;
display:inline-block;
font-weight: bold;
font-family: 'Poiret One', sans-serif;
text-shadow: 0 0 4px rgba(red, 0.6);
font-size:40px;
color:red;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment