Problems using a sprite sheet

45 views Asked by At

I am making some code for school that is supposed to get a sprite sheet from an online source and run through them to make it look just run through the sprite animation. I'm not sure what I need to fix here because I pretty much just used the slip of paper that my teacher gave me to write all of the code.

let img = new Image();
img.src = 'https://opengameart.org/sites/default/files/Green-Cap-Character-16x18.png';
img.onload = function() {
  init();
};

let canvas = document.getElementById('c');
let ctx = canvas.getContext('2d');

const scale = 2;
const width = 16;
const height = 18;
const scaledWidth = scale * width;
const scaledHeight = scale * height;

function drawFrame(frameX, frameY, canvasX, canvasY) {
  ctx.drawImage(img, frameX * width, frameY * height, width, height, canvasX, canvasY, scaledWidth, scaledHeight);
}

const cycleLoop = [0, 1, 0, 2];
let currentLoopIndex = 0;
let frameCount = 0;

function step() {
  frameCount++;
  if (frameCount < 15) {
    window.requestAnimationFrame(step);
    return;
  }
  frameCount = 0;
  ctx.clearRect(0, 0, canvas.width, canvas.height);
  drawFrame(cycleLoop[currentLoopIndex], 0, 0, 0);
  currentLoopIndex++;
  if (currentLoopIndex >= cycleLoop.length) {
    currentLoopIndex = 0;
  }
  window.requestAnimationFrame(step);
}

function init() {
  window.requestAnimationFrame(step);
}
canvas {
  border: 1px solid black;
}
<canvas id="c" width="300" height="200"></canvas>

1

There are 1 answers

0
Mr. Polywhirl On

You can lookup the frame by index and then convert that index to a row and column index, as long as you know how many columns are in the sprite sheet.

In the example below, I render all 4 walking animations by specifying a sequence of indices for each animation. These indices are 0-11, working from top to bottom, left to right.

const loadImage = /* async */ (url) => new Promise((resolve, reject) => {
  const img = new Image();
  img.addEventListener('load', () => resolve(img));
  img.addEventListener('error', (err) => reject(err));
  img.src = url;
});

class SpriteSheet {
  constructor(location, rows, cols) {
    this.location = location;
    this.rows = rows;
    this.cols = cols;
    this.loaded = false;
    this.img = null;
  }
  /* async */ load() {
    const self = this;
    return loadImage(this.location).then(img => {
      self.img = img;
      self.loaded = true;
    });
  }
  subImage(index, scale) {
    const row = Math.floor(index / this.cols);
    const col = index % this.cols;
    const width = this.img.width / this.cols;
    const height = this.img.height / this.rows;
    const x = col * width;
    const y = row * height;
    return { x, y, width, height };
  }
}

const heroSprite = new SpriteSheet(
  'https://opengameart.org/sites/default/files/Green-Cap-Character-16x18.png', 4, 3);

heroSprite.load().then(init);

let canvas = document.getElementById('c');
let ctx = canvas.getContext('2d');

const scale = 2.0;
const width = 16;
const height = 18;
const scaledWidth = width * scale;
const scaledHeight = height * scale;

const drawFrame = (x, y, spritesheet, index) => {
  const { x: xOff, y: yOff, width, height } = spritesheet.subImage(index, scale);
  ctx.drawImage(spritesheet.img,
    xOff, yOff,
    width, height,
    x, y,
    width * scale, height * scale);
};

const cycleCount = 4;
const walkCycles = {
  down  : [0,  1, 0,  2],
  up    : [3,  4, 3,  5],
  left  : [6,  7, 6,  8],
  right : [9, 10, 9, 11]
};

let currentLoopIndex = 0;
let frameCount = 0;

const step = () => {
  frameCount++;
  if (frameCount < 15) {
    window.requestAnimationFrame(step);
    return;
  }
  frameCount = 0;
  ctx.clearRect(0, 0, canvas.width, canvas.height);
  Object.entries(walkCycles).forEach(([key, cycle], i) => {
    const x = i * scaledWidth;
    drawFrame(x, 0, heroSprite, cycle[currentLoopIndex], x, 0);
  });
  currentLoopIndex = (currentLoopIndex + 1) % cycleCount;
  window.requestAnimationFrame(step);
}

function init() {
  window.requestAnimationFrame(step);
}

Object.assign(canvas, {
  width: scaledWidth * Object.keys(walkCycles).length,
  height: scaledHeight
})
html, body {
  width: 100%;
  height: 100%;
  margin: 0;
  padding: 0;
}

body {
  display: flex;
  align-items: center;
  justify-content: center;
  background: #222;
}

canvas {
  background: #6AF
}
<canvas id="c" width="300" height="200"></canvas>