Entonces, seguí un tutorial para hacer un juego. Obviamente, esa no es la mejor manera de aprender a codificar, así que comencé a modificarlo. Actualmente, el juego tiene enemigos que se mueven lentamente hacia el jugador, pero estos enemigos son círculos de colores y nada más. Me gustaría agregar una imagen que pueda poner en los enemigos, pero no tengo idea de cómo hacerlo. Aquí hay un código que te gustaría saber:
La clase enemiga (la actualización se llama cada cuadro):
class Enemy { constructor(x, y, radius, color, velocity) { this.x = x; this.y = y; this.radius = radius; this.color = color; this.velocity = velocity; } draw() { ctx.beginPath(); ctx.arc(this.x, this.y, this.radius, 0, Math.PI * 2, true); ctx.fillStyle = this.color; ctx.fill(); } update() { this.draw(); this.x = this.x + this.velocity.x; this.y = this.y + this.velocity.y; } }La función para crear enemigos:
function spawnEnemies() { setInterval(() => { const radius = Math.random() * (30 - 4) + 4; let x; let y; if (Math.random() < 0.5) { x = Math.random() < 0.5 ? 0 - radius : canvas.width + radius; y = Math.random() * canvas.height; } else { y = Math.random() < 0.5 ? 0 - radius : canvas.height + radius; x = Math.random() * canvas.width; } const color = `hsl(${Math.random() * 360}, 50%, 50%)`; const angle = Math.atan2(canvas.height / 2 - y, canvas.width / 2 - x); const velocity = { x: Math.cos(angle), y: Math.sin(angle) } enemies.push(new Enemy(x, y, radius, color, velocity)); }, 1000) }Este código se ejecuta en la función de animación:
enemies.forEach((enemy, index) => { enemy.update(); const dist = Math.hypot(player.x - enemy.x, player.y - enemy.y); if (isHacking) { if (dist - enemy.radius - player.radius < 11) { setTimeout(() => { for (let i = 0; i < enemy.radius * 2; i++) { particles.push(new Particle(enemy.x, enemy.y, Math.random() * 2, enemy.color, { x: (Math.random() - 0.5) * (Math.random() * 8), y: (Math.random() - 0.5) * (Math.random() * 8)} )); }}, 0) score += 25; scoreEl.innerHTML = score; setTimeout(() => { enemies.splice(index, 1); projectiles.splice(proIndex, 1); }, 0) } } else if (dist - enemy.radius - player.radius < 1) { cancelAnimationFrame(animationId); modal.style.display = 'flex'; modalScore.innerHTML = score; }Además, esta es la primera vez que publico en el desbordamiento de pila, así que si hay algo que debería haber hecho y no hice, o viceversa, ¡házmelo saber!
Hay muchas formas de hacer lo que quieras. Círculo con imagen.
Por lo que puedo adivinar de su pregunta y comentarios, desea un círculo dinámico que muestre una imagen cargada.
Crea un patrón usando la imagen.
const imgPat = ctx.createPattern(image, "no-repeat");Use el tamaño de la imagen para entrenar el radio máximo que puede tener sin salir de la imagen.
const minRadius = Math.min(image.width, image.height) / 2; Para dibujar el círculo en cualquier radio, necesitará usar la función setTransform de contexto 2D. La siguiente función hará eso
function drawImageCircle(imgPat, minRadius, x, y, radius) { // get scale of circle image const scale = radius / minRadius; // transform to put origin at top left of bounding rectangle scaling to fit image pattern ctx.setTransform(scale, 0, 0, scale, x - radius, y - radius); ctx.fillStyle = imgPat; ctx.beginPath(); ctx.arc(minRadius, minRadius, minRadius, 0, Math.PI * 2); ctx.fill(); // reset the default transform ctx.setTransform(1, 0, 0, 1, 0, 0); }Demo carga una imagen. Luego obtiene su tamaño, crea un patrón a partir de él y lo anima cambiando el radio y colocando un contorno de 4 píxeles a su alrededor.
const image = new Image; image.src = "https://i.stack.imgur.com/C7qq2.png?s=256&g=1"; image.addEventListener("load", imageReady); const ctx = canvas.getContext("2d"); var w = canvas.width, h = canvas.height, cw = w / 2, ch = h / 2; var imgPat, minRadius; function imageReady() { imgPat = ctx.createPattern(image, "no-repeat"); minRadius = Math.min(image.width, image.height) / 2; requestAnimationFrame(renderLoop); } function drawImageCircle(imgPat, minRadius, x, y, radius) { const scale = radius / minRadius; ctx.setTransform(scale, 0, 0, scale, x - radius, y - radius); ctx.strokeStyle = "#F00"; ctx.lineWidth = 8 / scale; ctx.fillStyle = imgPat; ctx.beginPath(); ctx.arc(minRadius, minRadius, minRadius, 0, Math.PI * 2); ctx.stroke(); ctx.fill(); ctx.setTransform(1, 0, 0, 1, 0, 0); } function renderLoop(time) { ctx.setTransform(1,0,0,1,0,0); ctx.clearRect(0, 0, w, h); var x = Math.cos(time / 500) * (cw - 80) + cw; var y = Math.sin(time / 600) * (ch - 80) + ch; var rad = Math.sin(time / 333) * 10 + 30; drawImageCircle(imgPat, minRadius, x, y, rad); requestAnimationFrame(renderLoop); } canvas { background: #888; border: 2px solid black; } <canvas id="canvas" width="256" height="256"></canvas>Si su caso de uso lo admite, sugiero usar un png con transparencia circular recortada previamente para mejorar el rendimiento y evitar tener que codificar esto.
Pero continuemos y respondamos tu pregunta del comentario . Sentí que esto era lo suficientemente diferente de la imagen del clip de Canvas con dos quadraticCurves para agregar una nueva respuesta, pero ese hilo muestra el enfoque general: use context.clip() después de una ruta con context.arc , luego termine con context.drawImage .
Como también está dibujando otras cosas, envuelva su clip con context.save() y context.restore() para evitar que su clip afecte todo lo que dibuje después.
He aquí un ejemplo mínimo:
const canvas = document.createElement("canvas"); canvas.height = canvas.width = 100; const {width: w, height: h} = canvas; document.body.appendChild(canvas); const ctx = canvas.getContext("2d"); const img = new Image(); img.onload = function () { ctx.fillRect(10, 0, 20, 20); // normal drawing before save() ctx.save(); ctx.beginPath(); ctx.arc(w / 2, h / 2, w / 2, 0, Math.PI * 2); ctx.clip(); ctx.drawImage(this, 0, 0); ctx.restore(); ctx.fillRect(0, 10, 20, 20); // back to normal drawing after restore() }; img.src = `http://placekitten.com/${w}/${h}`;Si tiene varias imágenes, sugiero usar promesas como se describe en Imágenes onload en una función .