Холст | Как создать след по оси Y из движущегося объекта по оси X

Сейчас я делаю игру в Canvas/Phaser и ищу решение следующей проблемы.

Мне нужно сделать след по оси Y из объекта, который движется только по оси X, чтобы он выглядел так, как будто он оставляет след пыли.

Однако везде, куда бы я ни посмотрел, люди создают след из прыгающих шариков или других движущихся объектов X/Y, это не то, что я хочу создать.

Я использую игровую инфраструктуру Phaser для разработки игры, если в этой среде есть решение, которое было бы превосходным, но если бы вы могли помочь мне с чисто холстовым решением / идеей, это было бы здорово!

Я надеюсь, что я выбрал правильные слова для своего объяснения, ниже я добавил картинку и небольшое видео, которое визуализирует желаемый конечный результат.

http://vrrsus.com/static/lastvoyage/web6.jpg

https://youtu.be/Fd7VOACEKig?t=22m32s

1 ответ

Решение

Я не знаю Phaser, но так как вы также просите простой холст пример, вот один для этого:

FIFO-буфер (массив)

Вы можете использовать буфер FIFO (первым пришел-первым обслужен) или буфер задержки. Сохраните значения x и / или y в зависимости от ваших потребностей в буфере. Когда буфер заполнен в соответствии с предопределенным максимальным значением, первое значение выбрасывается.

Теперь у вас есть значения хвостов, которые вы можете выводить в любое время.

демонстрация

Ниже мы храним только х. Для хвоста определяется градиент. Это даст наилучший / самый плавный результат, но вы также можете создать массив с предопределенным цветом, совпадающим с записями в fifo-буфере.

Просто имейте в виду, что в этом случае вам нужно визуализировать только твердые тела (без альфа), иначе будет виден переход между каждым отрезком линии.

И это в основном все, что нужно сделать. Просто приведите его в соответствие с вашим циклом рендеринга.

Советы по производительности:

  • Если вы двигаетесь в направлении Y, градиент должен следовать. Вместо того, чтобы каждый раз создавать новый градиент, используйте translate() для головы игрока. Это также переведет определение градиентной линии.
  • Использование типизированного массива может повысить производительность, если вам нужно много длинных хвостов. Они быстрее, чем массивы списков / узлов, но не поставляются со сдвигом, поэтому вместо них нужно использовать циклический указатель.
  • Для игры в видео, просто один раз отрендуйте хвост, затем используйте его для других голов.
  • Вместо использования тени для свечения, как в демонстрации, используйте изображение для головы с уже примененным свечением.

var ctx = document.querySelector("canvas").getContext("2d");
ctx.fillStyle = "#fff";

var fifo = [],           // fifo buffer to cycle older x values through
    max = 30,            // max positions stored in the buffer
    i = 0,               // just for demo, make x cycle on screen
    size = 8,            // draw size
    x = 300,             // x-pos from mousemove event
    y = 30,              // y-pos for player head
    
    // make gradient for the tail stroke:
    // Adjust range as needed
    gradient = ctx.createLinearGradient(0, 30, 0, 280);

// brightest color on top, transparent color at bottom.
gradient.addColorStop(0, "#ccd");
gradient.addColorStop(1, "rgba(200,200,240,0)");

// set up canvas
ctx.strokeStyle = gradient;
ctx.lineWidth = 10;
ctx.lineJoin = "round";
ctx.lineCap = "square";

// glow effect (because we can.. :) )
ctx.shadowColor = "rgba(255,255,255,0.5)";
ctx.shadowBlur = 20;

ctx.canvas.onmousemove = function(e) {
  var rect = this.getBoundingClientRect();
  x = e.clientX - rect.left;
};

// main loop -
(function loop() {

  // push new value(s) to fifo array (for demo, only x)
  fifo.push(x);
  
  // fifo buffer full? Throw out the first value
  if (fifo.length > max) fifo.shift();

  // render what we got;
  ctx.clearRect(0, 0, 600, 480);
  
  ctx.beginPath();
  ctx.moveTo(fifo[0], y + fifo.length * size);
  for(var t = 1; t < fifo.length; t++) {
    ctx.lineTo(fifo[t], y + (fifo.length - t) * size);
  }
  ctx.stroke();
  
  // draw main player head
  ctx.translate(x, y);
  ctx.rotate(0.25*Math.PI);
  ctx.fillRect(-size*0.5, -size*0.5, size*2, size*2);
  ctx.setTransform(1,0,0,1,0,0);
                 
  requestAnimationFrame(loop)
})();
canvas {background:#000}
<canvas width=600 height=360></canvas>

Другие вопросы по тегам