2
\$\begingroup\$

I am trying to replicate the wave gun from Super Metroid where the bullets fly along a sine wave path. The cool part is that you can shoot in 8 directions and the bullet still follows the sine as if the whole sine function is rotated. I've tried using a 2d transformation to allow shooting in every angle but I cant get it to work. Here my code so far (JSFiddle.net). If you uncomment the line "Axis.dir += .02;" in the fiddle to start the rotation you'll notice that the black box doesn't follow it's axis at all.

var canvas = document.getElementById("can"),
  ctx = canvas.getContext("2d"),
  width = canvas.width, height = canvas.height,
  Axis = { x: width / 2, y: height / 3, dir: 0, vx: 0, vy: 0, speed: 4 },
  Sine = { x: Axis.x, y: Axis.y, angle: 0 },
  loop = function() {
      var rotatedX, rotatedY;
      // Draw
      ctx.clearRect(0, 0, width, height);
      ctx.fillStyle = "#F00";
      ctx.fillRect(Axis.x, Axis.y, 5, 5);
      ctx.fillStyle = "#000";
      ctx.fillRect(Sine.x, Sine.y, 10, 10);
      // Apply speed
      Axis.vx = Math.cos(Axis.dir) * Axis.speed;
      Axis.vy = Math.sin(Axis.dir) * Axis.speed;
      Axis.x += Axis.vx;
      Axis.y += Axis.vy;
      Sine.x = Axis.x;
      Sine.y = Axis.y + (Math.sin(Sine.angle) * 30);
      // Apply rotation
      rotatedX = Sine.x * Math.cos(Axis.dir) - Sine.y * Math.sin(Axis.dir);
      rotatedY = Sine.x * Math.sin(Axis.dir) + Sine.y * Math.cos(Axis.dir);
      Sine.x = rotatedX;
      Sine.y = rotatedY;
      Sine.angle += .03;
      Axis.dir += .02;
      // Keep drawn stuff on the screen
      if (Axis.x > width && Axis.vx > 0) Axis.x = 0;
      if (Axis.x < 0 && Axis.vx < 0) Axis.x = width;
      if (Axis.y > height && Axis.vy > 0) Axis.y = 0;
      if (Axis.y < 0 && Axis.vy < 0) Axis.y = height;
      window.requestAnimationFrame(loop);
  }
loop();
\$\endgroup\$
3
  • \$\begingroup\$ How do you do it without the time? \$\endgroup\$
    – AturSams
    Feb 1, 2014 at 14:00
  • \$\begingroup\$ Here's the updated fiddle: jsfiddle.net/UbgT5/1 \$\endgroup\$
    – fnx
    Feb 3, 2014 at 18:55
  • \$\begingroup\$ This is very pretty. \$\endgroup\$
    – AturSams
    Feb 3, 2014 at 20:41

1 Answer 1

2
\$\begingroup\$

It is actually supposed to be simple. There is an invisible anchor bullet moving like a normal game bullet would in a straight line (implementation not included).

You have the angle alpha in which the anchor bullet is moving and its position px & py. You also have the amplitude (how far does the sine wave expands) and it's frequency (how fast does it go up and down).

You also need the time to determine where the actual bullet is now.

To compute the position of the sine wave bullet, use this:

beta = alpha + PI / 2.0.; // offset angle by 90 degrees, only run once : doesn't change
// Loop
current_distance = amplitude * sin(time * frequency); // Inside loop
bullet.x = px + cos(beta) * current_distance;
bullet.y = py + sin(beta) * current_distance;
// Done
\$\endgroup\$
1
  • 1
    \$\begingroup\$ Thanks, this helped me figure it out, didn't even need to use time. I guess I was just overthinking the math a bit :) \$\endgroup\$
    – fnx
    Feb 1, 2014 at 13:58

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .