commit 9800dcfb8d348a32b95d08e826e27e616f902415 Author: Jessica Guarato Date: Sat Dec 10 02:03:46 2022 +0000 Add files via upload diff --git a/README.md b/README.md new file mode 100644 index 0000000..6cb23f4 --- /dev/null +++ b/README.md @@ -0,0 +1,10 @@ +## 🐦 Flappy game + +* A game developed with JavaScript and CSS; +* This is a replica of the original flappy bird game developed for the sole purpose of learning. +* Instructions: + * Click on the screen, or use your spacebar to get started. + * Fly the bird as far as you can without hitting a pipe. + * As the score increases, the difficulty also increases. +* There is also a version in my CodePen page: [https://codepen.io/jguarato/full/mdKoaYR](https://codepen.io/jguarato/full/mdKoaYR). +​ diff --git a/src/flappy.js b/src/flappy.js new file mode 100644 index 0000000..07d7593 --- /dev/null +++ b/src/flappy.js @@ -0,0 +1,267 @@ +function newElement(tagName, className) { + const element = document.createElement(tagName); + element.className = className; + + return element; +} + +function ScoreBoard() { + + this.HTMLElement = newElement('div', 'score'); + + this.updateScore = score => this.HTMLElement.innerHTML = score; + + this.updateScore(0); + +} + +function Bird() { + + let isFlying = false; + + this.HTMLElement = newElement('img', 'bird'); + + this.getSource = () => parseInt(this.HTMLElement.src.split('bird')[1].split('.png')[0]); + this.setSource = number => this.HTMLElement.src = `./imgs/bird${number}.png`; + + this.getAltitude = () => parseFloat(this.HTMLElement.style.bottom.split('%')[0]); + this.setAltitude = altitude => this.HTMLElement.style.bottom = `${altitude}%`; + + const flying = () => isFlying = true; + const falling = () => isFlying = false; + + this.resetEvents = () => { + + window.onmousedown = () => flying(); + window.onmouseup = () => falling(); + + window.onkeydown = () => flying(); + window.onkeyup = () => falling(); + + window.ontouchstart = () => flying(); + window.ontouchend = () => falling(); + } + + this.fly = () => { + + let y = this.getAltitude(); + let src = this.getSource(); + + y += isFlying ? 1 : -0.6; + src = isFlying ? + (src === 0 ? 2 : 0) : 1; + + this.setAltitude(y); + this.setSource(src); + } + + this.resetEvents(); + this.setSource(1); + this.setAltitude(50); + +} + +function Barrier(reverse = false) { + + this.HTMLElement = newElement('div', 'barrier'); + + const pipe = newElement('div', 'pipe'); + this.HTMLElement.appendChild(pipe); + + const border = newElement('div', 'border'); + this.HTMLElement.appendChild(border); + + this.HTMLElement.style.flexDirection = reverse ? 'column-reverse' : 'column'; + + this.setHeight = height => this.HTMLElement.style.height = `${height}%`; + +} + +function BarrierPair(initPosition) { + + this.HTMLElement = newElement('div', 'barrier-pair'); + + this.crossedMiddle = false; + + this.top = new Barrier(); + this.HTMLElement.appendChild(this.top.HTMLElement); + + this.bottom = new Barrier(true); + this.HTMLElement.appendChild(this.bottom.HTMLElement); + + this.sortOpening = () => { + + const minOpening = 25; + const maxOpening = 40; + const minHeight = 10; + + const opening = Math.random() * (maxOpening - minOpening) + minOpening; + + const maxHeight = 100 - (2 * minHeight + opening); + const heightTop = Math.random() * (maxHeight - minHeight) + minHeight; + const heightBottom = 100 - opening - heightTop; + + this.top.setHeight(heightTop); + this.bottom.setHeight(heightBottom); + } + + this.getPosition = () => parseFloat(this.HTMLElement.style.right.split('%')[0]); + this.setPosition = position => this.HTMLElement.style.right = `${position}%`; + + this.move = displacement => this.setPosition(this.getPosition() + displacement); + + this.sortOpening(); + this.setPosition(initPosition); + +} + +function Obstacles(addScore) { + + this.displacement = 1; + + this.pairs = [ + new BarrierPair(-25), + new BarrierPair(-100) + ]; + + this.animation = () => { + this.pairs.forEach(pair => { + + const position = pair.getPosition(); + pair.move(this.displacement); + + if (position >= 100) { + pair.setPosition(-50); + pair.sortOpening(); + pair.crossedMiddle = false; + } + + const crossedMiddle = position >= 50 && + position < 50 + this.displacement; + + if (crossedMiddle && !pair.crossedMiddle) { + addScore(); + pair.crossedMiddle = true; + } + }); + }; + +} + +function checkOverlappingElements(elem1, elem2) { + + const e1 = elem1.getBoundingClientRect(); + const e2 = elem2.getBoundingClientRect(); + + const horizontal = e1.right >= e2.left + && e1.left <= e2.right; + + const vertical = e1.top <= e2.bottom + && e1.bottom >= e2.top; + + return horizontal && vertical; +} + +function checkCollision(bird, barriers) { + + let collisionHappened = false; + + barriers.forEach(pair => { + + if (!collisionHappened) { + + const birdElem = bird.HTMLElement; + const topBarrier = pair.top.HTMLElement; + const bottomBarrier = pair.bottom.HTMLElement; + + collisionHappened = checkOverlappingElements(birdElem, topBarrier) + || checkOverlappingElements(birdElem, bottomBarrier); + } + + }); + + return collisionHappened; +} + +function resetEventFunctions(clear = true, callBackFunc) { + + if (clear) { + window.onclick = () => {}; + window.onkeydown = () => {}; + window.ontouchstart = () => {}; + } else { + window.onclick = callBackFunc; + window.onkeydown = callBackFunc; + window.ontouchstart = callBackFunc; + } + +} + +function FlappyGame() { + + let loop; + let score = 0; + + const gameScene = document.querySelector('[flappy-game]'); + + const scoreBoard = new ScoreBoard(); + gameScene.appendChild(scoreBoard.HTMLElement); + + const bird = new Bird(); + gameScene.appendChild(bird.HTMLElement); + + const obstacles = new Obstacles(() => { + + scoreBoard.updateScore(++score); + if (score % 10 === 0 && score > 0) obstacles.displacement += 0.2; + + }); + + obstacles.pairs.forEach(pair => gameScene.appendChild(pair.HTMLElement)); + + this.start = () => { + + resetEventFunctions(); + bird.resetEvents(); + + loop = setInterval(() => { + + bird.fly(); + + obstacles.animation(); + + if (checkCollision(bird, obstacles.pairs)) this.finish(); + + }, 20); + + } + + this.startWithDelay = (miliseconds = 3000) => { + + setTimeout(() => this.start(), miliseconds); + } + + this.stop = () => clearInterval(loop); + + this.finish = () => { + + this.stop(); + + const userResponse = confirm('GAME OVER! \n\nRestart game?'); + + if (userResponse) { + window.location.reload(); + } else { + const message = () => { + alert('GAME OVER! \n\nReload page to restart (F5).'); + resetEventFunctions(); + } + resetEventFunctions(false, message); + } + } + +} + +const newGame = new FlappyGame(); +// newGame.startWithDelay(); +resetEventFunctions(false, newGame.start); \ No newline at end of file diff --git a/src/imgs/bird0.png b/src/imgs/bird0.png new file mode 100644 index 0000000..75d1fd7 Binary files /dev/null and b/src/imgs/bird0.png differ diff --git a/src/imgs/bird1.png b/src/imgs/bird1.png new file mode 100644 index 0000000..5d86c50 Binary files /dev/null and b/src/imgs/bird1.png differ diff --git a/src/imgs/bird2.png b/src/imgs/bird2.png new file mode 100644 index 0000000..97a82bb Binary files /dev/null and b/src/imgs/bird2.png differ diff --git a/src/index.html b/src/index.html new file mode 100644 index 0000000..e79a4e7 --- /dev/null +++ b/src/index.html @@ -0,0 +1,31 @@ + + + + + + Flappy Game + + + + +
+
+

