diff --git a/game.js b/game.js index d9bf300..8549cab 100644 --- a/game.js +++ b/game.js @@ -1,31 +1,28 @@ import * as lib from "./lib.js" -const playerSprite = await lib.texture.loadImage("assets/player.png"); -const bulletSprite = await lib.texture.loadImage("assets/bullet1.png"); -const enemySprite = await lib.texture.loadImage("assets/enemy1.png"); +const { loadImage } = lib.texture; -const explosionSprites = await Promise.all( - [1, 2, 3, 4] - .map((id) => lib.texture.loadImage(`assets/explosion${id}.png`)) - .map((texture) => texture.then(texture => { - texture.width = 64; - texture.height = 64; - texture.multiplyPixelColor(1, 1, 0) - return texture; - })), -); +const playerSprite = await loadImage("assets/player.png", 64, 64); +const bulletSprite = await loadImage("assets/bullet1.png", 24, 24); +const enemySprite = await loadImage("assets/enemy1.png", 48, 48); -playerSprite.width = 64; -playerSprite.height = 64; -playerSprite.multiplyPixelColor(1, 0.5, 0); +const explosionSprites = [ + await loadImage(`assets/explosion1.png`, 64, 64), + await loadImage(`assets/explosion2.png`, 64, 64), + await loadImage(`assets/explosion3.png`, 64, 64), + await loadImage(`assets/explosion4.png`, 64, 64), +]; -bulletSprite.width = 32; -bulletSprite.height = 32; +const enemyBullet = bulletSprite.copy(); + +playerSprite.adjustColor(1, 0.5, 0); bulletSprite.rotate(Math.PI); +enemySprite.adjustColor(0.2, 0.2, 1); +enemyBullet.adjustColor(0, 1, 0); -enemySprite.width = 64; -enemySprite.height = 64; -enemySprite.multiplyPixelColor(0.2, 0.2, 1) +for (const texture of explosionSprites) { + texture.adjustColor(1, 0.75, 0); +} const playerSpeed = 300; const bulletSpeed = 400; @@ -36,26 +33,33 @@ let bullets = []; const bulletCoolDown = 0.5; let bulletCooldownTimer = bulletCoolDown; -let enemies = [ - { x: 10, y: 0 }, - { x: 110, y: 0 }, - { x: 210, y: 0 }, - { x: 310, y: 0 }, - { x: 410, y: 0 }, - { x: 10, y: 100 }, - { x: 110, y: 100 }, - { x: 210, y: 100 }, - { x: 310, y: 100 }, - { x: 410, y: 100 }, -]; +let enemies = []; let explosions = []; const explosionDuration = 0.5; function loop() { + tick(); + render(); +} +function tick() { + bulletCooldownTimer += lib.frameDeltaT; + + if (enemies.length === 0) { + spawnEnemies(); + } + + updatePlayerMovement(); + updateBulletMovement(); + handleBulletCollisions(); + updateExplosions(); +} + +function updatePlayerMovement() { const goLeft = lib.isPressed("ArrowLeft"); const goRight = lib.isPressed("ArrowRight"); + if (goLeft && !goRight) { playerX -= playerSpeed * lib.frameDeltaT; if (playerX < 0) { @@ -67,12 +71,15 @@ function loop() { playerX = lib.canvas.width - playerSprite.width; } } +} +function updateBulletMovement() { for (const bullet of bullets) { - bullet.y -= bulletSpeed * lib.frameDeltaT + bullet.y -= bulletSpeed * lib.frameDeltaT; } - bulletCooldownTimer += lib.frameDeltaT; +} +function handleBulletCollisions() { let deadEnemies = [] let deadBullets = [] for (let enemyIdx = 0; enemyIdx < enemies.length; ++enemyIdx) { @@ -82,11 +89,11 @@ function loop() { for (let bulletIdx = 0; bulletIdx < bullets.length; ++bulletIdx) { const bullet = bullets[bulletIdx]; - if (bullet.x < enemy.x + 64 && bullet.x + 32 >= enemy.x - && bullet.y < enemy.y + 64 && bullet.y + 32 >= enemy.y) { + if (bullet.x < enemy.x + 48 && bullet.x + 24 >= enemy.x + && bullet.y < enemy.y + 48 && bullet.y + 24 >= enemy.y) { deadBullets.push(bulletIdx); deadEnemies.push(enemyIdx); - explosions.push({ x: bullet.x, y: bullet.y, time: 0 }); + explosions.push({ x: enemy.x + 24, y: enemy.y + 24, time: 0 }); } } @@ -97,7 +104,9 @@ function loop() { for (const i of deadBullets.toReversed()) { bullets.splice(i, 1) } +} +function updateExplosions() { let deadExplosions = [] for (let i = 0; i < explosions.length; ++i) { const explosion = explosions[i]; @@ -109,7 +118,17 @@ function loop() { for (const i of deadExplosions.toReversed()) { explosions.splice(i, 1) } +} +function spawnEnemies() { + for (let y = 0; y < 2; ++y) { + for (let x = 0; x < 5; ++x) { + enemies.push({ x: 10 + 100 * x, y: y * 80 }); + } + } +} + +function render() { const cx = lib.canvas; cx.clear("black"); @@ -134,7 +153,7 @@ function loop() { lib.onPress(" ", () => { if (bulletCooldownTimer < bulletCoolDown) return; - bullets.push({ x: playerX + 16, y: 300 }); + bullets.push({ x: playerX + 20, y: 300 }); bulletCooldownTimer = 0; }) diff --git a/lib/texture.js b/lib/texture.js index a7dd03c..830e34b 100644 --- a/lib/texture.js +++ b/lib/texture.js @@ -58,7 +58,7 @@ export class Texture { * @param {number} green * @param {number} blue */ - multiplyPixelColor(red, green, blue) { + adjustColor(red, green, blue) { const ctx = this.canvas.getContext("2d"); const data = ctx.getImageData(0, 0, this.canvas.width, this.canvas.height); for (let y = 0; y < data.height; ++y) { @@ -80,23 +80,41 @@ export class Texture { newCtx.save(); newCtx.translate(width / 2, height / 2) newCtx.rotate(angle) - newCtx.drawImage(this.canvas, -width / 2 , -height / 2, width, height); + newCtx.drawImage(this.canvas, -width / 2, -height / 2, width, height); newCtx.restore(); this.canvas = newCanvas; } + + copy() { + const { width, height } = this; + const newCanvas = new OffscreenCanvas(width, height); + const newCtx = newCanvas.getContext("2d"); + newCtx.imageSmoothingEnabled = false; + newCtx.drawImage(this.canvas, -width / 2, -height / 2, width, height); + return new Texture(newCanvas); + } } /** * * @param {string} path + * @param {number | undefined} width + * @param {number | undefined} height * @returns {Promise} */ -export function loadImage(path) { +export function loadImage(path, width, height) { const image = document.createElement("img"); image.src = path; return new Promise((resolve) => { image.onload = () => { - resolve(Texture.fromImage(image)); + const texture = Texture.fromImage(image); + if (width !== undefined) { + texture.width = width; + } + if (height !== undefined) { + texture.height = height; + } + resolve(texture); } }); }