Add files via upload

This commit is contained in:
Jessica Guarato 2022-12-10 02:03:46 +00:00 committed by GitHub
commit 9800dcfb8d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 457 additions and 0 deletions

10
README.md Normal file
View File

@ -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.
* <strong>Instructions:</strong>
* 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).

267
src/flappy.js Normal file
View File

@ -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);

BIN
src/imgs/bird0.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 91 KiB

BIN
src/imgs/bird1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 91 KiB

BIN
src/imgs/bird2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 91 KiB

31
src/index.html Normal file
View File

@ -0,0 +1,31 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Flappy Game</title>
<link rel="stylesheet" href="./style.css">
</head>
<body>
<div flappy-game></div>
<div class="instructions">
<p>
This is a replica of the original flappy bird game developed for the sole purpose of learning.
</p>
<p>
Click on the screen, or use your spacebar to get started.
Fly the bird as far as you can without hitting a pipe.
</p>
</div>
<footer>
<p>
Coded by <strong>jguarato</strong>
<a href="https://codepen.io/jguarato" target="_blank">Check out my other Pens</a>
<a href="https://github.com/jguarato" target="_blank">Follow me on GitHub</a>
</p>
</footer>
<script src="./flappy.js"></script>
</body>
</html>

149
src/style.css Normal file
View File

@ -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; }
}