Skip to content

Instantly share code, notes, and snippets.

@lewdev
Last active July 13, 2024 01:52
Show Gist options
  • Save lewdev/96d53f42545f34836edfe4f2d0676285 to your computer and use it in GitHub Desktop.
Save lewdev/96d53f42545f34836edfe4f2d0676285 to your computer and use it in GitHub Desktop.
🕹️ Stacking Game #JavaScript #Canvas

🏗️ Stacking Game

Date: June 29, 2024

🕹️ Play Now

Whenever I see a simple game, I start thinking about its mechanics and how it might be implemented.

So then when it seemed like a good small weekend project, I went for it. I got it down to its bare mechanics.

In future iterations, I could add animations, backgrounds, title screen, scoreboard, etc.

🕹️ My top score: 23

Notes on the code & gameplay

  • I minified @KilledByAPixel's Dwitter Shim as a basis for my code. It provides a great animation loop for canvas and single-letter functions used in Dwitter.
  • Any key press, click, or touch will stack your bar or restart the game when you see "Game Over"
  • New bars appear randomly on the left or right.
  • Bars will move across the screen and back infinitely.

See more of my apps here

<style>html,body{background:#222;display:flex;justify-content:center;align-items:center;height:100%;margin:0}#c{background:white;max-height:100%;max-width:100%}</style><canvas id=c width=1080 height=1080></canvas><script>x=c.getContext`2d`,lfr=1,S=Math.sin,C=Math.cos,T=Math.tan,R=(r,g,b,a=1)=>`rgba(${r|0},${g|0},${b|0},${a})`,f=nfm=0;loop=fTime=>{requestAnimationFrame(loop);if(lfr&&fTime<nfm-2)return;nfm=Math.max(nfm+1e3/60,fTime);t=f/60;if(t*60|0!=f++)t+=1e-6;render(t)};onload=loop
W = 150
H = 600
G = 7
J = -55
passed=score=d=u=V=X=Y=0
onkeydown=e=>e.key==" "?d=J:0
render=t=>{
c.width|=0
u=u-t
x.font="2in'"
d=d+G>G?G:d+G;
Y+=d
// update walls
if (X > c.width) {
X = -W
passed = 0
V = Math.random() * 200 - 350
}
if (X > c.width / 2 && !passed) {
passed = 1;
score++;
}
X = X + 9
x.fillText('Score: ' + score,200,150)
x.fillText('🐦',500,Y)
for(i=2;i--;)x.fillRect(X, (i ? 0 : H + 400) + V, W, H);
};</script>
<!doctype html>
<head>
<title>Stacking Game</title>
<link rel="icon" href="data:image/svg+xml,<svg xmlns=%22http://www.w3.org/2000/svg%22 viewBox=%220 0 100 100%22><text x=%22-.4rem%22 y=%221em%22 font-size=%2280%22>🏗️</text></svg>">
<style>html,body{background:#222;display:flex;justify-content:center;align-items:center;height:100%;margin:0}#c{background:white;max-height:100%;max-width:100%}</style>
</head>
<body>
<div style="position:fixed;top:0;right:0"><a href="https://gist.github.com/lewdev/96d53f42545f34836edfe4f2d0676285" target="_blank" title="Source code">👨‍💻</a></div>
<canvas id=c width=1080 height=1080></canvas><script>x=c.getContext`2d`,lfr=1,S=Math.sin,C=Math.cos,T=Math.tan,R=(r,g,b,a=1)=>`rgba(${r|0},${g|0},${b|0},${a})`,t=f=nfm=0;
const START_W = 700;
const STACK_HEIGHT = 100;
const SPEED = 1000;
const TOPPER_COLOR = "#00A";
const STACK_COLOR = "#A00";
const STORE_ID = "stacking-game-top-score";
let prev = diff = gameover = 0;
let goRight = true;
let stack = [];
let topper = {};
let topScore = 0;
const init = () => {
topScore = localStorage.getItem(STORE_ID) || 0;
gameover = false;
stack = [{
x: (c.width - START_W ) / 2,
y: c.height - STACK_HEIGHT,
w: START_W,
c: STACK_COLOR,
}];
topper = {
x: -stack[0].w,
y: c.height - (STACK_HEIGHT * 2),
w: START_W,
c: TOPPER_COLOR,
};
};
const drawBar = (s, y, h = STACK_HEIGHT) => {
x.fillStyle=s.c;
x.fillRect(s.x, y || s.y, s.w, h);
};
const drawStack = (s, y = 0, showFullStack) => {
let height = STACK_HEIGHT;
if (showFullStack) {
y = STACK_HEIGHT * 3;
if (showFullStack > 8) height = 8 * STACK_HEIGHT / showFullStack;
}
let yPos;
for (let i = 0; i < s.length; i++) {
yPos = y + (height * (i + 1));
drawBar(s[i], yPos, height);
}
};
dropBar = () => {
if (gameover) {
init();
return;
}
const top = stack[0];
// if cutoff left or equals
if (topper.x <= top.x && topper.x + topper.w >= top.x) {
topper.w = top.w - (top.x - topper.x);
stack.unshift({...topper, x: top.x, c: STACK_COLOR});
if (stack.length <= 5) topper.y -= STACK_HEIGHT;
}
// if cutoff right
else if (topper.x > top.x && topper.x < top.x + top.w) {
topper.w = (top.x + top.w) - topper.x;
stack.unshift({...topper, c: STACK_COLOR});
if (stack.length <= 5) topper.y -= STACK_HEIGHT;
}
else gameover = true;
if (stack.length - 1 > topScore) {
topScore = stack.length - 1;
localStorage.setItem(STORE_ID, topScore);
}
topper.x = Math.random() > .5 ? c.width : -topper.w;
};
ontouchstart = dropBar;
onkeydown = e => e.key === " " && dropBar();
init();
z=t=>{// loop start ---------------------------
//draw
c.width|=0
drawStack(stack, topper.y, gameover && stack.length);
drawBar(topper);
x.font = "2in'"
x.textAlign = "center";
if (gameover) x.fillText("Game Over", c.width / 2, 300);
x.font = "1in'"
x.fillText("Score: " + (stack.length - 1) + ", Best: " + topScore, c.width / 2, 100);
//update
diff = t - prev;
if (!gameover) {
if (topper.x > c.width) goRight = false;
else if (topper.x < -topper.w) goRight = true;
topper.x += (goRight ? 1 : -1) * SPEED * diff;
}
prev = t;
};loop=fTime=>{requestAnimationFrame(loop);if(lfr&&fTime<nfm-2)return;nfm=Math.max(nfm+1e3/60,fTime);t=f/60;if(t*60|0!=f++)t+=1e-6;z(t)};loop()</script>
<script async src="https://www.googletagmanager.com/gtag/js?id=G-GNP8ER8985"></script>
<script>window.dataLayer=window.dataLayer||[];function gtag(){dataLayer.push(arguments);}gtag('js', new Date());gtag('config', 'G-GNP8ER8985');</script>
</body>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment