Estoy trabajando en un juego de serpientes en JavaScript y el marco Phaser3 , y quiero dibujar una línea en forma de pastilla para usar en la serpiente. Así que quiero dibujar segmentos de línea redondeados llenos de un color (verde) y delineados con otro color (negro). No es una línea recta, sino varios segmentos de línea conectados. Todos están conectados en ángulo recto, es decir, 0, 90, 180 o 270 grados.
Vea la imagen simulada a continuación.
Aquí está mi código para dibujar una línea, hay una matriz con las ubicaciones de la cuadrícula de las posiciones de los segmentos de línea:
var TILE_SIZE = 32; var c = document.getElementById("myCanvas"); var ctx = c.getContext("2d"); // points of each snake segment snake_segs = [ [9, 11], [10, 11], [10, 12], [11, 12], [11, 13], [12, 13], [12, 12] ]; // set line color ctx.strokeStyle = "#FFFF00"; ctx.beginPath(); // draw all line segments for (var i = 0; i < snake_segs.length-1; i++) { var xpos1 = snake_segs[i][0] * TILE_SIZE; var ypos1 = snake_segs[i][1] * TILE_SIZE; var xpos2 = snake_segs[i+1][0] * TILE_SIZE; var ypos2 = snake_segs[i+1][1] * TILE_SIZE; ctx.moveTo(xpos1, ypos1); ctx.lineTo(xpos2, ypos2); } ctx.stroke();
¿Cómo convertir esto en una línea redondeada en forma de píldora?
¿Cuál es el mejor enfoque para hacer esto? Esto es más difícil de lo que inicialmente pensé. ¿Existe un algoritmo para dibujar un contorno en forma de píldora? ¿O hay una forma inteligente de usar rectángulos redondeados que se superponen? ¿O es mejor dibujar el contorno en el sentido de las agujas del reloj? Pero entonces, ¿cómo encuentras la forma de ese contorno?
Bueno, solo usando Phaser, usaría el objeto de graphics
Phaser, para una solución rápida y fácil . Es un poco engorroso, pero funciona.
Manifestación:
(para optimizar todo el dibujo [reducir uno for
- loop], uno podría agregar un objeto gráfico adicional, y así uno podría dibujar todo en un for
- loop, pero no quería hacer una demostración demasiado compleja)
document.body.style = 'margin:0;'; var config = { type: Phaser.AUTO, width: 536, height: 183, scene: { create, update }, banner: false }; let foregroundGraphics; let backgroundGraphics; let snake_segs = [ [1,1], [2,1], [3,1], [3,2], [3,3], [4,3], [5,3]]; let length_seg = 20; function create () { backgroundGraphics = this.add.graphics(); foregroundGraphics = this.add.graphics(); } function update(){ drawSnake(0x00a000, 0xffffff, 3); } function drawSnake(fgColor, bgColor, padding){ foregroundGraphics.clear(); backgroundGraphics.clear(); drawCorners(fgColor, bgColor, padding); drawSegments(fgColor, bgColor, padding); } function drawCorners(fgColor, bgColor, padding){ foregroundGraphics.lineStyle(length_seg/4, fgColor); backgroundGraphics.lineStyle((length_seg + padding * 2)/4, bgColor); snake_segs.forEach( point => { backgroundGraphics.strokeCircle(point[0] * length_seg, point[1] * length_seg, (length_seg + padding * 2)/8); foregroundGraphics.strokeCircle(point[0] * length_seg, point[1] * length_seg, length_seg/8); }); } function drawSegments(fgColor, bgColor, padding){ backgroundGraphics.beginPath(); foregroundGraphics.beginPath(); backgroundGraphics.lineStyle((length_seg + padding * 2)/2, bgColor); foregroundGraphics.lineStyle(length_seg /2, fgColor); for(let idx = 0; idx < snake_segs.length; idx++){ let point = snake_segs[idx]; if(idx==0){ backgroundGraphics.moveTo(point[0] * length_seg, point[1] * length_seg); foregroundGraphics.moveTo(point[0] * length_seg, point[1] * length_seg); }else { backgroundGraphics.lineTo(point[0] * length_seg, point[1] * length_seg); foregroundGraphics.lineTo(point[0] * length_seg, point[1] * length_seg); } } backgroundGraphics.strokePath(); foregroundGraphics.strokePath(); } new Phaser.Game(config);
<script src="https://cdn.jsdelivr.net/npm/phaser@3.55.2/dist/phaser.js"></script>
Descargo de responsabilidad: podría haber una mejor manera de resolver esto, pero esta fue la primera que se me ocurrió.
(Personalmente, intentaría adaptar este ejemplo a mis necesidades, ya que me gusta experimentar, y hasta ahora nunca tuve la oportunidad de usar las funciones de seguimiento y rutas en Phaser. Pero actualmente no estoy seguro, si estas funciones, podrían ser utilizado para resolver esta tarea)
Puede hacer esto muy fácilmente dibujando primero la serpiente usando su color de contorno con un grosor de línea de, por ejemplo, 18 píxeles y luego volviendo a dibujar la misma serpiente con su color de cuerpo y un grosor de línea más pequeño de, digamos, 15 píxeles.
Desafortunadamente, esto daría como resultado una serpiente de forma sólida con esquinas afiladas de 90°. Afortunadamente, CanvasRenderingContext2D tiene una propiedad adicional llamada lineCap
que controla el aspecto de los extremos de las líneas. Si configura esto en 'round'
, tiene su serpiente en forma de píldora.
Aquí hay un ejemplo:
var TILE_SIZE = 32; var c = document.getElementById("myCanvas"); var ctx = c.getContext("2d"); snake_segs = [ [9, 11], [10, 11], [10, 12], [11, 12], [11, 13], [12, 13], [12, 12] ]; ctx.fillStyle = "#00a000"; ctx.fillRect(0, 0, c.width, c.height) ctx.lineCap = "round"; function drawSnake(color, thickness) { ctx.beginPath(); ctx.lineWidth = thickness; ctx.strokeStyle = color; for (var i = 0; i < snake_segs.length - 1; i++) { var xpos1 = snake_segs[i][0] * TILE_SIZE; var ypos1 = snake_segs[i][1] * TILE_SIZE; var xpos2 = snake_segs[i + 1][0] * TILE_SIZE; var ypos2 = snake_segs[i + 1][1] * TILE_SIZE; ctx.moveTo(xpos1, ypos1); ctx.lineTo(xpos2, ypos2); } ctx.moveTo(xpos2, ypos2); ctx.closePath(); ctx.stroke(); } drawSnake("#ffff00", 18); drawSnake("#00c000", 15);
<canvas id="myCanvas" width="500" height="500"> </canvas>