How to make bullets follow a rotating sinewave pattern

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();

• How do you do it without the time? Feb 1, 2014 at 14:00
• Here's the updated fiddle: jsfiddle.net/UbgT5/1
– fnx
Feb 3, 2014 at 18:55
• This is very pretty. Feb 3, 2014 at 20:41

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

• Thanks, this helped me figure it out, didn't even need to use time. I guess I was just overthinking the math a bit :)
– fnx
Feb 1, 2014 at 13:58