Search code examples
javascriptcanvashtml5-canvas

Move an image towards a point with maximum speed


I watched a video on swarm intelligence and wanted to make a small simulation of agents delivering resources to a base. The rules given by the video said they have to turn towards "a point". I tried to make it manually but that quickly became 50 lines of code that didn't work. So I searched the internet and this is what I found:

let x = Destination.X - agent.X;
let y = Destination.Y - agent.Y;
let angle = Math.atan2(y, x);
let mag = 7.07106781;
let velX = Math.cos(angle) * mag;
let velY = Math.sin(angle) * mag;
agent.xVel = velX;
agent.YVel = velY;

It didn't work and they just ended up moving around aimlessly so I tried to set the mag to 5 since that was the max speed I wanted. Which didn't work either.

SO is there any way to make a rectangle on a canvas move towards a point with a maximum speed?


Solution

  • I can't understand the final target of your question, so this is the code that makes rectangle move to point (cursor) at their max speed.

    const canvas = document.getElementById("canvas");
    const ctx = canvas.getContext("2d");
    const pr = window.devicePixelRatio;
    canvas.width = canvas.clientWidth * pr;
    canvas.height = canvas.clientHeight * pr;
    let lastTs = null;
    
    const objects = [
      {
        x: 10*pr, y: 10*pr, color: "#f7a", s: 5 * pr,
        speed: {x: 0, y: 0, max: 2000},
        accel: {x: 0, y: 0, max: 20000}
      },
      {
        x: 500*pr, y: 150*pr, color: "#7fa", s: 7 * pr,
        speed: {x: 0, y: 0, max: 1000},
        accel: {x: 0, y: 0, max: 10000}
      }
    ]
    
    const point = {x: 20, y: 20, active: false};
    
    canvas.addEventListener('mousemove', (e) => {
      point.x = e.offsetX * pr;
      point.y = e.offsetY * pr;
      point.active = true;
    });
    
    canvas.addEventListener('mouseout', (e) => {
      point.active = false;
    });
    
    const drawCursor = () => {
      if (point.active){
        ctx.fillStyle = "#fff";
        ctx.beginPath();
        ctx.arc(point.x, point.y, 3*pr, 0, Math.PI * 2);
        ctx.closePath();
        ctx.fill();
      }
    };
    
    const moveAndDrawObject = (o, dt) => {
      if (point.active){
        const phi = Math.atan2(point.y - o.y, point.x - o.x);
        o.accel.x = Math.cos(phi) * o.accel.max;
        o.accel.y = Math.sin(phi) * o.accel.max;
      } else {
        o.accel = {...o.accel, x: 0, y: 0};
      }
      o.speed.x += o.accel.x * dt;
      o.speed.y += o.accel.y * dt;
      const absSpeed = Math.hypot(o.speed.x, o.speed.y);
      if (absSpeed > o.speed.max){
        o.speed.x = o.speed.x * o.speed.max / absSpeed;
        o.speed.y = o.speed.y * o.speed.max / absSpeed;
      }
      o.x += o.speed.x * dt;
      o.y += o.speed.y * dt;
      ctx.fillStyle = o.color;
      ctx.fillRect(o.x - o.s, o.y - o.s, o.s * 2, o.s * 2);
    };
    
    const draw = () => {
      ctx.fillStyle = "#333";
      ctx.fillRect(0, 0, canvas.width, canvas.height);
      drawCursor();
      const now = Date.now() / 1000;
      const dt = (lastTs == null ? 0 : now - lastTs);
      lastTs = now;
      objects.forEach(o => moveAndDrawObject(o, dt));
      requestAnimationFrame(draw);
    };
    
    draw();
    #canvas {
      width: 700px;
      height: 190px;
      border: 1px #999 solid;
    }
    
    h3 { color: #999; margin-left: 5px; margin-top: -30px}
    <canvas id="canvas"></canvas>
    <h3>move mouse over canvas</h3>

    You can increase the accel.max of object to make it move more straight to point if you want. You also can remove mouse handlers to make point static.