Created
July 24, 2023 08:04
-
-
Save daveknights/0bc1295c0f6682e9eac1ad0712256298 to your computer and use it in GitHub Desktop.
A basic Javascript version of Space Invaders using divs as lcd blocks.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!DOCTYPE html> | |
<html lang="en"> | |
<head> | |
<meta charset="UTF-8"> | |
<meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
<title>Div Invaders</title> | |
<link rel="stylesheet" href="styles.css"> | |
</head> | |
<body> | |
<main class="console"> | |
<div class="screen"></div> | |
</main> | |
<script type="module" src="script.js"></script> | |
</body> | |
</html> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { startMessage, lifeBlocks, numbers, alienBlocks, playerBlocks, ufoBlocks, gameOverBlocks } from "./shapes.js"; | |
const screen = document.querySelector('.screen'); | |
const [width, height] = [120, 110]; | |
const blocks = width * height; | |
let playerPosition = blocks - (width/2) - ((width * 6) + 6); | |
let lcdLifeBlocks= []; | |
let scoreBlocks = []; | |
let ufoPosition = 1559; | |
let lcdUfoBlocks = []; | |
let lcdAlienBlocks = []; | |
let lcdPlayerBlocks = []; | |
let lcdGameOverBlocks = []; | |
let timePassed = 0; | |
let lastTime = 0; | |
let alienFireBlock, alienFiring, alienOffsets, alienPosition, alienPattern, allBlocks, changeDirection, countSinceDrop, | |
direction, dropped, gameOver, interval, landed, lifeCount, lifeLost, playerFireBlock, playerFiring, playing, score, ufo; | |
const lifeBlocksPosition = lifePos => lifeBlocks.map(block => block + lifePos); | |
const setLifeBLocks = () => { | |
let livesPosition = 245; | |
lcdLifeBlocks= []; | |
[...Array(lifeCount -1).keys()].forEach(() => { | |
lcdLifeBlocks = [...lcdLifeBlocks, ...lifeBlocksPosition(livesPosition)]; | |
livesPosition += 12; | |
}); | |
}; | |
const scoreBlocksPosition = (digit, scorePos) => digit.map(block => block + scorePos); | |
const setScoreBlocks = () => { | |
const digits = score.toString().split('').reverse(); | |
let scorePosition = 590; | |
scoreBlocks = []; | |
for (const digit of digits) { | |
scoreBlocks = [...scoreBlocks, ...scoreBlocksPosition(numbers[digit], scorePosition)]; | |
scorePosition -= 4; | |
} | |
}; | |
const ufoBlocksPosition = ufoPos => ufoBlocks.map(block => block + ufoPos); | |
const alienBlocksPosition = (pattern, alienPos) => pattern.map(block => block + alienPos); | |
const playerBlocksPosition = playerPos => playerBlocks.map(block => block + playerPos); | |
const resetPlayerFiring = () => { | |
playerFireBlock = null; | |
playerFiring = false; | |
}; | |
const resetAlienFiring = () => { | |
alienFireBlock = null; | |
alienFiring = false; | |
}; | |
const hasLostLife = () => { | |
lifeLost = true; | |
lifeCount -= 1; | |
ufo = false; | |
ufoPosition = 1559; | |
lcdUfoBlocks = []; | |
resetPlayerFiring(); | |
resetAlienFiring(); | |
if (lifeCount === 0) { | |
gameOver = true; | |
} | |
} | |
const aliensSetUp = () => { | |
alienOffsets = [0,1,2,3,4,60,61,62,63,64,120,121,122,123,124]; | |
alienPosition = 2654; | |
alienPattern = 0; | |
direction = 'right'; | |
interval = 250; | |
landed = false; | |
}; | |
const updateScreen = time => { | |
lcdPlayerBlocks = playerBlocksPosition(playerPosition); | |
if (lifeLost === true) { | |
playing = false; | |
lcdPlayerBlocks = []; | |
if (lifeCount > 0) { | |
setTimeout(() => { | |
landed && aliensSetUp(); | |
lifeLost = false; | |
playing = true; | |
setLifeBLocks(); | |
window.requestAnimationFrame(updateScreen); | |
}, 1000); | |
} | |
} | |
if (lcdUfoBlocks.includes(playerFireBlock)) { | |
score += 50; | |
ufo = false; | |
lcdUfoBlocks = []; | |
ufoPosition = 1559; | |
setScoreBlocks(); | |
resetPlayerFiring(); | |
} | |
if (lcdAlienBlocks.includes(playerFireBlock)) { | |
alienOffsets.splice(Math.ceil(lcdAlienBlocks.indexOf(playerFireBlock) / 46) - 1, 1); | |
score += 10; | |
switch (alienOffsets.length) { | |
case 5: | |
interval = 100 | |
break; | |
case 1: | |
interval = 50 | |
break; | |
case 0: | |
lcdAlienBlocks = []; | |
setTimeout(() => { | |
aliensSetUp(); | |
resetAlienFiring(); | |
window.requestAnimationFrame(updateScreen); | |
}, 1000); | |
break; | |
} | |
setScoreBlocks(); | |
resetPlayerFiring(); | |
} | |
(lcdPlayerBlocks.includes(alienFireBlock) && !lifeLost) && hasLostLife(); | |
playerFiring && (playerFireBlock -= 480); | |
playerFireBlock < 1 && resetPlayerFiring(); | |
alienFiring && (alienFireBlock += 480); | |
alienFireBlock > 13200 && resetAlienFiring(); | |
if (Math.floor(time / 1000) % 3 === 0) { | |
const randomAlien = Math.floor(Math.random() * (alienOffsets.length - 1)); | |
alienFireBlock = lcdAlienBlocks[randomAlien * 46] + 603; | |
alienFiring = true; | |
} | |
if (!ufo && time > 1000 && Math.floor(time / 1000) % 15 === 0) { | |
ufo = true; | |
} | |
if(ufo) { | |
lcdUfoBlocks = [...ufoBlocksPosition(ufoPosition)]; | |
ufoPosition += 1; | |
if (ufoPosition > 1664) { | |
ufo = false; | |
ufoPosition = 1559; | |
lcdUfoBlocks = []; | |
} | |
} | |
timePassed += time - lastTime; | |
if (timePassed > interval) { | |
lcdAlienBlocks = []; | |
for (const alien of alienOffsets) { | |
lcdAlienBlocks = [...lcdAlienBlocks, ...alienBlocksPosition(alienBlocks[alienPattern], alienPosition + (alien * 20))]; | |
} | |
direction === 'right' ? alienPosition++ : alienPosition--; | |
if (changeDirection && !dropped) { | |
alienPosition += (10 * width); | |
dropped = true; | |
countSinceDrop = 1; | |
direction === 'right' ? direction = 'left' : direction = 'right'; | |
} | |
countSinceDrop >= 1 && countSinceDrop++; | |
if (countSinceDrop === 10) { | |
dropped = false; | |
changeDirection = false; | |
} | |
timePassed = 0; | |
alienPattern === 0 ? alienPattern = 1 : alienPattern = 0; | |
} | |
if (gameOver) { | |
lcdGameOverBlocks = gameOverBlocks.map(block => block + 521); | |
lcdPlayerBlocks = []; | |
} | |
const allLcdBlocks = [...lcdLifeBlocks, ...scoreBlocks, ...lcdUfoBlocks, ...lcdAlienBlocks, alienFireBlock && alienFireBlock, playerFireBlock && playerFireBlock, ...lcdPlayerBlocks, ...lcdGameOverBlocks]; | |
for (const [index, block] of allBlocks.entries()) { | |
allLcdBlocks.includes(index) ? block.style.background = '#333' | |
: block.style.background = '#dce6d6'; | |
if (lcdAlienBlocks.includes(index)) { | |
(!dropped && (index % 120 === 114 || index % 120 === 6)) && (changeDirection = true); | |
if (!lifeLost && index > 13080) { | |
landed = true; | |
hasLostLife(); | |
} | |
} | |
} | |
gameOver && (playing = false); | |
lastTime = time; | |
playing && window.requestAnimationFrame(updateScreen); | |
}; | |
const drawScreen = () => { | |
const lcdStartMessage = startMessage.map(block => block + 3392); | |
for (let block = 1; block <= blocks; block++ ) { | |
const screenBlock = document.createElement('div'); | |
screenBlock.className = 'block'; | |
lcdStartMessage.includes(block) && (screenBlock.style.background = '#333'); | |
screen.append(screenBlock); | |
} | |
allBlocks = document.querySelectorAll('.block'); | |
}; | |
const init = () => { | |
document.addEventListener('keydown', e => { | |
switch (e.code) { | |
case 'ArrowLeft': | |
playerPosition > 12360 && (playerPosition -= 2); | |
break; | |
case 'ArrowRight': | |
playerPosition < 12470 && (playerPosition += 2); | |
break; | |
case 'Space': | |
if (playing) { | |
!playerFiring && (playerFireBlock = lcdPlayerBlocks[0]); | |
(!lifeLost && alienOffsets.length > 0) && (playerFiring = true); | |
} | |
break; | |
case 'KeyS': | |
if (!playing) { | |
gameOver = false; | |
playing = true; | |
lifeCount = 3; | |
lifeLost = false; | |
score = 0; | |
window.requestAnimationFrame(updateScreen); | |
setLifeBLocks(); | |
setScoreBlocks(); | |
aliensSetUp(); | |
} | |
} | |
}); | |
drawScreen(); | |
}; | |
window.addEventListener('load', init); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
export const startMessage = [ | |
1, 2, 3, 5, 6, 7, 9, 10, 11, 13, 14, 15, 17, 18, 19, 24, 25, 26, 31, 32, 33, 35, 36, 37, 42, 43, 44, 46, 50, 51, 52, 54, 56, | |
121, 123, 125, 127, 129, 133, 137, 144, 152, 155, 157, 162, 164, 166, 170, 172, 174, 176, | |
241,242,243, 245,246,247, 249,250, 253,254,255, 257,258,259, 264,265,266, 272, 275, 277, 282,283,284, 286, 290,291,292, 294,295,296, | |
361, 365,366, 369, 375, 379, 386, 392, 395, 397, 402, 406, 410, 412, 415, | |
481, 485, 487, 489,490,491, 493,494,495, 497,498,499, 504,505,506, 512, 515,516,517, 522, 526,527,528, 530, 532, 535, | |
1441,1442,1443,1444,1445, 1447,1448,1449, 1451, 1453, 1455,1456,1457, 1480, 1494, | |
1561, 1563, 1565, 1567, 1569, 1571, 1573, 1575, 1599,1600, 1614, 1615, | |
1681, 1683, 1685, 1687, 1689, 1691, 1693, 1695,1696, 1718,1719,1720, 1734,1735,1736, | |
1801, 1803, 1805, 1807, 1809, 1811, 1813, 1815, 1839,1840, 1854,1855, | |
1921, 1923, 1925, 1927,1928,1929, 1931,1932,1933, 1935,1936,1937, 1960, 1974, | |
2401,2402,2403, 2405, 2407,2408,2409, 2411,2412,2413, 2438,2439,2440,2441,2442,2443,2444,2445,2446,2447,2448,2449,2450,2451,2452,2453,2454,2455,2456, | |
2521, 2525, 2527, 2529, 2531, 2558, 2576, | |
2641,2642, 2645, 2647,2648,2649, 2651,2652, 2678, 2696, | |
2761, 2765, 2767,2768, 2771, 2798, 2816, | |
2881, 2885, 2887, 2889, 2891,2892,2893, 2918,2919,2920,2921,2922,2923,2924,2925,2926,2927,2928,2929,2930,2931,2932,2933,2934,2935,2936 | |
]; | |
export const alienBlocks = [[ | |
3, 9, | |
124, 128, | |
243,244,245,246,247,248,249, | |
362,363, 365,366,367, 369,370, | |
481,482,483,484,485,486,487,488,489,490,491, | |
601, 603,604,605,606,607,608,609, 611, | |
721, 723, 729, 731, | |
844,845, 847,848 ], | |
[ 3, 9, | |
124, 128, | |
241, 243,244,245,246,247,248,249, 251, | |
361,362,363, 365,366,367, 369,370,371, | |
481,482,483,484,485,486,487,488,489,490,491, | |
602,603,604,605,606,607,608,609,610, | |
723, 729, | |
842, 850 ] | |
]; | |
export const playerBlocks = [ | |
5, | |
124,125,126, | |
244,245,246, | |
362,363,364,365,366,367,368, | |
481,482,483,484,485,486,487,488,489, | |
601,602,603,604,605,606,607,608,609, | |
721,722,723,724,725,726,727,728,729 | |
]; | |
export const ufoBlocks = [ | |
6, 7, 8, 9, 10, 11, | |
124,125,126,127,128,129,130,131,132,133, | |
243,244,245,246,247,248,249,250,251,252,253,254, | |
362,363, 365,366, 368,369, 371,372, 374,375, | |
481,482,483,484,485,486,487,488,489,490,491,492,493,494,495,496, | |
603,604,605, 608,609, 612,613,614, | |
724, 733 | |
]; | |
export const lifeBlocks = playerBlocks; | |
export const gameOverBlocks = [ | |
1, 2, 3, 5, 6, 7, 9, 10, 11, 12, 13, 15, 16, 17, 22, 23, 24, 26, 28, 30, 31, 32, 34, 35, 36, | |
121, 125, 127, 129, 131, 133, 135, 142, 144, 146, 148, 150, 154, 156, | |
241, 243, 245,246,247, 249, 251, 253, 255,256, 262, 264, 266, 268, 270,271, 274,275,276, | |
361, 363, 365, 367, 369, 371, 373, 375, 382, 384, 386, 388, 390, 394,395, | |
481,482,483, 485, 487, 489, 491, 493, 495,496,497, 502,503,504, 506,507,508, 510,511,512, 514, 516 | |
]; | |
export const numbers = [ | |
[1, 2, 3, | |
121, 123, | |
241, 243, | |
361, 363, | |
481,482,483], | |
[ 2, | |
122, | |
242, | |
362, | |
482 ], | |
[1, 2, 3, | |
123, | |
241,242,243, | |
361, | |
481,482,483], | |
[1, 2, 3, | |
123, | |
242,243, | |
363, | |
481,482,483], | |
[1, 3, | |
121, 123, | |
241,242,243, | |
363, | |
483], | |
[1, 2, 3, | |
121, | |
241,242,243, | |
363, | |
481,482,483], | |
[1, 2, 3, | |
121, | |
241,242,243, | |
361, 363, | |
481,482,483], | |
[1, 2, 3, | |
123, | |
243, | |
363, | |
483], | |
[1, 2, 3, | |
121, 123, | |
241,242,243, | |
361, 363, | |
481,482,483], | |
[1, 2, 3, | |
121, 123, | |
241,242,243, | |
363, | |
483] | |
]; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
.console { | |
align-items: center; | |
background: #d3d3d3; | |
border-radius: 5px; | |
display: flex; | |
height: 560px; | |
justify-content: center; | |
margin-inline: auto; | |
width: 600px; | |
} | |
.screen { | |
display: flex; | |
border: solid 1px #333; | |
border-bottom: solid 1px white; | |
border-right: solid 1px white; | |
flex-wrap: wrap; | |
height: 440px; | |
width: 480px; | |
} | |
.block { | |
background: #dce6d6; | |
height: 4px; | |
width: 4px; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment