user5191605
user5191605

Reputation:

Sinusoidal or other custom move type between two points

I am trying to build a function that moves bullets in mini game. For the moment i am doing it in very simple way.

Have function to calculate radian angle between two points:

this.rftv = (p1, p2) => Math.atan2(p2.y - p1.y, p2.x - p1.x);

Have to different points and calculate angle between them:

var x = objects[this.destination].x,
    y = objects[this.destination].y,
    rad = tools.rftv( { x: this.x, y: this.y }, { x: x, y: y } );

Define speed boost:

this.attack_speed = 2.5;

Move bullet using angle and speed boost:

this.x += Math.cos(rad) * this.attack_speed;
this.y += Math.sin(rad) * this.attack_speed;

What i am trying to do is to not move bullets in linear way, i am rather trying to move bullets using sinus wave, to achieve something like this: enter image description here

I have no idea how to start to build it, maybe someone could help and write a function that would take two points and make object move sinusoidal between them.

Upvotes: 1

Views: 619

Answers (1)

Patrick Roberts
Patrick Roberts

Reputation: 51946

I suggest you add a few more variables to each instance:

// initialization

this.distance = 0;
// have a max amplitude of about 30-50 depending on how you want it to look
this.amplitude = (Math.random() * 2 - 1) * MAX_AMPLITUDE;
// have a fixed period somewhere between 10-50 depending on how you want it to look
this.period = 30;
this.initial = { x: this.x, y: this.y };

// on each frame

this.distance += this.attack_speed;

this.x = this.initial.x + Math.cos(this.rad) * this.distance;
this.y = this.initial.y + Math.sin(this.rad) * this.distance;

const deviation = Math.sin(this.distance * Math.PI / this.period) * this.amplitude;

this.x += Math.sin(this.rad) * deviation;
this.y -= Math.cos(this.rad) * deviation;

Turns out I had a slight error with the math, it's corrected now, along with a very basic demo below.

A positive amplitude should cause the initial trajectory of the bullet to go at an angle slightly counter-clockwise compared to the angle from point A to point B, then oscillate back and forth on the way to B.

class Bullet {
  constructor({initial = {}, destination = {}, amplitude = 50, period = 30, speed = 2.5} = {}) {
    let { x: ix, y: iy } = this.initial = initial;
    let { x: dx, y: dy } = this.destination = destination;
    this.amplitude = (Math.random() * 2 - 1) * amplitude;
    this.period = period;
    this.speed = speed;
    
    this.distance = 0;

    this.x = ix;
    this.y = iy;
    
    this.rad = Math.atan2(dy - iy, dx - ix);
  }
  
  update() {
    this.distance += this.speed;
    
    this.x = this.initial.x + Math.cos(this.rad) * this.distance;
    this.y = this.initial.y + Math.sin(this.rad) * this.distance;
    
    const deviation = Math.sin(this.distance * Math.PI / this.period) * this.amplitude;
    
    this.x += Math.sin(this.rad) * deviation;
    this.y -= Math.cos(this.rad) * deviation;
  }
}

const canvas = document.querySelector('canvas');
const ctx = canvas.getContext('2d');

let initial = {
  x: canvas.width / 4,
  y: canvas.height * 3 / 4
};

let destination = {
  x: canvas.width * 3 / 4,
  y: canvas.height / 4
};

let bullet = new Bullet({initial, destination});

console.log(bullet.amplitude);

function draw() {
  requestAnimationFrame(draw);
  
  // ctx.clearRect(0, 0, canvas.width, canvas.height);
  
  bullet.update();
  
  ctx.fillStyle = '#0000FF';
  ctx.fillRect(bullet.x, bullet.y, 1, 1);
  
  ctx.fillStyle = '#FF0000';
  ctx.fillRect(canvas.width / 4, canvas.height * 3 / 4, 1, 1);
  ctx.fillStyle = '#00FF00';
  ctx.fillRect(canvas.width *3 / 4, canvas.height / 4, 1, 1);
}

draw();
<canvas width="500" height="200"></canvas>

Upvotes: 3

Related Questions