1./* "driving.js" is a simple driving simulator written to introduce students to the basics of game programming. Author: Bob Jones & Raven Lord Date: October 10th 2016 Usage: This script is invoked by loading "driving.html" in a browser. */ // Game Parameters /* Note that ANIMDELTA should be a factor of BLOCKSIZE (1 for per-pixel animation, higher for performance, BLOCKSIZE for charm) */ const BLOCKSIZE = 20; // Most dimensions are multiples of this const ANIMDELTA = 20; // Should be a factor of BLOCKSIZE (read above) const DIFFICULTYPERIOD = 1000; // Period between difficulty increases in ms const INITTIMEDELTA = 60; // Initial delay between frames in ms const MINTIMEDELTA = 5; // Browser is highly unlikely to go any faster const TITLE = "Driving Game"; const PAGEMARGIN = 20; const PAGECOLOUR = "White"; const MAINFONTCOLOUR = "Black"; const HEADINGBGCOLOUR = "White"; const INSBGCOLOUR = "White"; const PLAYAREAWIDTH = 35 * BLOCKSIZE; const PLAYAREAHEIGHT = 25 * BLOCKSIZE; const PLAYAREACOLOUR = "OliveDrab"; const PLAYAREAZINDEX = "-5"; const ROADWIDTH = 11 * BLOCKSIZE; // Best as odd number of blocks const ROADCOLOUR = "Silver"; const ROADZINDEX = "-4"; const ROADINITPOS = Math.floor((PLAYAREAWIDTH - ROADWIDTH) / 2); // halfway const NUMROADPIECES = Math.floor(PLAYAREAHEIGHT/ANIMDELTA); const ROADGOESLEFT = 0.4; // roadDiceThrow < this value, road curves left const ROADGOESRIGHT = 0.6; // roadDiceThrow > this value, road curves right const CARCOLOUR = "Yellow"; const CARZINDEX = "-3"; const CARINITPOS = Math.floor((PLAYAREAWIDTH - BLOCKSIZE) / 2); // halfway const FLASHCOLOUR = "Red"; // Colour used in death animation const FLASHPERIOD = 50; // Controls rate of flashing in death animation const NUMFLASHES = 20; // Control how long the death animation goes for // Setup the page document.body.style.margin = PAGEMARGIN + "px"; document.body.style.background = PAGECOLOUR; document.body.style.textAlign = "center"; var pageTitle = document.createElement("title"); pageTitle.innerHTML = TITLE; document.head.appendChild(pageTitle); var pageHeading = document.createElement("div"); pageHeading.style.width = (PLAYAREAWIDTH - 20) + "px"; pageHeading.style.color = MAINFONTCOLOUR; pageHeading.style.background = HEADINGBGCOLOUR; pageHeading.style.textAlign = "center"; pageHeading.style.font = "bold 20px Verdana,Geneva,sans-serif"; pageHeading.style.margin = "0px auto 0px auto"; pageHeading.style.padding = "10px 10px 10px 10px"; pageHeading.innerHTML = TITLE; document.body.appendChild(pageHeading); var scoreBox = document.createElement("div"); scoreBox.style.width = (PLAYAREAWIDTH - 20) + "px"; scoreBox.style.color = MAINFONTCOLOUR; scoreBox.style.background = HEADINGBGCOLOUR; scoreBox.style.textAlign = "center"; scoreBox.style.font = "14px Verdana,Geneva,sans-serif"; scoreBox.style.margin = "0px auto 0px auto"; scoreBox.style.padding = "0px 10px 10px 10px"; scoreBox.innerHTML = "Score: 0"; document.body.appendChild(scoreBox); // Create the playing area var playArea = document.createElement("div"); playArea.style.width = PLAYAREAWIDTH + "px"; playArea.style.height = PLAYAREAHEIGHT + "px"; playArea.style.margin = "0px auto 0px auto"; playArea.style.background = PLAYAREACOLOUR; playArea.style.position = "relative"; playArea.style.zIndex = PLAYAREAZINDEX; document.body.appendChild(playArea); // Display the instructions var instructions = document.createElement("div"); instructions.style.width = (PLAYAREAWIDTH - 14) + "px"; instructions.style.color = MAINFONTCOLOUR; instructions.style.background = INSBGCOLOUR; instructions.style.textAlign = "center"; instructions.style.font = "14px Verdana,Geneva,sans-serif"; instructions.style.margin = "0px auto 0px auto"; instructions.style.padding = "7px"; instructions.innerHTML = "S  Start     ◀  Left     ▶  Right"; document.body.appendChild(instructions); // Create the road var roadPieces = []; // an array of the rectangles that make up the road var roadPiece = 0; // will serve as the index of the roadPieces array for (roadPiece = 0; roadPiece < NUMROADPIECES ; roadPiece++) { roadPieces[roadPiece] = document.createElement("div"); roadPieces[roadPiece].style.width = ROADWIDTH + "px"; roadPieces[roadPiece].style.height = ANIMDELTA + "px"; roadPieces[roadPiece].style.background = ROADCOLOUR; roadPieces[roadPiece].style.position = "absolute"; roadPieces[roadPiece].style.zIndex = ROADZINDEX; roadPieces[roadPiece].style.left = ROADINITPOS + "px"; roadPieces[roadPiece].style.top = (roadPiece * ANIMDELTA) + "px"; playArea.appendChild(roadPieces[roadPiece]); } var carOnPiece = Math.floor(3/4*NUMROADPIECES); // constant in this version // Create the car (the manipulation below results in a triangle) var car = document.createElement("div"); car.style.width = BLOCKSIZE + "px"; car.style.height = BLOCKSIZE + "px"; car.style.background = CARCOLOUR; car.style.position = "absolute"; car.style.zIndex = CARZINDEX; car.style.left = CARINITPOS + "px"; car.style.top = roadPieces[carOnPiece].style.top; playArea.appendChild(car); // Listen for key presses var gameIsRunning = false; var changeInCarPos = 0; // used for steering document.onkeydown = checkDown; function checkDown(e) { if(e.keyCode === 37) changeInCarPos = -ANIMDELTA; // left cursor else if(e.keyCode === 39) changeInCarPos = ANIMDELTA; // right cursor else if(e.keyCode === 83 && !gameIsRunning) // S key { gameIsRunning = true; runGame(); } } document.onkeyup = checkLift; function checkLift(e) { if ( (e.keyCode === 37 && changeInCarPos < 0) || // left (e.keyCode === 39 && changeInCarPos > 0) // right ) changeInCarPos = 0; } // The game loop function runGame() { var score = 0; // The score var timeDelta = INITTIMEDELTA; // Initial delay between each frame var flashNumber = NUMFLASHES; // Flashes left on death animation var carLeftEdge = CARINITPOS; var roadLeftEdges = []; for (roadPiece = 0; roadPiece < NUMROADPIECES ; roadPiece++) roadLeftEdges[roadPiece] = ROADINITPOS; var changedLeftRoadEdge = ROADINITPOS; var roadDiceThrow = 0; var changeInRoadPos = 0; // note: changeInCarPos is defined above // reset positions of the road and car render(); // start difficulty timer var difficultyTimer = setTimeout(increaseDifficulty, DIFFICULTYPERIOD); function increaseDifficulty() { if (gameIsRunning) { if (timeDelta > MINTIMEDELTA) timeDelta--; difficultyTimer = setTimeout(increaseDifficulty, DIFFICULTYPERIOD); } } // start the game loop var frame = setTimeout(gameLoop, timeDelta); function gameLoop() { // calculate movement carLeftEdge += changeInCarPos; if (carLeftEdge < 0) carLeftEdge = 0; else if (carLeftEdge > (PLAYAREAWIDTH - BLOCKSIZE)) carLeftEdge = PLAYAREAWIDTH - BLOCKSIZE; roadDiceThrow = Math.random(); if (roadDiceThrow < ROADGOESLEFT) changeInRoadPos = -ANIMDELTA; else if (roadDiceThrow >= ROADGOESRIGHT) changeInRoadPos = ANIMDELTA; else changeInRoadPos = 0; changedLeftRoadEdge += changeInRoadPos; if (changedLeftRoadEdge < 0) changedLeftRoadEdge = 0; else if (changedLeftRoadEdge > (PLAYAREAWIDTH - ROADWIDTH)) changedLeftRoadEdge = PLAYAREAWIDTH - ROADWIDTH; for (roadPiece = NUMROADPIECES - 1; roadPiece >= 1; roadPiece --) roadLeftEdges[roadPiece] = roadLeftEdges[roadPiece -1]; roadLeftEdges[0] = changedLeftRoadEdge; // render new physical state render(); // collision detection if( (carLeftEdge < roadLeftEdges [carOnPiece]) || (carLeftEdge + BLOCKSIZE > roadLeftEdges[carOnPiece] + ROADWIDTH) ) { gameIsRunning = false; frame = setTimeout(deathAnimation, FLASHPERIOD); } // scoring score++; // loop again if the game is still going if (gameIsRunning) frame = setTimeout (gameLoop, timeDelta); } function render() { for (roadPiece = 0; roadPiece < NUMROADPIECES ; roadPiece++) roadPieces[roadPiece].style.left = roadLeftEdges[roadPiece] + "px"; car.style.left = carLeftEdge + "px"; //scoreBox.innerHTML = "Score: " + score + " (" + timeDelta + ")"; scoreBox.innerHTML = "Score: " + score; } function deathAnimation() { if (flashNumber % 2 == 0) car.style.background = FLASHCOLOUR; else car.style.background = CARCOLOUR; flashNumber--; if (flashNumber > 0 && !gameIsRunning) frame = setTimeout(deathAnimation, FLASHPERIOD); else car.style.background = CARCOLOUR; } }