Built with blockbuilder.org
| <!DOCTYPE html> | |
| <!DOCTYPE html> | |
| <html> | |
| <head> | |
| <style> | |
| body { | |
| position: relative; | |
| background-color: lightgray; | |
| } | |
| .top-container { | |
| width: 650px; | |
| } | |
| .scoreboard { | |
| float: left; | |
| background-color: lightgray; | |
| } | |
| .instructions { | |
| float: right; | |
| } | |
| .board { | |
| width: 650px; | |
| background-color: lightgray; | |
| cursor: none; | |
| } | |
| .svg-canvas { | |
| border: 1px solid black; | |
| margin-right: auto; | |
| margin-left: auto; | |
| } | |
| .crosshairs { | |
| fill: yellow; | |
| } | |
| .player { | |
| fill: white; | |
| } | |
| .enemy { | |
| fill: green; | |
| } | |
| .bullet { | |
| fill: white; | |
| } | |
| </style> | |
| </head> | |
| <body> | |
| <div class="top-container"> | |
| <div class="scoreboard"> | |
| <!-- Modify the scoreboard when important events occur in your game! --> | |
| <div class="high">High score: <span id="high">0</span></div> | |
| <div class="current">Time Alive: <span id="current">0</span></div> | |
| <div class="kills">Kills: <span id ="kills">0</span></div> | |
| <div class="health">Health: <span id ="health">100</span></div> | |
| </div> | |
| <div class="instructions"> | |
| <div>WASD controls movement!</div> | |
| <div>Mouse controls aim! Click to shoot!</div> | |
| <div>Space pauses! And starts the game!</div> | |
| <div>Don't touch the green guys!</div> | |
| <div>Reload to play again!</div> | |
| </div> | |
| </div> | |
| <div class="board"></div> | |
| <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.6/d3.min.js" charset="utf-8"></script> | |
| <script src='https://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.8.3/underscore-min.js'></script> | |
| <script src='https://code.jquery.com/jquery-2.1.4.min.js'></script> | |
| <script> | |
| //---SETTINGS--- | |
| var gameOptions = { | |
| width: 650, | |
| height: 600, | |
| friction: 0.9 | |
| }; | |
| var gameStats = { | |
| timeLived: 0, | |
| highScore: 0, | |
| kills: 0, | |
| health: 100, | |
| }; | |
| var updateScore = function () { | |
| d3.select('.scoreboard .current span').text(gameStats.timeLived); | |
| d3.select('.scoreboard .highscore span').text(gameStats.highScore); | |
| d3.select('.scoreboard .kills span').text(gameStats.kills); | |
| }; | |
| //---HELPERS--- | |
| var pixelize = function (n) { | |
| return n + 'px'; | |
| }; | |
| //Two functions to convert location from coordinates to pixels. | |
| //Example: toPixelAxes.x(gameOptions.height) --> 100 | |
| var toPixelAxes = { | |
| x: d3.scale.linear().domain([0, 100]).range([0, gameOptions.width]).clamp(true), | |
| y: d3.scale.linear().domain([0, 100]).range([0, gameOptions.height]).clamp(true) | |
| }; | |
| //Two functions to convert location from pixels to coordinates | |
| var toCoordAxes = { | |
| x: d3.scale.linear().domain([0, gameOptions.width]).range([0, 100]).clamp(true), | |
| y: d3.scale.linear().domain([0, gameOptions.height]).range([0, 100]).clamp(true) | |
| }; | |
| //check if in bounds IN PIXELS | |
| var inBounds = function (x, y) { | |
| var result = false; | |
| if (x < gameOptions.height && x > 0 && y < gameOptions.width && y > 0) { | |
| result = true; | |
| } | |
| return result; | |
| } | |
| var isMaxedOut = function (loc) { | |
| if ((loc[0] > gameOptions.width) || (loc[0] < 0) || (loc[1] > gameOptions.height) || (loc[1] < 0)) { | |
| return true; | |
| } | |
| return false; | |
| } | |
| //return random number between two constraints | |
| var rand = function (nBot, nTop) { | |
| return Math.floor(Math.random() * nTop) + nBot; | |
| }; | |
| //return random coords within game board | |
| var randX = function () { | |
| return rand(0, gameOptions.width); | |
| }; | |
| var randY = function () { | |
| return rand(0, gameOptions.height); | |
| }; | |
| //---GAME ELEMENT CONSTRUCTORS--- | |
| var GameElement = function (loc, size) { | |
| this.loc = [loc[0], loc[1]]; | |
| this.size = size; | |
| this.speed = 1; | |
| }; | |
| //all game elements have a function that creates them on board | |
| GameElement.prototype.draw = function (htmlClass) { | |
| board | |
| .append('svg:circle') | |
| .attr('class', htmlClass) | |
| .attr('cx', pixelize(this.loc[0])) | |
| .attr('cy', pixelize(this.loc[1])) | |
| .attr('stroke', 'black') | |
| .attr('r', this.size) | |
| }; | |
| GameElement.prototype.move = function (x, y) { | |
| this.loc[0] += x * this.speed; | |
| this.loc[1] += y * this.speed; | |
| }; | |
| //for player shots, call with 'player.shoot(crosshairs.loc);' | |
| var bulletObjs = []; | |
| GameElement.prototype.shoot = function (targetLoc) { | |
| var x = this.loc[0]; | |
| var y = this.loc[1]; | |
| var newBullet = new Bullet(this.loc, 2); | |
| newBullet.draw('bullet'); | |
| newBullet.targetLoc = targetLoc; | |
| var run = targetLoc[0] - newBullet.loc[0]; | |
| var rise = targetLoc[1] - newBullet.loc[1]; | |
| var length = Math.sqrt((rise * rise) + (run * run)); | |
| newBullet.unitX = run / length; | |
| newBullet.unitY = rise / length; | |
| bulletObjs.push(newBullet); | |
| } | |
| //bullet constructor | |
| var Bullet = function () { | |
| GameElement.apply(this, arguments); | |
| this.speed = 20; | |
| }; | |
| Bullet.prototype = Object.create(GameElement.prototype); | |
| Bullet.prototype.constructor = Bullet; | |
| Bullet.prototype.fire = function () { | |
| this.move(this.unitX, this.unitY); | |
| } | |
| Bullet.prototype.die = function () { | |
| for (var j = 0; j < bulletObjs.length; j++) { | |
| if (this === bulletObjs[j]) { | |
| d3.selectAll('.bullet') | |
| .filter(function (d, i) { | |
| return i === j; | |
| }) | |
| .remove(); | |
| bulletObjs.splice(j, 1); | |
| return; | |
| } | |
| } | |
| }; | |
| //player constructor | |
| var Player = function () { | |
| GameElement.apply(this, arguments); | |
| this.speed = 2; | |
| }; | |
| Player.prototype = Object.create(GameElement.prototype); | |
| Player.prototype.constructor = Player; | |
| //crosshairs constructor | |
| var Crosshairs = function () { | |
| GameElement.apply(this, arguments); | |
| }; | |
| Crosshairs.prototype = Object.create(GameElement.prototype); | |
| Crosshairs.prototype.constructor = Crosshairs; | |
| //enemy constructor | |
| var Enemy = function () { | |
| GameElement.apply(this, arguments); | |
| }; | |
| Enemy.prototype = Object.create(GameElement.prototype); | |
| Enemy.prototype.constructor = Enemy; | |
| Enemy.prototype.chase = function (target) { | |
| var run = target.loc[0] - this.loc[0]; | |
| var rise = target.loc[1] - this.loc[1]; | |
| var length = Math.sqrt((rise * rise) + (run * run)); | |
| var unitX = run / length; | |
| var unitY = rise / length; | |
| this.move(unitX, unitY); | |
| }; | |
| Enemy.prototype.die = function () { | |
| for (var j = 0; j < enemyObjs.length; j++) { | |
| if (this === enemyObjs[j]) { | |
| enemies | |
| .filter(function (d, i) { | |
| return i === j; | |
| }) | |
| .remove(); | |
| enemyObjs.splice(j, 1); | |
| gameStats.kills++; | |
| return; | |
| } | |
| } | |
| }; | |
| //---SET UP GAME--- | |
| var paused = true; | |
| //create board d3 selection | |
| var board = d3.select('.board') | |
| .append('svg:svg') | |
| .attr('width', gameOptions.width) | |
| .attr('height', gameOptions.height) | |
| .attr('class', 'svg-canvas') | |
| //instantiate player and append to board, save as selection | |
| var playerObj = new Player([toPixelAxes.x(50), toPixelAxes.y(50)], 10); | |
| playerObj.draw('player'); | |
| var player = d3.select('.player'); | |
| //instantiate and append some enemies | |
| var enemyObjs = []; | |
| var spawnEnemies = function () { | |
| var nEnemies = Math.floor(gameStats.timeLived / 20); | |
| var enemySize = Math.max(5, (gameStats.timeLived / 10)); | |
| if (gameStats.timeLived % 5 === 0 && enemyObjs.length < nEnemies) { | |
| for (var i = 0; i < nEnemies; i++) { | |
| var x = randX(); | |
| var y = randY(); | |
| var newEnemy = new Enemy([x, y], enemySize); | |
| enemyObjs.push(newEnemy); | |
| newEnemy.draw('enemy'); | |
| }; | |
| } | |
| } | |
| var enemies = d3.selectAll('.enemy'); | |
| //instantiate crosshairs and append to board | |
| var crosshairs = new Crosshairs([toPixelAxes.x(50), toPixelAxes.y(50)], 4); | |
| var mouse = { | |
| x: gameOptions.width / 2, | |
| y: gameOptions.height / 2 | |
| }; | |
| crosshairs.draw('crosshairs'); | |
| //mouse movement controls crosshairs | |
| board.on('mousemove', function () { | |
| var loc = d3.mouse(this); | |
| crosshairs.loc[0] = loc[0]; | |
| crosshairs.loc[1] = loc[1]; | |
| d3.select('.crosshairs') | |
| .attr('cx', pixelize(crosshairs.loc[0])) | |
| .attr('cy', pixelize(crosshairs.loc[1])) | |
| }); | |
| //click fires | |
| board.on('click', function () { | |
| if (paused) return; | |
| //only allow shoot if crosshair not inside player | |
| var dx = crosshairs.loc[0] - playerObj.loc[0]; | |
| var dy = crosshairs.loc[1] - playerObj.loc[1]; | |
| var distance = Math.sqrt(dx * dx + dy * dy); | |
| if (distance > crosshairs.size + playerObj.size) { | |
| var targetLoc = crosshairs.loc.slice(); | |
| playerObj.shoot(targetLoc); | |
| } | |
| }); | |
| var shoot = false; | |
| var targetLoc; | |
| var drag = d3.behavior.drag() | |
| .on('drag', function () { | |
| if (paused) return; | |
| //only allow shoot if crosshair not inside player | |
| var dx = crosshairs.loc[0] - playerObj.loc[0]; | |
| var dy = crosshairs.loc[1] - playerObj.loc[1]; | |
| var distance = Math.sqrt(dx * dx + dy * dy); | |
| if (distance > crosshairs.size + playerObj.size) { | |
| targetLoc = crosshairs.loc.slice(); | |
| shoot = true; | |
| } | |
| }); | |
| d3.select('.svg-canvas').call(drag); | |
| //---RUN ONCE GAME IS LOADED--- | |
| $(function () { | |
| //WASD controls | |
| var keyPushStates = {}; | |
| var keyCode; | |
| var logKey = function (event) { | |
| keyCode = event.keyCode; | |
| if (event.type === 'keydown') { | |
| keyPushStates[keyCode] = true; | |
| } else if (event.type === 'keyup') { | |
| keyPushStates[keyCode] = false; | |
| } | |
| }; | |
| d3.select('body').on('keydown', function () { | |
| //spacebar, for pausing | |
| if (d3.event.keyCode === 32) { | |
| if (paused) { | |
| console.log('unpausing...'); | |
| board.style('background-color', '#242424'); | |
| } else { | |
| console.log('pausing...'); | |
| d3.select('.svg-canvas') | |
| board.style('background-color', 'gray'); | |
| } | |
| paused = !paused; | |
| } | |
| logKey(d3.event); | |
| }); | |
| d3.select('body').on('keyup', function () { | |
| logKey(d3.event); | |
| }); | |
| //handle movement | |
| var keysHandler = function () { | |
| //a && w | |
| if (keyPushStates[65] && keyPushStates[87]) { | |
| playerObj.move(-1, -1); | |
| //a && s | |
| } else if (keyPushStates[65] && keyPushStates[83]) { | |
| playerObj.move(-1, 1); | |
| //w && d | |
| } else if (keyPushStates[87] && keyPushStates[68]) { | |
| playerObj.move(1, -1); | |
| //d && s | |
| } else if (keyPushStates[68] && keyPushStates[83]) { | |
| playerObj.move(1, 1); | |
| //a | |
| } else if (keyPushStates[65]) { | |
| playerObj.move(-2, 0); | |
| //w | |
| } else if (keyPushStates[87]) { | |
| playerObj.move(0, -2); | |
| //d | |
| } else if (keyPushStates[68]) { | |
| playerObj.move(2, 0); | |
| //s | |
| } else if (keyPushStates[83]) { | |
| playerObj.move(0, 2); | |
| } | |
| }; | |
| var updateLoc = function (targetSelection, loc) { | |
| targetSelection.datum(loc) | |
| .attr('cx', function (d) { | |
| var x = toPixelAxes.x(toCoordAxes.x(d[0])); | |
| return pixelize(x); | |
| }) | |
| .attr('cy', function (d) { | |
| var y = toPixelAxes.y(toCoordAxes.y(d[1])); | |
| return pixelize(y); | |
| }) | |
| }; | |
| var updateEnemyLoc = function () { | |
| enemies = d3.selectAll('.enemy'); | |
| enemies.each(function (_, i) { | |
| if (enemyObjs[i]) { | |
| updateLoc(d3.select(this), enemyObjs[i].loc); | |
| } | |
| }); | |
| }; | |
| var moveEnemies = function () { | |
| _.each(enemyObjs, function (enemy) { | |
| enemy.chase(playerObj); | |
| }); | |
| }; | |
| var moveBullets = function () { | |
| _.each(bulletObjs, function (bulletObj) { | |
| bulletObj.fire(bulletObj.targetLoc); | |
| }); | |
| }; | |
| var updateMultiLoc = function (selection, objs) { | |
| selection.each(function (_, i) { | |
| updateLoc(d3.select(this), objs[i].loc); | |
| }); | |
| }; | |
| var updateBulletLoc = function () { | |
| d3.selectAll('.bullet').each(function (_, i) { | |
| if (bulletObjs[i]) { | |
| updateLoc(d3.select(this), bulletObjs[i].loc); | |
| if (isMaxedOut(bulletObjs[i].loc)) { | |
| bulletObjs[i].die(); | |
| } | |
| } | |
| }); | |
| }; | |
| //---COLLISION DETECTION--- | |
| var prevCollision = false; | |
| var detectCollisions = function () { | |
| var collision = false; | |
| _.each(enemyObjs, function (enemyObj) { | |
| // the magic of collision detection | |
| var dx = enemyObj.loc[0] - playerObj.loc[0]; | |
| var dy = enemyObj.loc[1] - playerObj.loc[1]; | |
| var distance = Math.sqrt(dx * dx + dy * dy); | |
| if (distance < enemyObj.size + playerObj.size) { | |
| collision = true; | |
| } | |
| }); | |
| if (collision) { | |
| timeLived = 0; | |
| board.style('background-color', 'red'); | |
| gameStats.health--; | |
| d3.select('.scoreboard .health span').text(gameStats.health); | |
| if (gameStats.health <= 0) { | |
| paused = true; | |
| alert('You lose!'); | |
| } | |
| } else { | |
| board.style('background-color', '#242424'); | |
| } | |
| prevCollision = collision; | |
| }; | |
| //---HIT DETECTION--- | |
| var detectHits = function () { | |
| _.each(bulletObjs, function (bulletObj) { | |
| for (var i = 0; i < enemyObjs.length; i++) { | |
| var enemyObj = enemyObjs[i]; | |
| // the magic of collision detection | |
| var dx = enemyObj.loc[0] - bulletObj.loc[0]; | |
| var dy = enemyObj.loc[1] - bulletObj.loc[1]; | |
| var distance = Math.sqrt(dx * dx + dy * dy); | |
| if (distance < enemyObj.size + bulletObj.size) { | |
| enemyObj.die(); | |
| } | |
| }; | |
| }) | |
| } | |
| //---GAME TIMER--- | |
| d3.timer(function () { | |
| if (!paused) { | |
| if (shoot) { | |
| playerObj.shoot(targetLoc); | |
| shoot = false; | |
| } | |
| keysHandler(); | |
| updateLoc(player, playerObj.loc); | |
| moveEnemies(); | |
| updateEnemyLoc(); | |
| moveBullets(); | |
| // updateMultiLoc(d3.selectAll('.bullets'), bulletObjs); | |
| updateBulletLoc(); | |
| detectCollisions(); | |
| detectHits(); | |
| spawnEnemies(); | |
| } | |
| }); | |
| //---SCORE TIMER--- | |
| var scoreTicker = function () { | |
| if (!paused) { | |
| gameStats.timeLived = gameStats.timeLived + 1; | |
| gameStats.highScore = Math.max(gameStats.timeLived, gameStats.highScore); | |
| updateScore(); | |
| } | |
| }; | |
| setInterval(scoreTicker, 100); | |
| }); | |
| </script> | |
| </body> | |
| </html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment