2 years ago

#22786

test-img

afterburn

How do I repeatedly interpolate linearly between two values within a requestAnimationFrame loop

I'm trying to implement multiplayer position interpolation for my canvas game but I'm having trouble using my linear interpolation (lerp) function. I experience slight jitter when using t = 0.1 so I'm looking for some alternative way to calculate t such that movement will appear smooth to the user - or a different solution altogether.

I render a canvas as follows (simplified):

  function lerp (a, b, t) {
    return a + (b - a) * t;
  }

  function tick() {
    // Process position update(s).
    const t = 0.1;
    position.x = lerp(position.x, target_position.x, t);
    position.y = lerp(position.y, target_position.y, t);

    // Camera follow by offsetting canvas transform.
    update_camera();

    window.requestAnimationFrame(tick);
  }

  function update_camera() {
    const position_delta_x = position.x - initial_position.x;
    const position_delta_y = position.y - initial_position.y;
    const offset_x = Math.round(position_delta_x);
    const offset_y = Math.round(position_delta_y);
    ctx.setTransform(1, 0, 0, 1, -offset_x, -offset_y);
  }

  tick();

I receive about 10 updates every second via a websocket that contains new position data:

function handle_position_update(new_position) {
  target_position.x = new_position.x;
  target_position.y = new_position.y;
}

I've noticed the jitter is coming from my camera follow logic but I'm sure as to why this is happening.

JSFIDDLE

// Shared code.
const INITIAL_POSITION = {
  x: 300,
  y: 80
};

// Server-side code.
const server_state = {
  x: INITIAL_POSITION.x,
  y: INITIAL_POSITION.y
};
const UPDATE_TICK_RATE = 10;
const PHYSICS_TICK_RATE = 60;

setInterval(() => {
  // Simulate server physics update.
  const w = input[87] ? 1 : 0;
  const a = input[65] ? 1 : 0;
  const s = input[83] ? 1 : 0;
  const d = input[68] ? 1 : 0;

  const vertical = w ? 1 : s ? -1 : 0;
  const horizontal = d ? 1 : a ? -1 : 0;

  server_state.x += horizontal * 5;
  server_state.y -= vertical * 5;
}, 1000 / PHYSICS_TICK_RATE)

setInterval(() => {
  // Simulate server sending updates.
  target_pos_x = server_state.x;
  target_pos_y = server_state.y;
}, 1000 / UPDATE_TICK_RATE);


// Client-side code.
let target_pos_x = INITIAL_POSITION.x,
  target_pos_y = INITIAL_POSITION.y;
let input = [];

window.addEventListener('keydown', e => input[e.keyCode] = true);
window.addEventListener('keyup', e => input[e.keyCode] = false);

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

const W = canvas.width = 600;
const H = canvas.height = 160;

const circle = {
  position: {
    x: INITIAL_POSITION.x,
    y: INITIAL_POSITION.y
  },
  tick: function() {
    let t = 0.1;
    this.position.x = lerp(this.position.x, target_pos_x, t);
    this.position.y = lerp(this.position.y, target_pos_y, t);

    ctx.beginPath();
    ctx.arc(this.position.x, this.position.y, 3, 0, 2 * Math.PI);
    ctx.fillStyle = 'white'
    ctx.fill();
  }
}

const reference_point = {
  position: {
    x: 240,
    y: 60
  },
  tick: function() {
    ctx.beginPath();
    ctx.arc(this.position.x, this.position.y, 3, 0, 2 * Math.PI);
    ctx.fillStyle = 'red'
    ctx.fill()
  }
}

function tick(now) {
  clear();
  circle.tick();
  reference_point.tick();
  camera_follow();
  window.requestAnimationFrame(tick);
}

tick();

function clear() {
  ctx.save();
  ctx.setTransform(1, 0, 0, 1, 0, 0);
  ctx.clearRect(0, 0, W, H);
  ctx.fillStyle = '#000';
  ctx.fillRect(0, 0, W, H);
  ctx.restore();
}

function camera_follow() {
  const position_delta_x = circle.position.x - INITIAL_POSITION.x;
  const position_delta_y = circle.position.y - INITIAL_POSITION.y;
  const offset_x = Math.round(position_delta_x);
  const offset_y = Math.round(position_delta_y);
  ctx.setTransform(1, 0, 0, 1, -offset_x, -offset_y);
}

function lerp(a, b, t) {
  return a + (b - a) * t;
}
<canvas></canvas>
<div>WASD to move</div>

javascript

canvas

multiplayer

requestanimationframe

linear-interpolation

javascript

canvas

multiplayer

requestanimationframe

linear-interpolation

0 Answers

Your Answer

Accepted video resources