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\$
  • \$\begingroup\$ How do you do it without the time? \$\endgroup\$ – wolfdawn Feb 1 '14 at 14:00
  • \$\begingroup\$ Here's the updated fiddle: jsfiddle.net/UbgT5/1 \$\endgroup\$ – fnx Feb 3 '14 at 18:55
  • \$\begingroup\$ This is very pretty. \$\endgroup\$ – wolfdawn Feb 3 '14 at 20:41
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
    \$\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 '14 at 13:58

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.