+ This is a replica of the original flappy bird game developed for the sole purpose of learning. +

+

+ Click on the screen, or use your spacebar to get started. + Fly the bird as far as you can without hitting a pipe. +

+
+ + + + + \ No newline at end of file diff --git a/src/style.css b/src/style.css new file mode 100644 index 0000000..52edb9d --- /dev/null +++ b/src/style.css @@ -0,0 +1,149 @@ +@import url('https://fonts.googleapis.com/css2?family=Press+Start+2P&family=Poppins&display=swap'); + +* { + box-sizing: border-box; + user-select: none; +} + +html, body { + width: 100vw; + min-height: 100vh; + margin: 0; + padding: 0; +} + +body { + display: flex; + flex-direction: column; + justify-content:space-between; + align-items: center; + font-family: Poppins; + background-color: #232936; +} + +[flappy-game] { + position: relative; + display: flex; + justify-content: center; + overflow: hidden; + margin: 50px; + height: 640px; + width: 480px; + background-color: rgb(35, 177, 182); +} + +.score { + position: absolute; + margin: 20px; + z-index: 1; + font-size: 40px; + font-family: "Press Start 2P"; +} + +.bird { + position: absolute; + align-self: center; + z-index: 2; + width: 50px; +} + +.barrier-pair { + position: absolute; + display: flex; + flex-direction: column; + justify-content: space-between; + height: 100%; + width: 120px; +} + +.barrier { + display: flex; + align-items: center; +} + +.pipe { + height: calc(100% - 20px); + width: 85%; + background: linear-gradient(to right, #8bb747, #dcf582 20%, #528021 80%); + box-shadow: 0.5px 1px 10px 0px #272e1aad; +} + +.border { + height: 20px; + width: 100%; + background: linear-gradient(to right, #8bb747 ,#dcf582 20%, #528021 80%); + box-shadow: 0.5px 1px 10px 0px #272e1aad; +} + +.instructions { + position: fixed; + z-index: 3; + right: 0; + top: 40px; + width: 300px; + padding: 15px; + text-align: center; + background-color: #384155; +} + +.instructions p { + color: #d5dbe7; +} + +.instructions::before { + content: "?"; + font-size: 30px; + padding: 0 15px; + border-radius: 100%; + background-color: #d5dbe7; +} + +footer { + display: flex; + flex-direction: column; + justify-content: center; + height: 60px; + width: 100%; + padding: 0px 20px; + font-weight: 600; + color: #e7edf8; + text-align: center; + background-color: #384155; +} + +footer a:link { + color: #af8ae0; +} + +footer a:visited { + color: #eeff00; +} +footer a:hover { + color: #eb8919; +} +footer a:active { + color: #eb8919; +} + +footer strong { + color: #91d4d0; + text-decoration: none; + background: linear-gradient(to top, #eeff00 40%, #3a1866 100%); + -webkit-background-clip: text; + -webkit-text-fill-color: transparent; +} + +@keyframes fadeIn { + 0% { opacity: 0; } + 100% { opacity: 1; } +} + +@media (max-width: 1112px) { + .instructions { visibility: hidden; } + .instructions:hover { animation: 1s fadeIn; visibility: visible; } + .instructions::before { visibility: visible; cursor: pointer; } +} + +@media (max-width: 480px) { + body { overflow-x: hidden; } +} \ No newline at end of file