├── .gitignore ├── photos ├── stage.jpeg ├── stage2.jpeg ├── github_icon.png ├── linkedin_icon.png └── github_icon.svg ├── lib ├── guitar_hero.js ├── song.js ├── instructions.js ├── light.js ├── key.js ├── view_controls.js ├── songs │ ├── song_solo_1.js │ ├── song_chorus_2.js │ ├── song_chorus_1.js │ ├── song_verse_2.js │ ├── song_verse_1.js │ ├── song_solo_2.js │ ├── song_intro.js │ └── song_bridge.js ├── audio.js ├── game.js ├── game_notes.js └── game_view.js ├── docs ├── todo.md └── proposal.md ├── webpack.config.js ├── package.json ├── vendor ├── PointerLockControls.js └── OrbitControls.js ├── index.html ├── README.md └── css └── master.css /.gitignore: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /photos/stage.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jyschwrtz/JS-Hero/HEAD/photos/stage.jpeg -------------------------------------------------------------------------------- /photos/stage2.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jyschwrtz/JS-Hero/HEAD/photos/stage2.jpeg -------------------------------------------------------------------------------- /photos/github_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jyschwrtz/JS-Hero/HEAD/photos/github_icon.png -------------------------------------------------------------------------------- /photos/linkedin_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jyschwrtz/JS-Hero/HEAD/photos/linkedin_icon.png -------------------------------------------------------------------------------- /lib/guitar_hero.js: -------------------------------------------------------------------------------- 1 | import Game from "./game"; 2 | 3 | document.addEventListener("DOMContentLoaded", () => { 4 | let game = new Game(); 5 | 6 | }); 7 | -------------------------------------------------------------------------------- /docs/todo.md: -------------------------------------------------------------------------------- 1 | ** TODO: 2 | * Add lighting effects 3 | 4 | 5 | ** EXTRA: 6 | * allow for pausing gameplay 7 | * change notes to all rendered & different method on checks (no setTimeout ) 8 | * Add more 3d elements around world 9 | -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | 3 | 4 | module.exports = { 5 | // context: __dirname, 6 | entry: "./lib/guitar_hero.js", 7 | // target: 'node', 8 | output: { 9 | // path: path.resolve(__dirname, 'lib'), 10 | filename: "./lib/bundle.js" 11 | }, 12 | // resolve: { 13 | // extensions: ['.js'] 14 | // }, 15 | devtool: 'source-map', 16 | }; 17 | -------------------------------------------------------------------------------- /lib/song.js: -------------------------------------------------------------------------------- 1 | export const beatsPerMeasure = 8; 2 | 3 | import songIntro from './songs/song_intro'; 4 | import songVerse1 from './songs/song_verse_1'; 5 | import songChorus1 from './songs/song_chorus_1'; 6 | import songSolo1 from './songs/song_solo_1'; 7 | import songVerse2 from './songs/song_verse_2'; 8 | import songChorus2 from './songs/song_chorus_2'; 9 | import songSolo2 from './songs/song_solo_2'; 10 | import songBridge from './songs/song_bridge'; 11 | 12 | 13 | export const songNotes = songIntro.concat( 14 | songVerse1, 15 | songChorus1, 16 | songSolo1, 17 | songVerse2, 18 | songChorus2, 19 | songSolo2, 20 | songBridge 21 | ); 22 | -------------------------------------------------------------------------------- /lib/instructions.js: -------------------------------------------------------------------------------- 1 | class Instructions { 2 | constructor() { 3 | this.instructionsEl = document.getElementsByClassName('instructions')[0]; 4 | this.closeInstructionsEl = 5 | document.getElementsByClassName('close-instructions')[0]; 6 | this.openInstructionsEl = 7 | document.getElementsByClassName('open-instructions')[0]; 8 | 9 | this.closeInstructionsEl.onclick = this.closeInstructions.bind(this); 10 | this.openInstructionsEl.onclick = this.openInstructions.bind(this); 11 | } 12 | 13 | closeInstructions() { 14 | this.instructionsEl.className = "instructions hidden"; 15 | } 16 | 17 | openInstructions() { 18 | this.instructionsEl.className = "instructions"; 19 | } 20 | } 21 | 22 | export default Instructions; 23 | -------------------------------------------------------------------------------- /lib/light.js: -------------------------------------------------------------------------------- 1 | import * as THREE from '../vendor/three'; 2 | 3 | class Light { 4 | constructor(scene) { 5 | this.scene = scene; 6 | } 7 | 8 | addLights() { 9 | let lights = []; 10 | lights[0] = new THREE.PointLight( 0xFFFFFF, .9, 10000); 11 | lights[0].position.set(0, 300, 0); 12 | lights[1] = new THREE.PointLight( 0xFFFFFF, 1, 2000); 13 | lights[1].position.set(0, 200, 100); 14 | lights.forEach(light => this.scene.add(light)); 15 | } 16 | 17 | addMovingLights() { 18 | this.movingLights = []; 19 | // this.movingLights[0] = new THREE.PointLight( 0xFFFFFF, .5, 10000); 20 | // this.movingLights[0].position.set(0, 300, 0); 21 | this.movingLights[0] = new THREE.SpotLight( 0xFFFFFF, 1, 5000, .5 ); 22 | this.movingLights[0].position.set(200, 0, 0); 23 | this.movingLights.forEach(light => this.scene.add(light)); 24 | } 25 | 26 | } 27 | 28 | export default Light; 29 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "guitar_hero", 3 | "private": true, 4 | "dependencies": { 5 | "babel-core": "^6.26.0", 6 | "babel-loader": "^7.1.2", 7 | "babel-preset-es2015": "^6.24.1", 8 | "babel-preset-react": "^6.24.1", 9 | "webpack": "^3.8.1" 10 | }, 11 | "version": "1.0.0", 12 | "description": "", 13 | "engines": { 14 | "node": "6.10.1", 15 | "npm": "3.10.10" 16 | }, 17 | "main": "webpack.config.js", 18 | "directories": { 19 | "test": "test" 20 | }, 21 | "scripts": { 22 | "test": "echo \"Error: no test specified\" && exit 1", 23 | "start": "webpack --watch", 24 | "postinstall": "webpack" 25 | }, 26 | "keywords": [], 27 | "repository": { 28 | "type": "git", 29 | "url": "git+https://github.com/jyschwrtz/guitar-hero.git" 30 | }, 31 | "author": "", 32 | "license": "ISC", 33 | "bugs": { 34 | "url": "https://github.com/jyschwrtz/guitar-hero/issues" 35 | }, 36 | "homepage": "https://github.com/jyschwrtz/guitar-hero#readme" 37 | 38 | } 39 | -------------------------------------------------------------------------------- /lib/key.js: -------------------------------------------------------------------------------- 1 | // KEY LOGIC ADAPTED FROM https://github.com/nklsrh/BuildNewGames_ThreeJSGame/blob/gh-pages/Scripts/keyboard.js 2 | // Will use this Key.isDown boolean to test if it is being pressed at the right time. 3 | 4 | class Key { 5 | constructor() { 6 | this._pressed = {}; 7 | this._pressedVisually = {}; 8 | this.pos = { 9 | 1: 65, 10 | 2: 83, 11 | 3: 68, 12 | 4: 70, 13 | 5: 71 14 | }; 15 | this.A = 65; // songNote.pos: 1 16 | this.S = 83; // songNote.pos: 2 17 | this.D = 68; // songNote.pos: 3 18 | this.F = 70; // songNote.pos: 4 19 | this.G = 71; // songNote.pos: 5 20 | 21 | this.addKeyListeners(); 22 | } 23 | 24 | addKeyListeners() { 25 | window.addEventListener('keydown', (e) => { 26 | this.onKeydown(e); 27 | }); 28 | window.addEventListener('keyup', (e) => { 29 | this.onKeyup(e); 30 | }); 31 | } 32 | 33 | isDown(keyCode) { 34 | return this._pressed[keyCode]; 35 | } 36 | 37 | isDownVisually(keyCode) { 38 | return this._pressedVisually[keyCode]; 39 | } 40 | 41 | onKeydown(e) { 42 | this._pressed[e.keyCode] = true; 43 | this._pressedVisually[e.keyCode] = true; 44 | } 45 | 46 | onKeyup(e) { 47 | delete this._pressedVisually[e.keyCode]; 48 | let buffer = 300; // buffer for leniency 49 | setTimeout( () => { 50 | delete this._pressed[e.keyCode]; 51 | }, buffer); 52 | } 53 | 54 | } 55 | 56 | export default Key; 57 | -------------------------------------------------------------------------------- /lib/view_controls.js: -------------------------------------------------------------------------------- 1 | import * as THREE from '../vendor/three'; 2 | import OrbitControls from '../vendor/OrbitControls.js'; 3 | 4 | class ViewControls { 5 | constructor(camera, renderer) { 6 | this.camera = camera; 7 | this.renderer = renderer; 8 | 9 | this.displayEl = document.getElementsByClassName('game-display')[0]; 10 | this.lookAroundEl = document.getElementsByClassName('look-around')[0]; 11 | this.lookAroundInstructionsEl = 12 | document.getElementsByClassName('look-around-instructions')[0]; 13 | 14 | this.lookAroundEl.onclick = this.toggleLookAround.bind(this); 15 | this.controls = 16 | new THREE.OrbitControls(this.camera, this.renderer.domElement); 17 | this.controls.enabled = false; 18 | } 19 | 20 | addControls() { 21 | this.controls.enabled = true; 22 | this.lookAroundEl.innerHTML = "Fix Camera View"; 23 | this.lookAroundInstructionsEl.className = "look-around-instructions"; 24 | console.log(this.lookAroundInstructionsEl); 25 | setTimeout(() => { 26 | this.lookAroundInstructionsEl.className = "look-around-instructions hidden"; 27 | }, 2000); 28 | } 29 | 30 | removeControls() { 31 | this.controls.enabled = false; 32 | this.lookAroundEl.innerHTML = "Look Around"; 33 | this.lookAroundInstructionsEl.className = "look-around-instructions hidden"; 34 | } 35 | 36 | lookAround() { 37 | this.addControls(); 38 | this.displayEl.className = "game-display hidden"; 39 | } 40 | 41 | fixView() { 42 | this.removeControls(); 43 | this.displayEl.className = "game-display"; 44 | } 45 | 46 | toggleLookAround() { 47 | return this.controls.enabled ? this.fixView() : this.lookAround(); 48 | } 49 | 50 | } 51 | 52 | export default ViewControls; 53 | -------------------------------------------------------------------------------- /vendor/PointerLockControls.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @author mrdoob / http://mrdoob.com/ 3 | */ 4 | import * as THREE from './three'; 5 | 6 | THREE.PointerLockControls = function ( camera ) { 7 | 8 | var scope = this; 9 | 10 | camera.rotation.set( 0, 0, 0 ); 11 | 12 | var pitchObject = new THREE.Object3D(); 13 | pitchObject.add( camera ); 14 | 15 | var yawObject = new THREE.Object3D(); 16 | yawObject.position.y = 10; 17 | yawObject.add( pitchObject ); 18 | 19 | var PI_2 = Math.PI / 2; 20 | 21 | var onMouseMove = function ( event ) { 22 | 23 | if ( scope.enabled === false ) return; 24 | 25 | var movementX = event.movementX || event.mozMovementX || event.webkitMovementX || 0; 26 | var movementY = event.movementY || event.mozMovementY || event.webkitMovementY || 0; 27 | 28 | yawObject.rotation.y -= movementX * 0.002; 29 | pitchObject.rotation.x -= movementY * 0.002; 30 | 31 | pitchObject.rotation.x = Math.max( - PI_2, Math.min( PI_2, pitchObject.rotation.x ) ); 32 | 33 | }; 34 | 35 | this.dispose = function() { 36 | 37 | document.removeEventListener( 'mousemove', onMouseMove, false ); 38 | 39 | }; 40 | 41 | document.addEventListener( 'mousemove', onMouseMove, false ); 42 | 43 | this.enabled = false; 44 | 45 | this.getObject = function () { 46 | 47 | return yawObject; 48 | 49 | }; 50 | 51 | this.getDirection = function() { 52 | 53 | // assumes the camera itself is not rotated 54 | 55 | var direction = new THREE.Vector3( 0, 0, - 1 ); 56 | var rotation = new THREE.Euler( 0, 0, 0, "YXZ" ); 57 | 58 | return function( v ) { 59 | 60 | rotation.set( pitchObject.rotation.x, yawObject.rotation.y, 0 ); 61 | 62 | v.copy( direction ).applyEuler( rotation ); 63 | 64 | return v; 65 | 66 | }; 67 | 68 | }(); 69 | 70 | }; 71 | -------------------------------------------------------------------------------- /lib/songs/song_solo_1.js: -------------------------------------------------------------------------------- 1 | export default [ 2 | // Solo 1 - 1:37 3 | 4 | {m:48, t: 8.5, pos: 4}, 5 | 6 | {m:49, t: 1, pos: 3, hold: 3}, 7 | // {m:49, t: 2, pos: 3}, 8 | // {m:49, t: 3, pos: 2}, 9 | {m:49, t: 4, pos: 2, hold: 4}, 10 | // {m:49, t: 5, pos: 5}, 11 | // {m:49, t: 6, pos: 3}, 12 | // {m:49, t: 7, pos: 4}, 13 | {m:49, t: 8, pos: 4}, 14 | 15 | {m:50, t: 1, pos: 3, hold: 3}, 16 | // {m:50, t: 2, pos: 3}, 17 | // {m:50, t: 3, pos: 2}, 18 | {m:50, t: 4, pos: 2, hold: 4}, 19 | // {m:50, t: 5, pos: 5}, 20 | // {m:50, t: 6, pos: 3}, 21 | // {m:50, t: 7, pos: 4}, 22 | {m:50, t: 8, pos: 4}, 23 | 24 | {m:51, t: 1, pos: 3, hold: 3}, 25 | // {m:51, t: 2, pos: 3}, 26 | // {m:51, t: 3, pos: 2}, 27 | {m:51, t: 4, pos: 1, hold: 4}, 28 | // {m:51, t: 5, pos: 5}, 29 | // {m:51, t: 6, pos: 3}, 30 | // {m:51, t: 7, pos: 4}, 31 | {m:51, t: 8, pos: 4}, 32 | 33 | {m:52, t: 1, pos: 3, hold: 3}, 34 | // {m:52, t: 2, pos: 3}, 35 | // {m:52, t: 3, pos: 2}, 36 | {m:52, t: 4, pos: 1, hold: 4}, 37 | // {m:52, t: 5, pos: 5}, 38 | // {m:52, t: 6, pos: 3}, 39 | // {m:52, t: 7, pos: 4}, 40 | {m:52, t: 8, pos: 4}, 41 | 42 | {m:53, t: 1, pos: 3, hold: 5}, 43 | // {m:53, t: 2, pos: 3}, 44 | // {m:53, t: 3, pos: 2}, 45 | // {m:53, t: 4, pos: 1}, 46 | // {m:53, t: 5, pos: 5}, 47 | {m:53, t: 6, pos: 5}, 48 | {m:53, t: 7, pos: 4}, 49 | {m:53, t: 8, pos: 3}, 50 | 51 | {m:54, t: 1, pos: 4}, 52 | {m:54, t: 2, pos: 5}, 53 | {m:54, t: 3, pos: 3}, 54 | {m:54, t: 4, pos: 3}, 55 | {m:54, t: 4.5, pos: 4, hold: 2.5}, 56 | // {m:54, t: 5, pos: 5}, 57 | // {m:54, t: 6, pos: 5}, 58 | {m:54, t: 7, pos: 3}, 59 | {m:54, t: 8, pos: 1}, 60 | {m:54, t: 8.5, pos: 2, hold: 16.5}, 61 | 62 | // m:55 holding 63 | // m:56 holding 64 | ]; 65 | -------------------------------------------------------------------------------- /lib/songs/song_chorus_2.js: -------------------------------------------------------------------------------- 1 | export default [ 2 | // Chorus 2 - 2:23 3 | 4 | // {m:73, t: 1, pos: 4}, // OFF 5 | {m:73, t: 2, pos: 4}, 6 | {m:73, t: 3, pos: 3}, 7 | {m:73, t: 4, pos: 2}, 8 | {m:73, t: 5, pos: 5}, 9 | {m:73, t: 6, pos: 2}, 10 | {m:73, t: 7, pos: 4}, 11 | {m:73, t: 8, pos: 2}, 12 | 13 | // {m:74, t: 1, pos: 4}, // OFF 14 | {m:74, t: 2, pos: 3}, 15 | {m:74, t: 3, pos: 2}, 16 | {m:74, t: 4, pos: 1}, 17 | {m:74, t: 5, pos: 5}, 18 | {m:74, t: 6, pos: 3}, 19 | {m:74, t: 7, pos: 4}, 20 | {m:74, t: 8, pos: 3}, 21 | 22 | {m:75, t: 1, pos: 1}, 23 | {m:75, t: 2, pos: 4}, 24 | {m:75, t: 3, pos: 2}, 25 | {m:75, t: 4, pos: 1}, 26 | {m:75, t: 5, pos: 5}, 27 | {m:75, t: 6, pos: 2}, 28 | {m:75, t: 7, pos: 4}, 29 | {m:75, t: 8, pos: 2}, 30 | 31 | {m:76, t: 1, pos: 1}, 32 | {m:76, t: 2, pos: 4}, 33 | {m:76, t: 3, pos: 2}, 34 | {m:76, t: 4, pos: 1}, 35 | {m:76, t: 5, pos: 5}, 36 | {m:76, t: 6, pos: 2}, 37 | {m:76, t: 7, pos: 4}, 38 | {m:76, t: 8, pos: 2}, 39 | 40 | // {m:77, t: 1, pos: 4}, // OFF 41 | {m:77, t: 2, pos: 4}, 42 | {m:77, t: 3, pos: 3}, 43 | {m:77, t: 4, pos: 2}, 44 | {m:77, t: 5, pos: 5}, 45 | {m:77, t: 6, pos: 2}, 46 | {m:77, t: 7, pos: 4}, 47 | {m:77, t: 8, pos: 2}, 48 | 49 | // {m:78, t: 1, pos: 4}, // OFF 50 | {m:78, t: 2, pos: 3}, 51 | {m:78, t: 3, pos: 2}, 52 | {m:78, t: 4, pos: 1}, 53 | {m:78, t: 5, pos: 5}, 54 | {m:78, t: 6, pos: 3}, 55 | {m:78, t: 7, pos: 4}, 56 | {m:78, t: 8, pos: 3}, 57 | 58 | {m:79, t: 1, pos: 4}, 59 | {m:79, t: 2, pos: 3, hold: 15}, {m:79, t: 2, pos: 5, hold: 15}, 60 | // {m:79, t: 3, pos: 2}, 61 | // {m:79, t: 4, pos: 1}, 62 | // {m:79, t: 5, pos: 5}, 63 | // {m:79, t: 6, pos: 3}, 64 | // {m:79, t: 7, pos: 4}, 65 | // {m:79, t: 8, pos: 3}, 66 | 67 | // m:80 holding 68 | 69 | ]; 70 | -------------------------------------------------------------------------------- /lib/songs/song_chorus_1.js: -------------------------------------------------------------------------------- 1 | export default [ 2 | // Chorus 1 1:21 3 | // 252 BPM (metronome) 4 | 5 | {m:41, t: 1, pos: 4}, 6 | {m:41, t: 2, pos: 4}, 7 | {m:41, t: 3, pos: 3}, 8 | {m:41, t: 4, pos: 2}, 9 | {m:41, t: 5, pos: 5}, 10 | {m:41, t: 6, pos: 2}, 11 | {m:41, t: 7, pos: 4}, 12 | {m:41, t: 8, pos: 2}, 13 | 14 | {m:42, t: 1, pos: 3}, 15 | {m:42, t: 2, pos: 3}, 16 | {m:42, t: 3, pos: 2}, 17 | {m:42, t: 4, pos: 1}, 18 | {m:42, t: 5, pos: 5}, 19 | {m:42, t: 6, pos: 3}, 20 | {m:42, t: 7, pos: 4}, 21 | {m:42, t: 8, pos: 3}, 22 | 23 | {m:43, t: 1, pos: 1}, 24 | {m:43, t: 2, pos: 4}, 25 | {m:43, t: 3, pos: 2}, 26 | {m:43, t: 4, pos: 1}, 27 | {m:43, t: 5, pos: 5}, 28 | {m:43, t: 6, pos: 2}, 29 | {m:43, t: 7, pos: 4}, 30 | {m:43, t: 8, pos: 2}, 31 | 32 | {m:44, t: 1, pos: 1}, 33 | {m:44, t: 2, pos: 4}, 34 | {m:44, t: 3, pos: 2}, 35 | {m:44, t: 4, pos: 1}, 36 | {m:44, t: 5, pos: 5}, 37 | {m:44, t: 6, pos: 2}, 38 | {m:44, t: 7, pos: 4}, 39 | {m:44, t: 8, pos: 2}, 40 | 41 | {m:45, t: 1, pos: 4}, 42 | {m:45, t: 2, pos: 4}, 43 | {m:45, t: 3, pos: 3}, 44 | {m:45, t: 4, pos: 2}, 45 | {m:45, t: 5, pos: 5}, 46 | {m:45, t: 6, pos: 2}, 47 | {m:45, t: 7, pos: 4}, 48 | {m:45, t: 8, pos: 2}, 49 | 50 | {m:46, t: 1, pos: 3}, 51 | {m:46, t: 2, pos: 3}, 52 | {m:46, t: 3, pos: 2}, 53 | {m:46, t: 4, pos: 1}, 54 | {m:46, t: 5, pos: 5}, 55 | {m:46, t: 6, pos: 3}, 56 | {m:46, t: 7, pos: 4, hold: 2}, 57 | // {m:46, t: 8, pos: 3}, 58 | 59 | {m:47, t: 1, pos: 3, hold: 15}, {m:47, t: 1, pos: 5, hold: 15}, 60 | // {m:47, t: 2, pos: 3}, 61 | // {m:47, t: 3, pos: 2}, 62 | // {m:47, t: 4, pos: 1}, 63 | // {m:47, t: 5, pos: 5}, 64 | // {m:47, t: 6, pos: 3}, 65 | // {m:47, t: 7, pos: 4}, 66 | // {m:47, t: 8, pos: 3}, 67 | 68 | // m:48 hold until last beat 69 | 70 | ]; 71 | -------------------------------------------------------------------------------- /lib/audio.js: -------------------------------------------------------------------------------- 1 | class Audio { 2 | constructor(musicDelay) { 3 | this.musicDelay = musicDelay; 4 | 5 | this.songDivEl = document.getElementById('song'); 6 | this.muteButton = document.getElementsByClassName('mute')[0]; 7 | this.playPauseButton = document.getElementsByClassName('play-pause')[0]; 8 | this.src = 'https://s3-us-west-1.amazonaws.com/js-hero-guitar-hero-clone/sweet_child_o_mine.mp3'; 9 | this.songDivEl.innerHTML = 10 | `