マガジン

スプライトアニメーションの作り方:画像1枚で動かす

遊びをつくる

執筆にClaudeを使用しています。

正方形のタイルで芽のキャラクターを組み、拡大鏡で輪郭を確かめる写真風イメージ。
記事のテーマをもとに制作したAI生成イラスト。

スプライトアニメーションとは、複数のコマ絵を1枚の画像にまとめた「スプライトシート」を使い、表示する領域をコマ単位でずらすことで動きを表現する技術です。キャラクターの歩行・攻撃・ジャンプといった動作を、画像ファイルを何枚も読み込まずに実現できます。 この方法が広く使われる理由は実用的なところにあります。ブラウザゲームでは画像ファイルの読み込み回数が多いほど通信コストがかかります。1枚にまとめることでHTTPリクエストを減らし、読み込み完了後のアニメーション開始を安定させることができます。また、Canvas APIやbackground-positionのどちらでも同じシートが使えるため、実装方法を選びやすいという利点もあります。 この記事では、スプライトシートの設計から、JavaScriptで動かすまでの手順を具体的に説明します。完成したコードをそのまま使えるよう、動かして確認できる最小構成を示します。

この記事の目次
  1. スプライトシートの設計:コマをどう並べるか
  2. Canvasで動かす:基本の実装
  3. 複数のアニメーション状態を切り替える
  4. よくある失敗と対処
  5. CSSの`background-position`方式との使い分け
  6. 次の行動
  7. 参考資料

スプライトシートの設計:コマをどう並べるか

スプライトシートを用意するとき、まずコマの並べ方を決める必要があります。実装のしやすさを優先するなら、横一列に並べる方式が最もシンプルです。コマ番号に幅を掛けるだけで切り出し位置が計算でき、コードの見通しが良くなります。

コマを作る際に決めておくべき数値は次の3つです。

  • コマの幅と高さ:全コマを同じサイズにする。例えば64×64ピクセル。
  • 総コマ数:歩行なら4〜8コマが一般的です。
  • 1コマあたりの表示時間(ミリ秒):100〜150msが自然な歩行に見えます。速い動作は60〜80ms程度が目安です。

画像を自作する場合、Aseprite・LibreSprite・Piskel(ブラウザで使える無料ツール)などがスプライトシートのエクスポートに対応しています。フリー素材を使う場合は、配布ページでコマのサイズと枚数を確認してから実装を始めてください。

Canvasで動かす:基本の実装

HTMLのCanvas要素に描画する方法を説明します。drawImageの9引数形式を使うと、シートの一部を切り出してキャンバスに描けます。

javascript
const canvas = document.getElementById('game');
const ctx = canvas.getContext('2d');

const sheet = new Image();
sheet.src = 'walk.png'; // スプライトシートのパス

const FRAME_W = 64;   // 1コマの幅
const FRAME_H = 64;   // 1コマの高さ
const TOTAL   = 6;    // 総コマ数
const FPS     = 8;    // 1秒に進むコマ数

let frame = 0;
let lastTime = 0;
const interval = 1000 / FPS;

function animate(timestamp) {
  if (timestamp - lastTime >= interval) {
    ctx.clearRect(0, 0, canvas.width, canvas.height);
    ctx.drawImage(
      sheet,
      frame * FRAME_W, 0,   // シート上の切り出し開始位置(x, y)
      FRAME_W, FRAME_H,     // 切り出すサイズ
      0, 0,                 // キャンバス上の描画位置
      FRAME_W, FRAME_H      // 描画サイズ
    );
    frame = (frame + 1) % TOTAL;
    lastTime = timestamp;
  }
  requestAnimationFrame(animate);
}

sheet.onload = () => requestAnimationFrame(animate);

requestAnimationFrameはブラウザの描画タイミングに合わせてループを呼び出す関数です。timestamp引数で経過時間を受け取り、intervalと比較することでフレームレートを一定に保ちます。setIntervalでも同じ動きは作れますが、タブが非アクティブのときに処理が止まるrequestAnimationFrameの方がバッテリー消費を抑えられます。

複数のアニメーション状態を切り替える

歩行・停止・ジャンプのように状態が複数ある場合、シートの縦方向にアニメーション種別を並べる方式が便利です。横方向がコマ番号、縦方向が状態になります。

javascript
const STATES = {
  idle:  { row: 0, frames: 2 },
  walk:  { row: 1, frames: 6 },
  jump:  { row: 2, frames: 4 },
};

let currentState = 'walk';

// drawImage の切り出しy座標を state.row × FRAME_H にする
const state = STATES[currentState];
ctx.drawImage(
  sheet,
  frame * FRAME_W, state.row * FRAME_H,
  FRAME_W, FRAME_H,
  0, 0,
  FRAME_W, FRAME_H
);
frame = (frame + 1) % state.frames;

currentStateを書き換えるだけで表示が切り替わります。ただし状態を切り替えたときにframeを0にリセットしないと、コマ数が少ない状態に移ったとき範囲外のコマを参照してしまいます。切り替えの処理には必ずframe = 0を添えてください。

よくある失敗と対処

画像が真っ白・表示されないdrawImageをsheet.onloadの前に呼ぶと、画像がまだ読み込まれていないため何も描かれません。かならずonloadコールバックの中かその後でアニメーションを開始してください。

アニメーションがちらつく・速すぎるclearRectを忘れると前のコマが残ります。またintervalの計算が0になっているケースもあります。FPSが0の場合は割り算でInfinityになるため、値の検証をしておくと安心です。

向きを反転させたい(左右移動)ctx.save()→ctx.scale(-1, 1)→描画→ctx.restore()の順で左右を鏡反転できます。移動方向フラグで反転のオン・オフを切り替えると、同じシートで左右両方向に対応できます。

スプライトシートのコマが等間隔でないフリー素材の中にはコマ間にパディング(余白)が入っているものがあります。その場合は切り出し座標の計算に余白分を足す必要があります。素材のドキュメントに記載されているコマ幅・余白の値を確認してから実装に使ってください。

CSSの`background-position`方式との使い分け

スプライトアニメーションはCanvas以外に、HTML要素のbackground-imageとbackground-positionでも実現できます。

  • Canvas drawImage、向いている用途:ゲームロジックと描画をまとめて管理したい場合、変形・エフェクト処理が多い場合
  • CSS background-position、向いている用途:UIパーツやアイコンなど、DOM要素として扱いたい場合

CSSアニメーションのsteps()関数を使うとJavaScriptなしでコマ送りができます。animation: walk 0.6s steps(6) infinite;のように書き、background-positionの終点とstepsのコマ数を合わせます。ただし変形・衝突判定・他オブジェクトとの連携が必要なゲームの場合はCanvas方式の方が管理しやすいです。

次の行動

上記のCanvasコードをHTMLファイルに貼り付け、walk.pngを自作または素材サイトのものに差し替えて動作を確認してください。アニメーションが動いたら、キーボード入力とcurrentStateの切り替えを組み合わせて、プレイヤーの操作に応じた状態遷移を試してみましょう。作ったゲームはHTMLファイルまたはZIPファイルとしてアソバースに投稿できます。

参考資料

  • MDN Web Docs「CanvasRenderingContext2D: drawImage() method」
  • MDN Web Docs「requestAnimationFrame」
  • MDN Web Docs「animation-timing-function(steps関数)」