├── imgs ├── cactus.png ├── ground.png ├── dino-lose.png ├── dino-run-0.png ├── dino-run-1.png └── dino-stationary.png ├── README.md ├── updateCustomProperty.js ├── ground.js ├── index.html ├── styles.css ├── cactus.js ├── dino.js └── script.js /imgs/cactus.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/moadhamousti/Chrome-Dinosaur-Game/HEAD/imgs/cactus.png -------------------------------------------------------------------------------- /imgs/ground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/moadhamousti/Chrome-Dinosaur-Game/HEAD/imgs/ground.png -------------------------------------------------------------------------------- /imgs/dino-lose.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/moadhamousti/Chrome-Dinosaur-Game/HEAD/imgs/dino-lose.png -------------------------------------------------------------------------------- /imgs/dino-run-0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/moadhamousti/Chrome-Dinosaur-Game/HEAD/imgs/dino-run-0.png -------------------------------------------------------------------------------- /imgs/dino-run-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/moadhamousti/Chrome-Dinosaur-Game/HEAD/imgs/dino-run-1.png -------------------------------------------------------------------------------- /imgs/dino-stationary.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/moadhamousti/Chrome-Dinosaur-Game/HEAD/imgs/dino-stationary.png -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Chrome Dinosaur Game 🦖 🎮 : 2 | 3 | ### Screen Shoot 📷 : 4 | 5 | 6 | Dinosaur Game 7 | 8 | 9 | -------------------------------------------------------------------------------- /updateCustomProperty.js: -------------------------------------------------------------------------------- 1 | export function getCustomProperty(elem, prop) { 2 | return parseFloat(getComputedStyle(elem).getPropertyValue(prop)) || 0 3 | } 4 | 5 | export function setCustomProperty(elem, prop, value) { 6 | elem.style.setProperty(prop, value) 7 | } 8 | 9 | export function incrementCustomProperty(elem, prop, inc) { 10 | setCustomProperty(elem, prop, getCustomProperty(elem, prop) + inc) 11 | } 12 | -------------------------------------------------------------------------------- /ground.js: -------------------------------------------------------------------------------- 1 | import { 2 | getCustomProperty, 3 | incrementCustomProperty, 4 | setCustomProperty, 5 | } from "./updateCustomProperty.js" 6 | 7 | const SPEED = 0.05 8 | const groundElems = document.querySelectorAll("[data-ground]") 9 | 10 | export function setupGround() { 11 | setCustomProperty(groundElems[0], "--left", 0) 12 | setCustomProperty(groundElems[1], "--left", 300) 13 | } 14 | 15 | export function updateGround(delta, speedScale) { 16 | groundElems.forEach(ground => { 17 | incrementCustomProperty(ground, "--left", delta * speedScale * SPEED * -1) 18 | 19 | if (getCustomProperty(ground, "--left") <= -300) { 20 | incrementCustomProperty(ground, "--left", 600) 21 | } 22 | }) 23 | } 24 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Dinosaur Game 8 | 9 | 10 | 11 | 12 |
13 |
0000
14 |
Press Any Key To Start
15 | 16 | 17 | 18 |
19 | 20 | 21 | -------------------------------------------------------------------------------- /styles.css: -------------------------------------------------------------------------------- 1 | @import url('https://fonts.cdnfonts.com/css/joystix'); 2 | 3 | *, *::before, *::after { 4 | box-sizing: border-box; 5 | user-select: none; 6 | } 7 | 8 | body { 9 | margin: 0; 10 | display: flex; 11 | justify-content: center; 12 | align-items: center; 13 | min-height: 50vh; 14 | width: 150vh; 15 | font-family: 'Joystix', sans-serif; 16 | 17 | } 18 | 19 | .world { 20 | overflow: hidden; 21 | position: relative; 22 | } 23 | 24 | .score { 25 | position: absolute; 26 | font-size: 3vmin; 27 | right: 1vmin; 28 | top: 1vmin; 29 | } 30 | 31 | .start-screen { 32 | position: absolute; 33 | font-size: 5vmin; 34 | top: 50%; 35 | left: 50%; 36 | transform: translate(-50%, -50%); 37 | color: rgb(89, 89, 89); 38 | font-family: 'Joystix', sans-serif; 39 | font-size: 20px; 40 | } 41 | 42 | .hide { 43 | display: none; 44 | } 45 | 46 | .ground { 47 | --left: 0; 48 | position: absolute; 49 | width: 300%; 50 | bottom: 0; 51 | left: calc(var(--left) * 1%) 52 | } 53 | 54 | .dino { 55 | --bottom: 0; 56 | position: absolute; 57 | left: 1%; 58 | height: 30%; 59 | bottom: calc(var(--bottom) * 1%); 60 | } 61 | 62 | .cactus { 63 | position: absolute; 64 | left: calc(var(--left) * 1%); 65 | height: 30%; 66 | bottom: 0; 67 | } 68 | -------------------------------------------------------------------------------- /cactus.js: -------------------------------------------------------------------------------- 1 | import { 2 | setCustomProperty, 3 | incrementCustomProperty, 4 | getCustomProperty, 5 | } from "./updateCustomProperty.js" 6 | 7 | const SPEED = 0.05 8 | const CACTUS_INTERVAL_MIN = 500 9 | const CACTUS_INTERVAL_MAX = 2000 10 | const worldElem = document.querySelector("[data-world]") 11 | 12 | let nextCactusTime 13 | export function setupCactus() { 14 | nextCactusTime = CACTUS_INTERVAL_MIN 15 | document.querySelectorAll("[data-cactus]").forEach(cactus => { 16 | cactus.remove() 17 | }) 18 | } 19 | 20 | export function updateCactus(delta, speedScale) { 21 | document.querySelectorAll("[data-cactus]").forEach(cactus => { 22 | incrementCustomProperty(cactus, "--left", delta * speedScale * SPEED * -1) 23 | if (getCustomProperty(cactus, "--left") <= -100) { 24 | cactus.remove() 25 | } 26 | }) 27 | 28 | if (nextCactusTime <= 0) { 29 | createCactus() 30 | nextCactusTime = 31 | randomNumberBetween(CACTUS_INTERVAL_MIN, CACTUS_INTERVAL_MAX) / speedScale 32 | } 33 | nextCactusTime -= delta 34 | } 35 | 36 | export function getCactusRects() { 37 | return [...document.querySelectorAll("[data-cactus]")].map(cactus => { 38 | return cactus.getBoundingClientRect() 39 | }) 40 | } 41 | 42 | function createCactus() { 43 | const cactus = document.createElement("img") 44 | cactus.dataset.cactus = true 45 | cactus.src = "imgs/cactus.png" 46 | cactus.classList.add("cactus") 47 | setCustomProperty(cactus, "--left", 100) 48 | worldElem.append(cactus) 49 | } 50 | 51 | function randomNumberBetween(min, max) { 52 | return Math.floor(Math.random() * (max - min + 1) + min) 53 | } 54 | -------------------------------------------------------------------------------- /dino.js: -------------------------------------------------------------------------------- 1 | import { 2 | incrementCustomProperty, 3 | setCustomProperty, 4 | getCustomProperty, 5 | } from "./updateCustomProperty.js" 6 | 7 | const dinoElem = document.querySelector("[data-dino]") 8 | const JUMP_SPEED = 0.45 9 | const GRAVITY = 0.0015 10 | const DINO_FRAME_COUNT = 2 11 | const FRAME_TIME = 100 12 | 13 | let isJumping 14 | let dinoFrame 15 | let currentFrameTime 16 | let yVelocity 17 | export function setupDino() { 18 | isJumping = false 19 | dinoFrame = 0 20 | currentFrameTime = 0 21 | yVelocity = 0 22 | setCustomProperty(dinoElem, "--bottom", 0) 23 | document.removeEventListener("keydown", onJump) 24 | document.addEventListener("keydown", onJump) 25 | } 26 | 27 | export function updateDino(delta, speedScale) { 28 | handleRun(delta, speedScale) 29 | handleJump(delta) 30 | } 31 | 32 | export function getDinoRect() { 33 | return dinoElem.getBoundingClientRect() 34 | } 35 | 36 | export function setDinoLose() { 37 | dinoElem.src = "imgs/dino-lose.png" 38 | } 39 | 40 | function handleRun(delta, speedScale) { 41 | if (isJumping) { 42 | dinoElem.src = `imgs/dino-stationary.png` 43 | return 44 | } 45 | 46 | if (currentFrameTime >= FRAME_TIME) { 47 | dinoFrame = (dinoFrame + 1) % DINO_FRAME_COUNT 48 | dinoElem.src = `imgs/dino-run-${dinoFrame}.png` 49 | currentFrameTime -= FRAME_TIME 50 | } 51 | currentFrameTime += delta * speedScale 52 | } 53 | 54 | function handleJump(delta) { 55 | if (!isJumping) return 56 | 57 | incrementCustomProperty(dinoElem, "--bottom", yVelocity * delta) 58 | 59 | if (getCustomProperty(dinoElem, "--bottom") <= 0) { 60 | setCustomProperty(dinoElem, "--bottom", 0) 61 | isJumping = false 62 | } 63 | 64 | yVelocity -= GRAVITY * delta 65 | } 66 | 67 | function onJump(e) { 68 | if (e.code !== "Space" || isJumping) return 69 | 70 | yVelocity = JUMP_SPEED 71 | isJumping = true 72 | } 73 | -------------------------------------------------------------------------------- /script.js: -------------------------------------------------------------------------------- 1 | import { updateGround, setupGround } from "./ground.js" 2 | import { updateDino, setupDino, getDinoRect, setDinoLose } from "./dino.js" 3 | import { updateCactus, setupCactus, getCactusRects } from "./cactus.js" 4 | 5 | const WORLD_WIDTH = 100 6 | const WORLD_HEIGHT = 30 7 | const SPEED_SCALE_INCREASE = 0.00001 8 | 9 | const worldElem = document.querySelector("[data-world]") 10 | const scoreElem = document.querySelector("[data-score]") 11 | const startScreenElem = document.querySelector("[data-start-screen]") 12 | 13 | setPixelToWorldScale() 14 | window.addEventListener("resize", setPixelToWorldScale) 15 | document.addEventListener("keydown", handleStart, { once: true }) 16 | 17 | let lastTime 18 | let speedScale 19 | let score 20 | function update(time) { 21 | if (lastTime == null) { 22 | lastTime = time 23 | window.requestAnimationFrame(update) 24 | return 25 | } 26 | const delta = time - lastTime 27 | 28 | updateGround(delta, speedScale) 29 | updateDino(delta, speedScale) 30 | updateCactus(delta, speedScale) 31 | updateSpeedScale(delta) 32 | updateScore(delta) 33 | if (checkLose()) return handleLose() 34 | 35 | lastTime = time 36 | window.requestAnimationFrame(update) 37 | } 38 | 39 | function checkLose() { 40 | const dinoRect = getDinoRect() 41 | return getCactusRects().some(rect => isCollision(rect, dinoRect)) 42 | } 43 | 44 | function isCollision(rect1, rect2) { 45 | return ( 46 | rect1.left < rect2.right && 47 | rect1.top < rect2.bottom && 48 | rect1.right > rect2.left && 49 | rect1.bottom > rect2.top 50 | ) 51 | } 52 | 53 | function updateSpeedScale(delta) { 54 | speedScale += delta * SPEED_SCALE_INCREASE 55 | } 56 | 57 | function updateScore(delta) { 58 | score += delta * 0.01 59 | scoreElem.textContent = Math.floor(score) 60 | } 61 | 62 | function handleStart() { 63 | lastTime = null 64 | speedScale = 1 65 | score = 0 66 | setupGround() 67 | setupDino() 68 | setupCactus() 69 | startScreenElem.classList.add("hide") 70 | window.requestAnimationFrame(update) 71 | } 72 | 73 | function handleLose() { 74 | setDinoLose() 75 | setTimeout(() => { 76 | document.addEventListener("keydown", handleStart, { once: true }) 77 | startScreenElem.classList.remove("hide") 78 | }, 100) 79 | } 80 | 81 | function setPixelToWorldScale() { 82 | let worldToPixelScale 83 | if (window.innerWidth / window.innerHeight < WORLD_WIDTH / WORLD_HEIGHT) { 84 | worldToPixelScale = window.innerWidth / WORLD_WIDTH 85 | } else { 86 | worldToPixelScale = window.innerHeight / WORLD_HEIGHT 87 | } 88 | 89 | worldElem.style.width = `${WORLD_WIDTH * worldToPixelScale}px` 90 | worldElem.style.height = `${WORLD_HEIGHT * worldToPixelScale}px` 91 | } 92 | --------------------------------------------------------------------------------