diff --git a/README.md b/README.md index 8f831f5..b6413bb 100644 --- a/README.md +++ b/README.md @@ -23,14 +23,49 @@

- Welcome to the official repository for the IESD Hack Day event. + August 2019 IESD Hack Day event.

## **Getting Started** -Look under branches and find the appropriate month and year for the Hack Day you're participating in. Each branch has its own documentation for getting started. +This repository has the resources to get you prepared for the Hack Day event. + +```Starter code has been update - 8/31/19 @ 1:55 PM.``` If you have any questions, please ask on our [Slack](https://ie-sd.slack.com). We welcome everyone to our Slack, so don't be afraid to join! Have fun coding! **Join our Slack!** + +## **Concepts to Review** +Resources section below has links for these concepts. +- Canvas +- RequestAnimationFrame (JS Method) +- Object Oriented JavaScript +- Prototypal Inheritance +- Sprites +- Keyboard Events + +## **Resources** +List will be updated up until the day of the Hack Day event. +- Making Sprite Based Games with Canvas (2013 article - still relevant) +- HTML5 Canvas Game: 2D Collision Detection +- Adding collision detection to images drawn on canvas on StackOverflow +- Modals in Pure ES6 JavaScript +- KeyboardEvent Value (keyCodes, metaKey, etc) via CSS-Tricks + +### **RequestAnimationFrame** + +The `window.requestAnimationFrame()` method tells the browser that you wish to perform an animation and requests that the browser call a specified function to update an animation before the next repaint. The method takes a callback as an argument to be invoked before the repaint. Read more about this here. + +### **Object-Oriented JavaScript** +Make sure you are comfortable with Object-Oriented JavaScript: + +- What is an object? +- How is an object different from a primitive in JavaScript (e.g., can a primitive have methods?) +- What are some ways to create or instantiate a new object? +- How do you modify properties, or add/remove properties from an object? +- What is a constructor function (or class)? +- What is `this`? What does it refer to and how is it used in different contexts (i.e., in a constructor function, a method, etc.)? +- How do you add a property or method to a constructor's prototype? +- What is prototypal inheritance and how is it implemented? diff --git a/css/style.css b/css/style.css new file mode 100644 index 0000000..e28c912 --- /dev/null +++ b/css/style.css @@ -0,0 +1,3 @@ +body { + text-align: center; +} diff --git a/images/Heart.png b/images/Heart.png new file mode 100644 index 0000000..aa49b5c Binary files /dev/null and b/images/Heart.png differ diff --git a/images/Key.png b/images/Key.png new file mode 100644 index 0000000..7602326 Binary files /dev/null and b/images/Key.png differ diff --git a/images/Rock.png b/images/Rock.png new file mode 100644 index 0000000..29c4e20 Binary files /dev/null and b/images/Rock.png differ diff --git a/images/Selector.png b/images/Selector.png new file mode 100644 index 0000000..e7c5475 Binary files /dev/null and b/images/Selector.png differ diff --git a/images/Star.png b/images/Star.png new file mode 100644 index 0000000..17c0af5 Binary files /dev/null and b/images/Star.png differ diff --git a/images/char-boy.png b/images/char-boy.png new file mode 100644 index 0000000..3dc7c29 Binary files /dev/null and b/images/char-boy.png differ diff --git a/images/char-cat-girl.png b/images/char-cat-girl.png new file mode 100644 index 0000000..dc0538b Binary files /dev/null and b/images/char-cat-girl.png differ diff --git a/images/char-horn-girl.png b/images/char-horn-girl.png new file mode 100644 index 0000000..90f2278 Binary files /dev/null and b/images/char-horn-girl.png differ diff --git a/images/char-pink-girl.png b/images/char-pink-girl.png new file mode 100644 index 0000000..baef177 Binary files /dev/null and b/images/char-pink-girl.png differ diff --git a/images/char-princess-girl.png b/images/char-princess-girl.png new file mode 100644 index 0000000..9d9f958 Binary files /dev/null and b/images/char-princess-girl.png differ diff --git a/images/enemy-bug.png b/images/enemy-bug.png new file mode 100644 index 0000000..191587b Binary files /dev/null and b/images/enemy-bug.png differ diff --git a/images/gem-blue.png b/images/gem-blue.png new file mode 100644 index 0000000..48c677c Binary files /dev/null and b/images/gem-blue.png differ diff --git a/images/gem-green.png b/images/gem-green.png new file mode 100644 index 0000000..ad3da84 Binary files /dev/null and b/images/gem-green.png differ diff --git a/images/gem-orange.png b/images/gem-orange.png new file mode 100644 index 0000000..1f3cd46 Binary files /dev/null and b/images/gem-orange.png differ diff --git a/images/grass-block.png b/images/grass-block.png new file mode 100644 index 0000000..eb04ba9 Binary files /dev/null and b/images/grass-block.png differ diff --git a/images/stone-block.png b/images/stone-block.png new file mode 100644 index 0000000..01113cf Binary files /dev/null and b/images/stone-block.png differ diff --git a/images/water-block.png b/images/water-block.png new file mode 100644 index 0000000..0383ed8 Binary files /dev/null and b/images/water-block.png differ diff --git a/index.html b/index.html new file mode 100644 index 0000000..a8a564f --- /dev/null +++ b/index.html @@ -0,0 +1,13 @@ + + + + + Hack Day - Frogger Clone Starter Code + + + + + + + + diff --git a/js/app.js b/js/app.js new file mode 100644 index 0000000..736378e --- /dev/null +++ b/js/app.js @@ -0,0 +1,183 @@ +//Parent object for sprites +class Populate { + constructor () { + this.x = 0; + this.y = 0; + this.speed = 0; + this.sprite = ""; + this.sideways = 101; + this.upDown = 83; + } + + render () { + ctx.drawImage(Resources.get(this.sprite), this.x, this.y); + } + + reset () { + this.x = 0; + this.y = 415; + } +} + +//Player class +class Player extends Populate { + constructor () { + super(); + this.x = 0; + this.y = 415; + this.sprite = "images/char-boy.png"; + this.blueGemsCollected = 0; + this.greenGemsCollected = 0; + this.orangeGemsCollected = 0; + this.gemsLeft = 0; + this.score = 0; + } + +//key input for Player + handleInput (input) { + switch (input) { + case "left": + if (this.x >= this.sideways) { + this.x -= this.sideways; + } + break; + case "right": + if (this.x <= this.sideways * 3) { + this.x += this.sideways; + } + break; + case "up": + if (this.y >= 83) { + this.y -= this.upDown; + } + break; + case "down": + if (this.y <= this.upDown * 4) { + this.y += this.upDown; + } + break; + } + } + + //updates player and sets condition for collision & win + update () { + for (let enemy of allEnemies) { + if (this.y === enemy.y && (enemy.x + enemy.sideways / 2 > this.x && enemy.x < this.x + this.sideways / 2)) { + this.reset(); + } + } + + for (let gem of allGems) { + if ((gem.x + gem.sideways / 2 > this.x && gem.x < this.x + this.sideways / 2) && + (gem.y - gem.upDown / 2 < this.y && gem.y > this.y - this.upDown / 2)) { + console.log(gemsLeft); + gem.x = -10000; + gem.y = -10000; + gemsLeft--; + + switch (gem.color) { + case "blue": + this.blueGemsCollected++; + this.score += 100; + break; + case "green": + this.greenGemsCollected++; + this.score += 250; + break; + case "orange": + this.orangeGemsCollected++; + this.score += 500; + break; + default: + break; + } + + } + + if (gemsLeft === 0 && this.score > 0) { + resetGems(); + } + + } + } +} + +const player = new Player(); + +//Array to hold Enemy objects +const allEnemies = []; + +//Array to hold gems for points +let allGems = []; + +//Enemy class +class Enemy extends Populate { + constructor (x, y, speed) { + super(); + this.x = x; + this.y = y; + this.speed = speed; + this.sprite = "images/enemy-bug.png"; + this.enemySprite = this.sprite; + } + + //Smooth movement of Enemy objects across gameboard + update (dt) { + if (this.x < this.sideways * 5) { + this.x += this.speed * dt; + } else { + this.x = -100; + } + } +} + +class Gem extends Populate { + constructor (color) { + super(); + this.x = Math.floor(Math.random() * 5) * 101 + 25; + this.y = Math.floor(Math.random() * 5) * 83 + 35; + this.color = color; + this.speed = 0; + this.sprite = "images/gem-"+color+".png"; + this.sideways = 50; + this.upDown = 85; + } +} + +const enemy1 = new Enemy(101, 83, 150); +const enemy2 = new Enemy(404, 166, 350); +const enemy3 = new Enemy(0, 249, 375); +const enemy4 = new Enemy(0, 83, 100); +let gemsLeft = 0; +resetGems(); + +function resetGems() { + allGems = []; + + for (i = 0; i < Math.random() * 3 + 1; i++) + {allGems.push(new Gem("blue")); + gemsLeft++; +console.log(gemsLeft);} + + for (i = 0; i < Math.random() * 3 + 1; i++) + {allGems.push(new Gem("green")); + gemsLeft++;} + + for (i = 0; i < Math.random() * 3 + 1; i++) + {allGems.push(new Gem("orange")); + gemsLeft++;} +} + +allEnemies.push(enemy1, enemy2, enemy3, enemy4); + +// Player.handleInput() method. You don't need to modify this. +document.addEventListener("keyup", function (e) { + var allowedKeys = { + 37: "left", + 38: "up", + 39: "right", + 40: "down" + }; + + player.handleInput(allowedKeys[e.keyCode]); +}); diff --git a/js/engine.js b/js/engine.js new file mode 100644 index 0000000..752fec8 --- /dev/null +++ b/js/engine.js @@ -0,0 +1,156 @@ +/* Engine.js + * This file provides the game loop functionality (update entities and render), + * draws the initial game board on the screen, and then calls the update and + * render methods on your player and enemy objects (defined in your app.js). + * This engine makes the canvas' context (ctx) object globally available to make + * writing app.js a little simpler to work with. + */ + +var Engine = (function (global) { + var doc = global.document, + win = global.window, + canvas = doc.createElement("canvas"), + ctx = canvas.getContext("2d"), + lastTime; + + canvas.width = 505; + canvas.height = 606; + doc.body.appendChild(canvas); + + /* This function serves as the kickoff point for the game loop itself + * and handles properly calling the update and render methods. + */ + function main () { + /* Get our time delta information which is required if your game + * requires smooth animation. + */ + var now = Date.now(), + dt = (now - lastTime) / 1000.0; + + /* Call our update/render functions, pass along the time delta to + * our update function + */ + update(dt); + render(); + + /* Set our lastTime variable which is used to determine the time delta + */ + lastTime = now; + + /* Use the browser's requestAnimationFrame function to call this + * function again as soon as the browser is able to draw another frame. + */ + if (player.winAStar === true) { + modal.style.display = "block"; + win.cancelAnimationFrame; + + } else { + win.requestAnimationFrame(main); + + } + } + + /* This function does some initial setup that should only occur once, + * particularly setting the lastTime variable that is required for the + * game loop. + */ + function init () { + lastTime = Date.now(); + main(); + } + + /* This function is called by main (our game loop) and itself calls all + * of the functions which may need to update entity's data. */ + + function update (dt) { + updateEntities(dt); + } + + /* This is called by the update function and loops through all of the + * objects within your allEnemies array as defined in app.js + */ + function updateEntities (dt) { + allEnemies.forEach(function (enemy) { + enemy.update(dt); + }); + player.update(); + } + + /* This function initially draws the "game level", it will then call + * the renderEntities function. + */ + function render () { + /* This array holds the relative URL to the image used + * for that particular row of the game level. + */ + var rowImages = [ + "images/water-block.png", // Top row is water + "images/stone-block.png", // Row 1 of 3 of stone + "images/stone-block.png", // Row 2 of 3 of stone + "images/stone-block.png", // Row 3 of 3 of stone + "images/grass-block.png", // Row 1 of 2 of grass + "images/grass-block.png" // Row 2 of 2 of grass + ], + numRows = 6, + numCols = 5, + row, col; + + // Before drawing, clear existing canvas + ctx.clearRect(0, 0, canvas.width, canvas.height); + + /* Loop through the number of rows and columns we've defined above + * and, using the rowImages array, draw the correct image for that + * portion of the "grid" + */ + for (row = 0; row < numRows; row++) { + for (col = 0; col < numCols; col++) { + /* The drawImage function of the canvas' context element + * requires 3 parameters: the image to draw, the x coordinate + * to start drawing and the y coordinate to start drawing. + * We're using our Resources helpers to refer to our images + * so that we get the benefits of caching these images, since + * we're using them over and over. + */ + ctx.drawImage(Resources.get(rowImages[row]), col * 101, row * 83); + } + } + + renderEntities(); + } + + /* This function is called by the render function and is called on each game + * tick. Its purpose is to then call the render functions you have defined + * on your enemy and player entities within app.js + */ + function renderEntities () { + allGems.forEach(function (gem) { + gem.render(); + }); + + allEnemies.forEach(function (enemy) { + enemy.render(); + }); + + player.render(); + } + + //Images used in game + Resources.load([ + "images/stone-block.png", + "images/water-block.png", + "images/grass-block.png", + "images/enemy-bug.png", + "images/char-boy.png", + "images/gem-blue.png", + "images/gem-green.png", + "images/gem-orange.png", + "images/Star.png" + ]); + Resources.onReady(init); + + /* Assign the canvas' context object to the global variable (the window + * object when run in a browser) so that developers can use it more easily + * from within their app.js files. + */ + global.ctx = ctx; +})(this); diff --git a/js/resources.js b/js/resources.js new file mode 100644 index 0000000..cd267b5 --- /dev/null +++ b/js/resources.js @@ -0,0 +1,112 @@ +/* Resources.js + * This is simply an image loading utility. It eases the process of loading + * image files so that they can be used within your game. It also includes + * a simple "caching" layer so it will reuse cached images if you attempt + * to load the same image multiple times. + */ +(function() { + var resourceCache = {}; + // var loading = []; + var readyCallbacks = []; + + /* This is the publicly accessible image loading function. It accepts + * an array of strings pointing to image files or a string for a single + * image. It will then call our private image loading function accordingly. + */ + function load(urlOrArr) { + if(urlOrArr instanceof Array) { + /* If the developer passed in an array of images + * loop through each value and call our image + * loader on that image file + */ + urlOrArr.forEach(function(url) { + _load(url); + }); + } else { + /* The developer did not pass an array to this function, + * assume the value is a string and call our image loader + * directly. + */ + _load(urlOrArr); + } + } + + /* This is our private image loader function, it is + * called by the public image loader function. + */ + function _load(url) { + if(resourceCache[url]) { + /* If this URL has been previously loaded it will exist within + * our resourceCache array. Just return that image rather + * re-loading the image. + */ + return resourceCache[url]; + } else { + /* This URL has not been previously loaded and is not present + * within our cache; we'll need to load this image. + */ + var img = new Image(); + img.onload = function() { + /* Once our image has properly loaded, add it to our cache + * so that we can simply return this image if the developer + * attempts to load this file in the future. + */ + resourceCache[url] = img; + + /* Once the image is actually loaded and properly cached, + * call all of the onReady() callbacks we have defined. + */ + if(isReady()) { + readyCallbacks.forEach(function(func) { func(); }); + } + }; + + /* Set the initial cache value to false, this will change when + * the image's onload event handler is called. Finally, point + * the image's src attribute to the passed in URL. + */ + + resourceCache[url] = false; + img.src = url; + } + } + + /* This is used by developers to grab references to images they know + * have been previously loaded. If an image is cached, this functions + * the same as calling load() on that URL. + */ + function get(url) { + return resourceCache[url]; + } + + /* This function determines if all of the images that have been requested + * for loading have in fact been properly loaded. + */ + function isReady() { + var ready = true; + for(var k in resourceCache) { + if(resourceCache.hasOwnProperty(k) && + !resourceCache[k]) { + ready = false; + } + } + return ready; + } + + /* This function will add a function to the callback stack that is called + * when all requested images are properly loaded. + */ + function onReady(func) { + readyCallbacks.push(func); + } + + /* This object defines the publicly accessible functions available to + * developers by creating a global Resources object. + */ + window.Resources = { + load: load, + get: get, + onReady: onReady, + isReady: isReady + }; +})();