diff --git a/README.md b/README.md index 317a3e58..3aef9ebe 100644 --- a/README.md +++ b/README.md @@ -1,96 +1,187 @@ -# Project Name (Start editing here) - - -# ![](https://ga-dash.s3.amazonaws.com/production/assets/logo-9f88ae6c9c3871690e33280fcf557f33.png) Project #1: The Game - -### Overview - -Let's start out with something fun - **a game!** - -Everyone will get a chance to **be creative**, and work through some really **tough programming challenges** – since you've already gotten your feet wet with Tic Tac Toe, it's up to you to come up with a fun and interesting game to build. - -**You will be working individually for this project**, but we'll be guiding you along the process and helping as you go. Show us what you've got! - - ---- - -### Technical Requirements - -Your app must: - -* **Render a game in the browser** -* **Any number of players** will be okay, switch turns will be great -* **Design logic for winning** & **visually display which player won** -* **Include separate HTML / CSS / JavaScript files** -* Stick with **KISS (Keep It Simple Stupid)** and **DRY (Don't Repeat Yourself)** principles -* Use **Javascript** for **DOM manipulation**, jQuery is not compulsory -* **Deploy your game online**, where the rest of the world can access it -* Use **semantic markup** for HTML and CSS (adhere to best practices) -* **No canvas** project will be accepted, only HTML5 + CSS3 + JS please - ---- - -### Necessary Deliverables - -* A **working game, built by you**, hosted somewhere on the internet -* A **link to your hosted working game** in the URL section of your GitHub repo -* A **git repository hosted on GitHub**, with a link to your hosted game, and frequent commits dating back to the very beginning of the project -* **A ``readme.md`` file** with explanations of the technologies used, the approach taken, installation instructions, unsolved problems, etc. - ---- - -### Suggested Ways to Get Started - -* **Break the project down into different components** (data, presentation, views, style, DOM manipulation) and brainstorm each component individually. Use whiteboards! -* **Use your Development Tools** (console.log, inspector, alert statements, etc) to debug and solve problems -* Work through the lessons in class & ask questions when you need to! Think about adding relevant code to your game each night, instead of, you know... _procrastinating_. -* **Commit early, commit often.** Don’t be afraid to break something because you can always go back in time to a previous version. -* **Consult documentation resources** (MDN, jQuery, etc.) at home to better understand what you’ll be getting into. -* **Don’t be afraid to write code that you know you will have to remove later.** Create temporary elements (buttons, links, etc) that trigger events if real data is not available. For example, if you’re trying to figure out how to change some text when the game is over but you haven’t solved the win/lose game logic, you can create a button to simulate that until then. - ---- - -### Potential Project Ideas - -##### Blackjack -Make a one player game where people down on their luck can lose all their money by guessing which card the computer will deal next! - -##### Self-scoring Trivia -Test your wits & knowledge with whatever-the-heck you know about (so you can actually win). Guess answers, have the computer tell you how right you are! - ---- - -### Useful Resources - -* **[MDN Javascript Docs](https://developer.mozilla.org/en-US/docs/Web/JavaScript)** _(a great reference for all things Vanilla Javascript)_ -* **[jQuery Docs](http://api.jquery.com)** _(if you're using jQuery)_ -* **[GitHub Pages](https://pages.github.com)** _(for hosting your game)_ -* **[How to write readme - Markdown CheatSheet](https://github.com/adam-p/markdown-here/wiki/Markdown-Cheatsheet)** _(for editing this readme)_ -* **[How to write a good readme for github repo!](https://gist.github.com/PurpleBooth/109311bb0361f32d87a2)** _(to make it better)_ - ---- - -### Project Feedback + Evaluation - -* __Project Workflow__: Did you complete the user stories, wireframes, task tracking, and/or ERDs, as specified above? Did you use source control as expected for the phase of the program you’re in (detailed above)? - -* __Technical Requirements__: Did you deliver a project that met all the technical requirements? Given what the class has covered so far, did you build something that was reasonably complex? - -* __Creativity__: Did you add a personal spin or creative element into your project submission? Did you deliver something of value to the end user (not just a login button and an index page)? - -* __Code Quality__: Did you follow code style guidance and best practices covered in class, such as spacing, modularity, and semantic naming? Did you comment your code as your instructors have in class? - -* __Deployment__: Did you deploy your application to a public url using GitHub Pages? - -* __Total__: Your instructors will give you a total score on your project between: - - Score | Expectations - ----- | ------------ - **0** | _Incomplete._ - **1** | _Does not meet expectations._ - **2** | _Meets expectations, good job!_ - **3** | _Exceeds expectations, you wonderful creature, you!_ - - This will serve as a helpful overall gauge of whether you met the project goals, but __the more important scores are the individual ones__ above, which can help you identify where to focus your efforts for the next project! +# **Basic Pac-Man** + +flowchart.png + +## Overview + +This is a simplified version of Pac Man. + +The player controls Pac-Man through a maze of various dots, as well as four multi-coloured ghosts. The goal of the game is to consume all the dots. The four ghosts roam the maze, trying to kill Pac-Man. If any of the ghosts touch Pac-Man, he loses a life; when all lives have been lost, the game ends. + +## Instructions + ++ Arrow-Left '◀' : Move 'Left' ++ Arrow-Right '▶' : Move 'Right' ++ Arrow-Up '▲' : Move 'Up' ++ Arrow-Down '▼' : Move 'Down' + +## [▶ Click Here to Play Game ◀](https://koozy0.github.io/project-1/) + +## Considerations + ++ Grid-style layout VS open game board while actively checking for collision at set intervals. ++ Since game board is reasonably small, a grid-style layout is used to reduce the amount of resources consumed by the game ++ Constantly checking for collision against all elements VS checking if the target tile is valid at the point of movement. ++ Also makes generating the game assets easier. + +## Loading Game Assets + +A 19x18 array containing numbers between 0 - 9 is used to generate the game board. Each number denotes which assets to generate as well as mark out the location of the Pac-Dots, Pac Man and the four Ghosts. A loop is used to go through the array and generate the game assets. + +Below is a code snippet: + +``` javascript +function loadAssets (tileSet) { + // objects to store css properties for tiles + var blackTile = { 'background-color': 'black', 'height': '28px', 'width': '28px', 'border': '1px solid #303030' } + var blueTile = { 'background-color': '#2D47DD', 'height': '28px', 'width': '28px', 'border': '1px solid #3366FF' } + var yellowTile = { 'background-color': '#FFCC00', 'height': '5px', 'width': '30px' } + + // loop through tileSet array and generate map as well as load characters + for (var i = 0; i < tileSet.length; i++) { + // create new
for tiles and dots + var $tile = $('
') + // setting tile properties and adding characters + switch (tileSet[i]) { + case 0: $tile.css(blackTile).data('attr', 0).append($('
')) + break + case 1: $tile.css(blueTile).data('attr', 1) + break + case 2: $tile.css(blackTile).data('attr', 2) + break + case 5: $tile.css(blackTile).data('attr', 5).append($('
')) + break + case 6: $tile.css(blackTile).data('attr', 6).append($('
')) + break + case 7: $tile.css(blackTile).data('attr', 7).append($('
')) + break + case 8: $tile.css(yellowTile).data('attr', 8).append($('
')) + break + case 9: $tile.css(blackTile).data('attr', 9).append($('
')) + break + } + $gameBoard.append($tile) + } +} +``` + +## Collision Logic + +Basic Pac Man is created with a grid-style layout. No collision algorithms are used. Each individual tile has it's own data attribute, denoting whether it is a tile which the 5 characters can enter. Checking whether Pac Man touches any of the four Ghosts is a simple check for whether any of other elements inhabit the same tile as Pac Man or his target tile. Checking whether Pac Man touches any of the Pac-Dots is a check for whether Pac Man inhabits the same tile as the Pac-Dots. + +Below is a code snippet: + +``` javascript +function checkCollision () { + var $pacManTile = $('#pac-man').parent() + var targetTile = pacTarget(direction) + // return true if pacman's parent tile or pacman's target tile contains any of the ghosts + switch (true) { + case ($pacManTile.has('#ghost-one, #ghost-two, #ghost-three, #ghost-four').length > 0): + case ($(targetTile).has('#ghost-one, #ghost-two, #ghost-three, #ghost-four').length > 0): + return true + } +} +``` + +## Moving Pac-Man + +To move Pac Man through the grid, the game first gets the tile which Pac-Man is inhabiting. Depending on which tile Pac Man is trying to move into, the game then gets the target tile and checks it's data attribute to determine if the tile selected is a valid tile. Event handlers are used to change Pac Man's direction depending on which ArrowKey is hit. + +Below are some code snippets: + +``` javascript +// start game after 3 seconds +setTimeout(function () { + // moving pacman + setInterval(function () { movePac() }, pacManSpeed) + // switching directions + $body.on('keydown', (event) => { direction = changeDirection(event) }) + // moving ghost-one + setInterval(function () { patrolTopLeft($('#ghost-one')) }, ghostSpeed) +}, 3000) + +function pacTarget (direction) { + // using direction, target tile + var $pacManTile = $('#pac-man').parent() + + switch (direction) { + case 'left': return $pacManTile.prev() + case 'right': return $pacManTile.next() + case 'up': return $pacManTile.prevAll().eq(18) + case 'down': return $pacManTile.nextAll().eq(18) + } +} + +function movePac () { + var targetTile = pacTarget(direction) + // check if tile has dots + if ($(targetTile).has('.dots').length > 0) eatAndChangeScore($(targetTile)) + // check if pac-man can move into tile + if ($(targetTile).data('attr') !== 1 && $(targetTile).data('attr') !== 8) $(targetTile).append($('#pac-man')) +} + +function changeDirection (event) { + switch (event.key) { + case 'ArrowUp': return 'up' + case 'ArrowRight': return 'right' + case 'ArrowDown': return 'down' + case 'ArrowLeft': return 'left' + } +} +``` + +## Ghosts Movement + +Ghosts movement use the same logic as Pac Man. Initial parent tile of the ghost is captured, then the target tile is evaluated if it is a viable tile. + +However, ghosts movement has to be automated. Four separate patrol functions were created and assigned to each of the four ghosts. + +Below are some code snippets: + +``` javascript +// Ghost Variables +var ghostSpeed = 400 + +// moving ghost-two after 6 seconds +setTimeout(function () { setInterval(function () { patrolTopRight($('#ghost-two')) }, ghostSpeed) }, 6000) + +// ghost-two variables +var prevDirTwo = [] + +function patrolTopRight ($ghost) { + var $ghostTile = $ghost.parent() + var $ghostUp = $ghostTile.prevAll().eq(18) + var $ghostLeft = $ghostTile.prev() + var $ghostRight = $ghostTile.next() + var $ghostDown = $ghostTile.nextAll().eq(18) + + if ($ghostUp.data('attr') !== 1 && prevDirTwo[0] !== 'down' && prevDirTwo[1] !== 'down') { + prevDirTwo.push('up') + $ghostUp.append($ghost) + } else if ($ghostRight.data('attr') !== 1 && prevDirTwo[0] !== 'left' && prevDirTwo[1] !== 'left') { + $ghostRight.append($ghost) + prevDirTwo.push('right') + } else if ($ghostLeft.data('attr') !== 1 && prevDirTwo[0] !== 'right' && prevDirTwo[1] !== 'right') { + prevDirTwo.push('left') + $ghostLeft.append($ghost) + } else if ($ghostDown.data('attr') !== 1 && prevDirTwo[0] !== 'up' && prevDirTwo[1] !== 'up') { + prevDirTwo.push('down') + $ghostDown.append($ghost) + } + // shift entire array left by one element (keeps array 2 elements long) + if (prevDirTwo.length > 2) prevDirTwo.shift() +} +``` + +## Reflections + ++ Creating a character class which stores the position of the character's parent tile, a method to get and store the four adjacent tiles and an array variable to store the ghosts' last two moves will reduce the amount of code repetition and global variables. ++ Ghosts movement is overly simple. Two movement modes were originally planned: Scatter and Chase. Scatter is the current patrol mode, while chase will cause the ghosts to actively track and chase Pac Man. The Ghosts were to switch between the two modes at every fixed interval. ++ Plan further ahead. Some features were not implemented due to time constraints (Proper restart function, Ghosts Chase mode, Power Pellets). + +## Resources + ++ [Get dynamically added element by data attribute using jQuery](https://stackoverflow.com/questions/31402103/how-to-find-dynamically-added-element-by-data-attribute-using-jquery) ++ [Ghost Behaviour](http://gameinternals.com/post/2072558330/understanding-pac-man-ghost-behavior) ++ [Font](https://fonts.googleapis.com/css?family=Bungee) diff --git a/assets/css/reset.css b/assets/css/reset.css new file mode 100644 index 00000000..af944401 --- /dev/null +++ b/assets/css/reset.css @@ -0,0 +1,48 @@ +/* http://meyerweb.com/eric/tools/css/reset/ + v2.0 | 20110126 + License: none (public domain) +*/ + +html, body, div, span, applet, object, iframe, +h1, h2, h3, h4, h5, h6, p, blockquote, pre, +a, abbr, acronym, address, big, cite, code, +del, dfn, em, img, ins, kbd, q, s, samp, +small, strike, strong, sub, sup, tt, var, +b, u, i, center, +dl, dt, dd, ol, ul, li, +fieldset, form, label, legend, +table, caption, tbody, tfoot, thead, tr, th, td, +article, aside, canvas, details, embed, +figure, figcaption, footer, header, hgroup, +menu, nav, output, ruby, section, summary, +time, mark, audio, video { + margin: 0; + padding: 0; + border: 0; + font-size: 100%; + font: inherit; + vertical-align: baseline; +} +/* HTML5 display-role reset for older browsers */ +article, aside, details, figcaption, figure, +footer, header, hgroup, menu, nav, section { + display: block; +} +body { + line-height: 1; +} +ol, ul { + list-style: none; +} +blockquote, q { + quotes: none; +} +blockquote:before, blockquote:after, +q:before, q:after { + content: ''; + content: none; +} +table { + border-collapse: collapse; + border-spacing: 0; +} \ No newline at end of file diff --git a/assets/css/stylesheet.css b/assets/css/stylesheet.css index e69de29b..a7082e3d 100644 --- a/assets/css/stylesheet.css +++ b/assets/css/stylesheet.css @@ -0,0 +1,259 @@ +body { + margin: 0; + height: 100vh; + width: 100vw; + overflow: hidden; + font-family: "Bungee", cursive; + background-color: black; + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; +} + +.container { + /*border: 1px solid red;*/ + /*background-color: red;*/ + width: 1000px; + max-width: 90vw; + height: 720px; + max-height: 90vh; + margin: 0 auto; + display: flex; + flex-direction: column; + justify-content: space-around; +} + +.window { + /*border: 1px solid blue;*/ + background-color: black; + width: 800px; + max-width: 80vw; + height: 700px; + max-height: 80vh; + margin: 0 auto; + display: flex; + flex-direction: column; + justify-content: space-around; +} + +.game-board { + /*border: 1px solid green;*/ + background-color: black; + width: 570px; + height: 540px; + max-width: 70vw; + max-height: 70vh; + margin: 0 auto; + display: flex; + flex-wrap: wrap; + align-content: flex-start; + position: relative; + /*display: none;*/ +} + +.instruction-panel { + /*border: 1px solid purple;*/ + background-color: #404040; + width: 570px; + height: 540px; + max-width: 70vw; + max-height: 70vh; + margin: 0 auto; + display: flex; + justify-content: center; + flex-direction: column; +} + +.score-board { + /*border: 1px solid cyan;*/ + background-color: #2D47DD; + margin: 0px; + padding: 0px; + height: 50px; + display: flex; + justify-content: space-between; + align-items: center; + visibility: hidden; +} + +.win-panel { + background-color: #404040; + width: 570px; + height: 540px; + max-width: 70vw; + max-height: 70vh; + margin: 0 auto; + display: flex; + justify-content: center; + align-items: center; + flex-direction: column; + position: absolute; + z-index: 1; +} + +.lose-panel { + background-color: #404040; + width: 570px; + height: 540px; + max-width: 70vw; + max-height: 70vh; + margin: 0 auto; + display: flex; + justify-content: center; + align-items: center; + flex-direction: column; + position: absolute; + z-index: 1; +} + +.timer { + height: 60px; + width: 60px; + top: 300px; + left: 250px; + display: flex; + justify-content: center; + align-items: center; + position: absolute; + z-index: 2; +} + +#time { + font-size: 60px; + color: #FFCC00; + position: absolute; + z-index: 3; +} + +.tile { + display: flex; + justify-content: center; + align-items: center; + color: white; +} + +.dots { + width: 8px; + height: 8px; + border-radius: 4px; + background-color: #F8C6AB; +} + +.character { + width: 28px; + height: 28px; +} + +#pac-man { + background-color: yellow; + border-radius: 14px; +} + +#ghost-one { + background: url("../images/ghosts.jpg"); + background-position: -33px -35px; + background-size: 230%; + position: absolute; +} + +#ghost-two { + background: url("../images/ghosts.jpg"); + background-position: -3px -35px; + background-size: 230%; + position: absolute; +} + +#ghost-three { + background: url("../images/ghosts.jpg"); + background-position: -3px -7px; + background-size: 230%; + position: absolute; +} + +#ghost-four { + /*background-color: cyan;*/ + background: url("../images/ghosts.jpg"); + background-position: -33px -7px; + background-size: 230%; + position: absolute; +} + +h1 { + font-size: 50px; + font-weight: 500; + text-align: center; + color: #FFCC00; +} + +h2 { + font-size: 40px; + font-weight: 400; + text-align: center; + text-transform: uppercase; + color: #FFCC00; +} + +h3 { + font-size: 40px; + font-weight: 400;' + text-align: center; + text-transform: uppercase; + color: #99ccff; + margin: 5px; +} + +p { + font-size: 16px; + line-height: 30px; + text-align: center; + color: #99ccff; + margin: 20px; +} + +span { + font-size: 26px; + line-height: 40px; +} + +strong { + font-size: 30px; + font-weight: 400; + line-height: 50px; + text-transform: uppercase; + color: #FFCC00; +} + +.lives-container { + width: 300px; + height: 40px; + display: flex; + align-items: center; + justify-content: flex-start; +} + +.lives { + width: 40px; + height: 40px; + margin-right: 5px; + background-image: url("../images/pacman.png"); + background-size: 100% 100%; + transform: scaleX(-1); +} + +.hit-enter { + font-size: 30px; + font-weight: 400; + line-height: 40px; + color: #FFCC00; + text-transform: uppercase; + /*animation-name: blink; + animation-duration: 1s; + animation-iteration-count: infinite;*/ + animation: blink 0.6s ease-in infinite; +} + +@keyframes blink { + 0% { color: black; } + 100% { color: #FFCC00; } +} diff --git a/assets/images/flowchart.png b/assets/images/flowchart.png new file mode 100644 index 00000000..eb7089cb Binary files /dev/null and b/assets/images/flowchart.png differ diff --git a/assets/images/ghosts.jpg b/assets/images/ghosts.jpg new file mode 100644 index 00000000..8cb2b0eb Binary files /dev/null and b/assets/images/ghosts.jpg differ diff --git a/assets/images/pacman.png b/assets/images/pacman.png new file mode 100644 index 00000000..a0b69482 Binary files /dev/null and b/assets/images/pacman.png differ diff --git a/assets/js/script.js b/assets/js/script.js index e69de29b..e92849e5 100644 --- a/assets/js/script.js +++ b/assets/js/script.js @@ -0,0 +1,321 @@ +// tileSet array (for generating map) +var tileSet = [ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, + 1, 0, 1, 1, 1, 0, 1, 1, 0, 1, 0, 1, 1, 0, 1, 1, 1, 0, 1, + 1, 0, 1, 1, 1, 0, 1, 1, 0, 1, 0, 1, 1, 0, 1, 1, 1, 0, 1, + 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, + 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 0, 1, + 1, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 1, + 1, 1, 1, 1, 1, 1, 2, 1, 1, 8, 1, 1, 2, 1, 1, 1, 1, 1, 1, + 2, 2, 2, 2, 2, 2, 2, 1, 7, 6, 5, 1, 2, 2, 2, 2, 2, 2, 2, + 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, + 1, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 1, + 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 0, 1, + 1, 0, 1, 1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 1, 1, 0, 1, + 1, 0, 0, 0, 0, 0, 1, 1, 0, 9, 0, 1, 1, 0, 0, 0, 0, 0, 1, + 1, 0, 1, 1, 1, 0, 1, 1, 0, 1, 0, 1, 1, 0, 1, 1, 1, 0, 1, + 1, 0, 1, 1, 1, 0, 1, 1, 0, 1, 0, 1, 1, 0, 1, 1, 1, 0, 1, + 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 +] + +// jQuery objects +var $body = $('body') +var $gameBoard = $('.game-board') +var $instructionPanel = $('.instruction-panel') +var $winPanel = $('.win-panel') +var $losePanel = $('.lose-panel') +var $scoreBoard = $('.score-board') +var $tiles = $('.tile') + +// game variables +var score = 0 +var isGameOver = false +// pacman variables +var direction = 'left' +var pacManSpeed = 500 +var pacManMeetGhost = false +var pacManLives = 3 +// ghost variables +var ghostSpeed = 300 +var prevDirOne = [] // ghost-one variables +var prevDirTwo = [] // ghost-two variables +var prevDirThree = [] // ghost-three variables +var prevDirFour = [] // ghost-four variables + +$(function () { + togglePanels('hide') + // hit enter to start + $body.on('keyup', (event) => { + if (event.key === 'Enter') { + // preparing game board and countdown to start + $body.off() + isGameOver = false + togglePanels('starting') + loadAssets(tileSet) + countDown() + + // start game after 3 seconds + setTimeout(function () { + // moving pacman + setInterval(function () { movePac() }, pacManSpeed) + // switching directions + $body.on('keydown', (event) => { direction = changeDirection(event) }) + // moving ghost-one + setInterval(function () { patrolTopLeft($('#ghost-one')) }, ghostSpeed) + }, 3000) + // moving ghost-two after 6 seconds + setTimeout(function () { setInterval(function () { patrolTopRight($('#ghost-two')) }, ghostSpeed) }, 6000) + // moving ghost-three after 9 seconds + setTimeout(function () { setInterval(function () { patrolBottomRight($('#ghost-three')) }, ghostSpeed) }, 9000) + // moving ghost-four after 12 seconds + setTimeout(function () { setInterval(function () { patrolBottomLeft($('#ghost-four')) }, ghostSpeed) }, 12000) + // lose condition + setInterval(function () { + pacManMeetGhost = checkCollision() + if (pacManMeetGhost) { + movePacToStart() + updateLives() + pacManMeetGhost = false + } + if (pacManLives <= 0) togglePanels('loss') + }, 100) + // win condition + setInterval(function () { if (score === 119) togglePanels('win') }, 300) + // clearing game board + var clear = setInterval(function () { + if (isGameOver) { + $tiles.remove() + clearInterval(clear) + } + }, 100) + // reloads window to restart game + $body.on('keydown', (event) => { if (isGameOver && event.key === ' ') this.location.reload() }) + } + }) +}) + +function loadAssets (tileSet) { + // objects to store css properties for tiles + var blackTile = { 'background-color': 'black', 'height': '28px', 'width': '28px', 'border': '1px solid #303030' } + var blueTile = { 'background-color': '#2D47DD', 'height': '28px', 'width': '28px', 'border': '1px solid #3366FF' } + var yellowTile = { 'background-color': '#FFCC00', 'height': '5px', 'width': '30px' } + + // loop through tileSet array and generate map as well as load characters + for (var i = 0; i < tileSet.length; i++) { + // create new
for tiles and dots + var $tile = $('
') + // setting tile properties and adding characters + switch (tileSet[i]) { + case 0: $tile.css(blackTile).data('attr', 0).append($('
')) + break + case 1: $tile.css(blueTile).data('attr', 1) + break + case 2: $tile.css(blackTile).data('attr', 2) + break + case 5: $tile.css(blackTile).data('attr', 5).append($('
')) + break + case 6: $tile.css(blackTile).data('attr', 6).append($('
')) + break + case 7: $tile.css(blackTile).data('attr', 7).append($('
')) + break + case 8: $tile.css(yellowTile).data('attr', 8).append($('
')) + break + case 9: $tile.css(blackTile).data('attr', 9).append($('
')) + break + } + $gameBoard.append($tile) + } +} + +function countDown () { + var $timer = $('
') + var $time = $('

') + var time = 3 + $time.text(time) + $timer.append($time) + $gameBoard.append($timer) + + var countdown = setInterval(function () { + time-- + $time.text(time) + if (time === 0) { + $timer.remove() + clearInterval(countdown) + } + }, 1000) +} + +function changeDirection (event) { + switch (event.key) { + case 'ArrowUp': return 'up' + case 'ArrowRight': return 'right' + case 'ArrowDown': return 'down' + case 'ArrowLeft': return 'left' + } +} + +function eatAndChangeScore (tile) { + score++ + $('.score').text(`Score: ${score}`) + tile.find('div:first').remove() // find first element (dots) and remove +} + +function pacTarget (direction) { + // using direction, target tile, then move pacman into target tile if its a valid tile + var $pacManTile = $('#pac-man').parent() + switch (direction) { + case 'left': return $pacManTile.prev() + case 'right': return $pacManTile.next() + case 'up': return $pacManTile.prevAll().eq(18) + case 'down': return $pacManTile.nextAll().eq(18) + } +} + +function movePac () { + var targetTile = pacTarget(direction) + // check if tile has dots + if ($(targetTile).has('.dots').length > 0) eatAndChangeScore($(targetTile)) + // check if pac-man can move into tile + if ($(targetTile).data('attr') !== 1 && $(targetTile).data('attr') !== 8) $(targetTile).append($('#pac-man')) +} + +function patrolTopLeft ($ghost) { + var $ghostTile = $ghost.parent() + var $ghostUp = $ghostTile.prevAll().eq(18) + var $ghostLeft = $ghostTile.prev() + var $ghostRight = $ghostTile.next() + var $ghostDown = $ghostTile.nextAll().eq(18) + + if ($ghostUp.data('attr') !== 1 && prevDirOne[0] !== 'down' && prevDirOne[1] !== 'down') { + prevDirOne.push('up') + $ghostUp.append($ghost) + } else if ($ghostLeft.data('attr') !== 1 && prevDirOne[0] !== 'right' && prevDirOne[1] !== 'right') { + prevDirOne.push('left') + $ghostLeft.append($ghost) + } else if ($ghostRight.data('attr') !== 1 && prevDirOne[0] !== 'left' && prevDirOne[1] !== 'left') { + $ghostRight.append($ghost) + prevDirOne.push('right') + } else if ($ghostDown.data('attr') !== 1 && prevDirOne[0] !== 'up' && prevDirOne[1] !== 'up') { + prevDirOne.push('down') + $ghostDown.append($ghost) + } + // shift entire array left by one element (keeps array 2 elements long) + if (prevDirOne.length > 2) prevDirOne.shift() +} + +function patrolTopRight ($ghost) { + var $ghostTile = $ghost.parent() + var $ghostUp = $ghostTile.prevAll().eq(18) + var $ghostLeft = $ghostTile.prev() + var $ghostRight = $ghostTile.next() + var $ghostDown = $ghostTile.nextAll().eq(18) + + if ($ghostUp.data('attr') !== 1 && prevDirTwo[0] !== 'down' && prevDirTwo[1] !== 'down') { + prevDirTwo.push('up') + $ghostUp.append($ghost) + } else if ($ghostRight.data('attr') !== 1 && prevDirTwo[0] !== 'left' && prevDirTwo[1] !== 'left') { + $ghostRight.append($ghost) + prevDirTwo.push('right') + } else if ($ghostLeft.data('attr') !== 1 && prevDirTwo[0] !== 'right' && prevDirTwo[1] !== 'right') { + prevDirTwo.push('left') + $ghostLeft.append($ghost) + } else if ($ghostDown.data('attr') !== 1 && prevDirTwo[0] !== 'up' && prevDirTwo[1] !== 'up') { + prevDirTwo.push('down') + $ghostDown.append($ghost) + } + // shift entire array left by one element (keeps array 2 elements long) + if (prevDirTwo.length > 2) prevDirTwo.shift() +} + +function patrolBottomRight ($ghost) { + var $ghostTile = $ghost.parent() + var $ghostUp = $ghostTile.prevAll().eq(18) + var $ghostLeft = $ghostTile.prev() + var $ghostRight = $ghostTile.next() + var $ghostDown = $ghostTile.nextAll().eq(18) + + if ($ghostDown.data('attr') !== 1 && prevDirThree[0] !== 'up' && prevDirThree[1] !== 'up') { + prevDirThree.push('down') + $ghostDown.append($ghost) + } else if ($ghostUp.data('attr') !== 1 && prevDirThree[0] !== 'down' && prevDirThree[1] !== 'down') { + prevDirThree.push('up') + $ghostUp.append($ghost) + } else if ($ghostRight.data('attr') !== 1 && prevDirThree[0] !== 'left' && prevDirThree[1] !== 'left') { + $ghostRight.append($ghost) + prevDirThree.push('right') + } else if ($ghostLeft.data('attr') !== 1 && prevDirThree[0] !== 'right' && prevDirThree[1] !== 'right') { + prevDirThree.push('left') + $ghostLeft.append($ghost) + } + // shift entire array left by one element (keeps array 2 elements long) + if (prevDirThree.length > 2) prevDirThree.shift() +} + +function patrolBottomLeft ($ghost) { + var $ghostTile = $ghost.parent() + var $ghostUp = $ghostTile.prevAll().eq(18) + var $ghostLeft = $ghostTile.prev() + var $ghostRight = $ghostTile.next() + var $ghostDown = $ghostTile.nextAll().eq(18) + + if ($ghostDown.data('attr') !== 1 && prevDirFour[0] !== 'up' && prevDirFour[1] !== 'up') { + prevDirFour.push('down') + $ghostDown.append($ghost) + } else if ($ghostUp.data('attr') !== 1 && prevDirFour[0] !== 'down' && prevDirFour[1] !== 'down') { + prevDirFour.push('up') + $ghostUp.append($ghost) + } else if ($ghostLeft.data('attr') !== 1 && prevDirFour[0] !== 'right' && prevDirFour[1] !== 'right') { + prevDirFour.push('left') + $ghostLeft.append($ghost) + } else if ($ghostRight.data('attr') !== 1 && prevDirFour[0] !== 'left' && prevDirFour[1] !== 'left') { + $ghostRight.append($ghost) + prevDirFour.push('right') + } + // shift entire array left by one element (keeps array 2 elements long) + if (prevDirFour.length > 2) prevDirFour.shift() +} + +function checkCollision () { + var $pacManTile = $('#pac-man').parent() + var targetTile = pacTarget(direction) + // return true if pacman's parent tile or pacman's target tile contains any of the ghosts + switch (true) { + case ($pacManTile.has('#ghost-one, #ghost-two, #ghost-three, #ghost-four').length > 0): + case ($(targetTile).has('#ghost-one, #ghost-two, #ghost-three, #ghost-four').length > 0): + return true + } +} + +function movePacToStart () { + // targeting pac-man starting tile + var $pacManStart = $('.tile').filter(function () { return $(this).data('attr') === 9 }) + // append pac-man to starting tile + $pacManStart.append($('#pac-man')) +} + +function updateLives () { + pacManLives-- + $('.lives-container').children().last().remove() +} + +function togglePanels (panel) { + if (panel === 'win') { + $scoreBoard.css('visibility', 'hidden') + $losePanel.hide() + $winPanel.show() + isGameOver = true + } else if (panel === 'loss') { + $scoreBoard.css('visibility', 'hidden') + $winPanel.hide() + $losePanel.show() + isGameOver = true + } else if (panel === 'starting') { + $instructionPanel.hide() + $scoreBoard.css('visibility', 'visible') + } else { + $winPanel.hide() + $losePanel.hide() + } +} diff --git a/index.html b/index.html index 5b002212..64539a59 100644 --- a/index.html +++ b/index.html @@ -2,10 +2,56 @@ - - + Basic Pac-Man + + + - +
+

Basic Pac-Man

+
+
+
+

Instructions

+

+ Navigate Pac-Man through a maze of Pac-Dots.
+ The goal of the game is to consume all the Pac-Dots.
+ If any of the ghosts touch Pac-Man, he loses a life.
+ when all lives have been lost, the game ends.
+ Change Directions
+ + Arrow-Left '◀' : Move 'Left'
+ Arrow-Right '▶' : Move 'Right'
+ Arrow-Up '▲' : Move 'Up'
+ Arrow-Down '▼' : Move 'Down' +
+

+

Hit < Enter > to begin

+
+
+

You Win!

+

Score: 0

+

Hit < Space > to play again

+
+
+

You Lose!

+

Score: 0

+

Hit < Space > to try again

+
+
+
+

Score: 0

+
+

Lives:

+
+
+
+
+
+
+
+ +