first commit

This commit is contained in:
henry-hiles 2021-11-26 12:38:45 -05:00
commit e5f9c2ea99
9 changed files with 259 additions and 0 deletions

30
bird.js Normal file
View file

@ -0,0 +1,30 @@
const birdElem = document.querySelector( "[data-bird]" )
const BIRD_SPEED = 0.5
const JUMP_DURATION = 150
let timeSinceLastJump = Number.POSITIVE_INFINITY
const setTop = ( top ) => birdElem.style.setProperty( "--bird-top", top )
const getTop = () => parseFloat( getComputedStyle( birdElem ).getPropertyValue( "--bird-top" ) )
const handleJump = ( event ) => {
if ( event.code === "Space" ) timeSinceLastJump = 0
}
export const setupBird = () => {
setTop( window.innerHeight / 2 )
document.removeEventListener( "keydown", handleJump )
document.addEventListener( "keydown", handleJump )
}
export const updateBird = ( delta ) => {
if ( timeSinceLastJump < JUMP_DURATION )
setTop( getTop() - BIRD_SPEED * delta )
else
setTop( getTop() + BIRD_SPEED * delta )
timeSinceLastJump += delta
}
export const getBirdRect = () => birdElem.getBoundingClientRect()

BIN
images/background.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 122 KiB

BIN
images/bird.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 48 KiB

BIN
images/bottomPipe.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 7 KiB

BIN
images/topPipe.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

21
index.html Normal file
View file

@ -0,0 +1,21 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="styles.css">
<script src="script.js" type="module"></script>
<title>Document</title>
</head>
<body>
<h1 class="title" data-title>
Press Any Key To Start
<small data-subtitle class="subtitle hide"></small>
</h1>
<img data-bird src="images/bird.png" class="bird"></img>
</body>
</html>

81
pipe.js Normal file
View file

@ -0,0 +1,81 @@
const HOLE_HEIGHT = 200
const PIPE_WIDTH = 138
const PIPE_INTERVAL = 1500
const PIPE_SPEED = 0.5
let pipes = []
let timeSinceLastPipe
let passedPipeCount
const createPipe = () => {
const pipeElem = document.createElement( "div" )
const topElem = createPipeSegment( "top" )
const bottomElem = createPipeSegment( "bottom" )
pipeElem.append( topElem )
pipeElem.append( bottomElem )
pipeElem.classList.add( "pipe" )
pipeElem.style.setProperty(
"--hole-top",
randomNumberBetween(
HOLE_HEIGHT * 1.5,
window.innerHeight - HOLE_HEIGHT * 0.5
)
)
const pipe = {
get left () {
return parseFloat(
getComputedStyle( pipeElem ).getPropertyValue( "--pipe-left" )
)
},
set left ( value ) {
pipeElem.style.setProperty( "--pipe-left", value )
},
remove: () => {
pipes = pipes.filter( p => p !== pipe )
pipeElem.remove()
},
rects: () => [
topElem.getBoundingClientRect(),
bottomElem.getBoundingClientRect(),
]
}
pipe.left = window.innerWidth
document.body.append( pipeElem )
pipes.push( pipe )
}
const createPipeSegment = ( position ) => {
const segment = document.createElement( "div" )
segment.classList.add( "segment", position )
return segment
}
const randomNumberBetween = ( min, max ) => Math.floor( Math.random() * ( max - min + 1 ) + min )
export const setupPipes = () => {
document.documentElement.style.setProperty( "--pipe-width", PIPE_WIDTH )
document.documentElement.style.setProperty( "--hole-height", HOLE_HEIGHT )
pipes.forEach( pipe => pipe.remove() )
timeSinceLastPipe = PIPE_INTERVAL
passedPipeCount = 0
}
export const updatePipes = ( delta ) => {
timeSinceLastPipe += delta
if ( timeSinceLastPipe > PIPE_INTERVAL ) {
timeSinceLastPipe -= PIPE_INTERVAL
createPipe()
}
pipes.forEach( pipe => {
if ( pipe.left + PIPE_WIDTH < 0 ) {
passedPipeCount++
return pipe.remove()
}
pipe.left = pipe.left - delta * PIPE_SPEED
} )
}
export const getPassedPipesCount = () => passedPipeCount
export const getPipeRects = () => pipes.flatMap( pipe => pipe.rects() )

60
script.js Normal file
View file

@ -0,0 +1,60 @@
import { updateBird, setupBird, getBirdRect } from "./bird.js"
import {
updatePipes,
setupPipes,
getPassedPipesCount,
getPipeRects,
} from "./pipe.js"
const title = document.querySelector( "[data-title]" )
const subtitle = document.querySelector( "[data-subtitle]" )
let lastTime
const updateLoop = ( time ) => {
if ( lastTime == null ) {
lastTime = time
window.requestAnimationFrame( updateLoop )
return
}
const delta = time - lastTime
updateBird( delta )
updatePipes( delta )
if ( checkLose() ) return handleLose()
lastTime = time
window.requestAnimationFrame( updateLoop )
}
const checkLose = () => {
const birdRect = getBirdRect()
const insidePipe = getPipeRects().some( rect => isCollision( birdRect, rect ) )
const outsideWorld = birdRect.top < 0 || birdRect.bottom > window.innerHeight
return outsideWorld || insidePipe
}
const isCollision = ( rect1, rect2 ) => {
return (
rect1.left < rect2.right &&
rect1.top < rect2.bottom &&
rect1.right > rect2.left &&
rect1.bottom > rect2.top
)
}
const handleStart = () => {
title.classList.add( "hide" )
setupBird()
setupPipes()
lastTime = null
window.requestAnimationFrame( updateLoop )
}
const handleLose = () => setTimeout( () => {
title.classList.remove( "hide" )
subtitle.classList.remove( "hide" )
console.log( !( !document.cookie ) )
const highscore = 0
subtitle.innerHTML = `Score: ${getPassedPipesCount()}<br />Highscore: ${highscore}`
document.addEventListener( "keypress", handleStart, { once: true } )
}, 100 )
document.addEventListener( "keypress", handleStart, { once: true } )

67
styles.css Normal file
View file

@ -0,0 +1,67 @@
*, *::after, *::before {
box-sizing: border-box;
}
body {
margin: 0;
background-image: url(images/background.png);
background-size: cover;
font-family: "Lato", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; overflow: hidden;
}
.title {
z-index: 10;
position: absolute;
display: flex;
text-align: center;
justify-content: center;
align-items: center;
inset: 0;
margin: 0;
flex-direction: column;
}
.subtitle {
margin-top: .5rem;
}
.hide {
display: none;
}
.bird {
--bird-top: -1000;
--bird-size: 60px;
position: absolute;
width: var(--bird-size);
left: var(--bird-size);
top: calc(var(--bird-top) * 1px);
border-radius: 50%;
}
.pipe {
position: absolute;
top: 0;
bottom: 0;
width: calc(var(--pipe-width) * 1px);
left: calc(var(--pipe-left) * 1px);
}
.pipe > .segment {
position: absolute;
width: 100%;
background-size: 100% 600px;
}
.pipe > .top {
top: 0;
bottom: calc(var(--hole-top) * 1px);
background-image: url(images/topPipe.png);
background-position: bottom;
}
.pipe > .bottom {
bottom: 0;
top: calc(100vh - (var(--hole-top) * 1px) + calc(var(--hole-height) * 1px));
background-image: url(images/bottomPipe.png);
}