├── 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 |
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 |
--------------------------------------------------------------------------------