Add files via upload
This commit is contained in:
commit
9800dcfb8d
10
README.md
Normal file
10
README.md
Normal 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
267
src/flappy.js
Normal 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
BIN
src/imgs/bird0.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 91 KiB |
BIN
src/imgs/bird1.png
Normal file
BIN
src/imgs/bird1.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 91 KiB |
BIN
src/imgs/bird2.png
Normal file
BIN
src/imgs/bird2.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 91 KiB |
31
src/index.html
Normal file
31
src/index.html
Normal 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
149
src/style.css
Normal 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; }
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user