Add procedural pixel-art sprite engine

This commit is contained in:
2026-09-24 10:08:00 +02:00
parent 8d95f4bda0
commit 9e88804695
+101
View File
@@ -0,0 +1,101 @@
/* ============================================================
DSP ARCADE — procedural pixel-art sprites
============================================================ */
(function () {
'use strict';
const cache = {};
function makeSprite(map, palette, scale = 1) {
const h = map.length;
const w = Math.max(...map.map(r => r.length));
const cv = document.createElement('canvas');
cv.width = w; cv.height = h;
const ctx = cv.getContext('2d');
for (let y = 0; y < h; y++) {
for (let x = 0; x < map[y].length; x++) {
const c = map[y][x];
if (c === '.' || c === ' ') continue;
ctx.fillStyle = palette[c] || '#fff';
ctx.fillRect(x, y, 1, 1);
}
}
if (scale > 1) {
const big = document.createElement('canvas');
big.width = w * scale; big.height = h * scale;
const bctx = big.getContext('2d');
bctx.imageSmoothingEnabled = false;
bctx.drawImage(cv, 0, 0, w * scale, h * scale);
return big;
}
return cv;
}
function sprite(map, palette, scale = 1, key) {
const k = key || JSON.stringify([map, palette, scale]);
if (!cache[k]) cache[k] = makeSprite(map, palette, scale);
return cache[k];
}
function draw(ctx, img, x, y) {
ctx.drawImage(img, Math.round(x - img.width / 2), Math.round(y - img.height / 2));
}
const SPRITES = {
ship: {
map: ['...W...','..WWW..','..WWW..','.WWWWWW','WWWWWWWW','W.W..W.W'],
palette: { W: '#e8f4ff' },
},
shipAlt: {
map: ['...C...','..CCC..','..CCC..','.CCCCCC','CCCCCCCC','C.C..C.C'],
palette: { C: '#7df9ff' },
},
invader1: {
map: ['..X...X..','...X.X...','..XXXXX..','.XX.X.XX.','XXXXXXXXX','X.XXXXX.X','X.X...X.X','...X.X...'],
palette: { X: '#7dff5e' },
},
invader2: {
map: ['...X.X...','..XXXXX..','.XXXXXXX.','XX.XXX.XX','XXXXXXXXX','..X...X..','.X.X.X.X.','X..X.X..X'],
palette: { X: '#ff7de9' },
},
invader3: {
map: ['..X...X..','.XXXXXXX.','XXXXXXXXX','XXX.X.XXX','XXXXXXXXX','.X.....X.','X..X.X..X','.X.....X.'],
palette: { X: '#ffe14d' },
},
ufo: {
map: ['...GGGG...','..GGGGGG..','.GGGGGGGG.','GGGRRRRGGG','GGGGGGGGGG','.G.G..G.G.'],
palette: { G: '#9d7dff', R: '#ff4d6d' },
},
heart: {
map: ['.XX.XX.','XXXXXXX','XXXXXXX','.XXXXX.','..XXX..','...X...'],
palette: { X: '#ff2d95' },
},
heartEmpty: {
map: ['.XX.XX.','X.....X','X.....X','.X...X.','..X.X..','...X...'],
palette: { X: '#5a2a5a' },
},
star: {
map: ['...X...','...X...','XXXXXXX','...X...','...X...'],
palette: { X: '#ffffff' },
},
pellet: {
map: ['XX','XX'],
palette: { X: '#ffe14d' },
},
bonusCell: {
map: ['.XXX.','X...X','X.X.X','X...X','.XXX.'],
palette: { X: '#ff9d2d' },
},
ghostCell: {
map: ['.XXX.','X...X','X.X.X','X...X','.XXX.'],
palette: { X: '#7df9ff' },
},
};
function get(name, scale = 1) {
const s = SPRITES[name];
return sprite(s.map, s.palette, scale, name + '_' + scale);
}
window.DSP_SPRITES = { get, sprite, draw, SPRITES };
})();