オリジナルのジグソーパズルゲームを作りたい。
でも、何から手をつけていいか分からない。
途中で実装が止まって、完成にたどり着けない。
JavaScriptとHTML5 Canvasを使えば、ブラウザで動くジグソーパズルを作れます。
この記事では、画像を分割してピースを作り、ドラッグ&ドロップで配置し、正しい位置にスナップする仕組みまで、実装していきます。
- HTML5 Canvasを使った画像のグリッド分割方法
- ドラッグ&ドロップAPIでピースを移動させる実装
- 座標計算を使った正しい位置へのスナップ判定
- 全ピースの配置状態をチェックする完成判定
- 初心者がつまずく座標計算とイベントハンドリングのデバッグ方法
\パズルの次は!頭を使うゲームを自分で作ってみよう/
最初の1本目に最適
Unity タワーディフェンス
ゲームの作り方講座
敵の侵攻を防ぐ配置戦略がパズル脳に直結!全10回のコンパクトな構成で、初心者でもゲーム開発の全体像をつかめます。
スマホアプリを作りたい方へ
Unity 放置インフレ型
クリッカーゲームの作り方講座
数値が積み上がる快感はパズル好きに刺さる!スマホ対応・リリースレベルの放置系クリッカーを全12回で完成させられます。
思考系ゲームに挑戦
Unity ノンフィールドRPG
+スレスパ風JRPG講座
カードを選んで戦略を組み立てるデッキ構築はパズル脳との相性抜群。Unity6対応・スマホ化対応の最新カリキュラムで挑戦できます。
あなたのオリジナルゲーム、今年こそ完成させませんか?
RPG・アクション・ホラー…Unityで本格ゲームを作りたい人のための学習サイトです。
実際に完成するゲームを題材に、
ソースコード・素材・プロジェクト一式をすべて公開。
仕事や学校の合間の1〜2時間でも、
「写経→改造」で自分のゲームまで作りきれる環境です。
ジグソーパズルゲームの基本構造

ジグソーパズルゲームは、3つの要素で構成されています。
画像を分割してピースを作る処理、ピースをドラッグ&ドロップで移動させる処理、正しい位置に配置されたかを判定する処理。
これらの要素が組み合わさることで、ゲームとして成立するんです。
まずは、それぞれの要素がどのような役割を担っているか、理解しておきましょう。
| 要素 | 役割 | 実装方法 |
|---|---|---|
| 画像分割 | 元画像をグリッド状に分割してピースを作成 | Canvas APIのdrawImageメソッド |
| ドラッグ&ドロップ | マウスでピースを移動させる | mousedown、mousemove、mouseupイベント |
| スナップ判定 | 正しい位置に近づいたら自動配置 | 距離計算と閾値判定 |
この3つの要素を順番に実装していけば、ジグソーパズルゲームが完成します。
それぞれの実装方法を、詳しく見ていきましょう。
画像分割の仕組み

ジグソーパズルのピースを作るには、元の画像をグリッド状に分割する必要があります。
3×3のグリッドなら、9つのピースに分割。
4×4のグリッドなら、16個のピースに分割。
分割数が増えるほど、難易度が上がる仕組みです。
JavaScriptでは、Canvas APIのdrawImageメソッドを使って、元画像の一部分だけを切り出せます。
この技術が、ジグソーパズルのピース生成の基礎になります。
- 3×3(9ピース):初心者向け
- 4×4(16ピース):中級者向け
- 5×5(25ピース):上級者向け
まずは3×3から始めて、動作を確認しながら難易度を上げていくのがおすすめです。
ドラッグ&ドロップの実装

ピースをマウスで動かすには、mousedown、mousemove、mouseupという3つのイベントを使います。
マウスを押した瞬間に「ドラッグ開始」を記録。
マウスを動かしている間、ピースの位置をマウス座標に追従させる。
マウスを離した瞬間に「ドラッグ終了」を処理。
この流れで、自然なドラッグ&ドロップが実現できます。
ここで注意すべきポイントは、ピース内での相対座標を記録すること。
マウス座標とピースの左上座標の差を覚えておくと、ドラッグ中もマウスとピースの位置関係が保たれます。
スナップ判定の考え方

ピースを正しい位置に近づけた時、自動的に「パチッ」と吸いつくのがスナップ機能。
実装のポイントは、ピースの現在位置と目標位置の距離を計算すること。
距離が一定の閾値(例えば20ピクセル)以下になったら、ピースの位置を目標位置に固定する。
閾値の調整で、スナップの感度を変えられます。

値が小さい(10〜20ピクセル)と、ピッタリ合わせないとスナップしないため、難易度が上がります。
値が大きい(40〜50ピクセル)と、少し近づけるだけでスナップするため、初心者向けになります。
実装の準備:HTMLとCanvasの設定

まずは、HTMLファイルとCanvas要素を準備しましょう。
HTMLファイルの基本構造
基本的なHTMLの構造は、以下のようになります。
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
<!DOCTYPE html> <html lang="ja"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>ジグソーパズルゲーム</title> <style> #puzzle-container { text-align: center; margin: 20px auto; } canvas { border: 2px solid #333; cursor: pointer; } </style> </head> <body> <div id="puzzle-container"> <h1>ジグソーパズル</h1> <canvas id="puzzle-canvas" width="600" height="600"></canvas> <div id="pieces-container"></div> </div> <script src="jigsaw.js"></script> </body> </html> |
Canvas要素は、ピースを配置する領域として使います。
実際のピースは、別のCanvas要素として動的に生成。
これにより、各ピースを独立して操作できるようになります。
画像の分割処理を実装する

次に、元画像を分割してピースを作る処理を書いていきましょう。
Imageオブジェクトで画像を読み込む
まずは、元画像を読み込む処理から。
|
1 2 3 4 5 6 7 8 |
const image = new Image(); image.src = 'puzzle-image.jpg'; image.onload = function() { // 画像の読み込みが完了したら、分割処理を実行 createPieces(image, 3, 3); // 3×3のグリッドに分割 }; |
Imageオブジェクトのonloadイベントは、画像の読み込みが完了した時に発火。
この時点で、画像の幅と高さが確定しているため、安全に分割処理を実行できます。
Canvasで画像の一部分を切り出す
画像を分割するには、元画像の一部分だけを新しいCanvasに描画します。
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
function createPieces(sourceImage, rows, cols) { const pieceWidth = sourceImage.width / cols; const pieceHeight = sourceImage.height / rows; const pieces = []; for (let row = 0; row < rows; row++) { for (let col = 0; col < cols; col++) { // 新しいCanvas要素を作成 const pieceCanvas = document.createElement('canvas'); pieceCanvas.width = pieceWidth; pieceCanvas.height = pieceHeight; const ctx = pieceCanvas.getContext('2d'); // 元画像の一部分だけを描画 const sourceX = col * pieceWidth; const sourceY = row * pieceHeight; ctx.drawImage( sourceImage, sourceX, sourceY, pieceWidth, pieceHeight, // 元画像から切り出す範囲 0, 0, pieceWidth, pieceHeight // Canvasに描画する範囲 ); pieces.push({ canvas: pieceCanvas, correctRow: row, correctCol: col, currentX: 0, currentY: 0 }); } } return pieces; } |
drawImageメソッドの引数の意味を理解することが重要。
最初の4つの引数(sourceX, sourceY, pieceWidth, pieceHeight)は、元画像のどの部分を切り出すかを指定。
後の4つの引数(0, 0, pieceWidth, pieceHeight)は、切り出した画像をCanvasのどこに描画するかを指定。
この処理を全ピース分繰り返せば、画像が分割されます。
ドラッグ&ドロップ機能を実装する

ピースを動かせるようにするため、マウスイベントを実装しましょう。
mousedownでドラッグ開始を記録
マウスを押した瞬間、どのピースを掴んでいるかを記録します。
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
let draggedPiece = null; let dragOffsetX = 0; let dragOffsetY = 0; pieces.forEach(piece => { piece.canvas.addEventListener('mousedown', function(e) { const rect = piece.canvas.getBoundingClientRect(); dragOffsetX = e.clientX - rect.left; dragOffsetY = e.clientY - rect.top; draggedPiece = piece; piece.canvas.style.position = 'absolute'; piece.canvas.style.zIndex = '1000'; }); }); |
getBoundingClientRectは、Canvas要素の画面座標を取得するメソッド。
マウス座標(e.clientX, e.clientY)からCanvasの左上座標を引くことで、ピース内での相対座標が計算できます。
この相対座標を覚えておくと、ドラッグ中もマウスとピースの位置関係が保たれます。
mousemoveでピースを移動させる
マウスを動かしている間、ピースの位置を更新します。
|
1 2 3 4 5 6 7 |
document.addEventListener('mousemove', function(e) { if (draggedPiece) { draggedPiece.canvas.style.left = (e.clientX - dragOffsetX) + 'px'; draggedPiece.canvas.style.top = (e.clientY - dragOffsetY) + 'px'; } }); |
ドラッグ開始時に記録したdragOffsetXとdragOffsetYを使うのがポイント。
これにより、マウスの位置から相対座標を引いた値が、ピースの左上座標になります。
mouseupでドラッグ終了とスナップ判定
マウスを離した瞬間、ピースが正しい位置に近いかチェックします。
|
1 2 3 4 5 6 7 8 |
document.addEventListener('mouseup', function(e) { if (draggedPiece) { checkSnap(draggedPiece); draggedPiece.canvas.style.zIndex = '1'; draggedPiece = null; } }); |
ドラッグ終了時は、スナップ判定を実行。
判定が成功すれば、ピースは正しい位置に固定されます。
スナップ機能の実装

ピースを正しい位置に近づけた時に、自動的に配置する処理を実装しましょう。
距離計算でスナップ判定
スナップ判定は、ピースの現在位置と目標位置の距離を計算します。
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
function checkSnap(piece) { const correctX = piece.correctCol * pieceWidth; const correctY = piece.correctRow * pieceHeight; const currentX = parseInt(piece.canvas.style.left) || 0; const currentY = parseInt(piece.canvas.style.top) || 0; const distance = Math.sqrt( Math.pow(currentX - correctX, 2) + Math.pow(currentY - correctY, 2) ); const snapThreshold = 30; // スナップの閾値(ピクセル) if (distance < snapThreshold) { piece.canvas.style.left = correctX + 'px'; piece.canvas.style.top = correctY + 'px'; piece.isPlaced = true; // スナップ成功時の視覚的フィードバック piece.canvas.style.border = '3px solid green'; } } |
Math.sqrtとMath.powで、2点間の距離を計算。
三平方の定理の応用ですね。
距離が閾値以下なら、ピースの位置を目標位置に固定。
視覚的フィードバックとして、緑色の枠線を表示すると、ユーザーが成功を実感しやすくなります。
スナップの感度調整
snapThresholdの値を変えると、スナップの感度が変わります。
ゲームの難易度調整で、この値を動的に変更するのもアリ。

完成判定の実装

全ピースが正しい位置に配置されたかをチェックする処理を追加しましょう。
配列でピースの配置状態を管理
各ピースにisPlacedフラグを持たせて、配置状態を管理します。
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
function checkCompletion(pieces) { const allPlaced = pieces.every(piece => piece.isPlaced === true); if (allPlaced) { // パズル完成! alert('パズル完成!おめでとうございます!'); showCompletionEffect(); } } function showCompletionEffect() { // 完成時の演出(例:全ピースを光らせる) pieces.forEach(piece => { piece.canvas.style.boxShadow = '0 0 20px gold'; }); } |
Array.everyメソッドは、全ての要素が条件を満たすかチェック。
全てのピースがisPlaced === trueなら、パズル完成。
完成時は、視覚的な演出を追加すると、達成感がアップします。
実装手順の全体像

ここまで説明した内容を、実装手順として整理しましょう。
- STEP1HTMLとCanvasの準備
HTMLファイルを作成し、Canvas要素を配置します。基本的なスタイルも設定しておくと、後で調整しやすくなります。
- STEP2画像の読み込み
Imageオブジェクトを使って、元画像を読み込みます。onloadイベントで読み込み完了を確認してから、分割処理を実行します。
- STEP3画像の分割処理
Canvas APIのdrawImageメソッドを使って、元画像をグリッド状に分割します。各ピースを新しいCanvas要素として作成し、配列で管理します。
- STEP4ドラッグ&ドロップ実装
mousedown、mousemove、mouseupイベントを使って、ピースをドラッグ&ドロップできるようにします。相対座標を記録することで、自然な操作感を実現します。
- STEP5スナップ機能の実装
距離計算を使って、ピースが正しい位置に近づいた時に自動配置するスナップ機能を実装します。閾値を調整することで、難易度を変更できます。
- STEP6完成判定の実装
全ピースが正しい位置に配置されたかをチェックし、完成時に演出を表示します。Array.everyメソッドを使うと、シンプルに判定できます。
この順番で実装すれば、ジグソーパズルゲームが完成します。
順番を間違えると、後で大幅な修正が必要になるので注意。
初心者がつまずきやすいポイントとデバッグ方法

JavaScriptでジグソーパズルを作る際、いくつか注意すべき点があります。
座標系の混乱を避ける
Canvasの座標系と、画面座標(getBoundingClientRect)は別物。
Canvas内の座標は、Canvas要素の左上が(0, 0)。
一方、画面座標は、ブラウザウィンドウの左上が(0, 0)。
この違いを理解していないと、座標計算がずれてしまいます。
デバッグ時は、console.logで座標を出力して確認しましょう。
|
1 2 3 4 |
console.log('Canvas座標:', currentX, currentY); console.log('画面座標:', e.clientX, e.clientY); console.log('相対座標:', dragOffsetX, dragOffsetY); |
イベントハンドリングの重複を防ぐ
同じピースに複数回イベントリスナーを追加すると、イベントが重複して発火する場合があります。
対策として、イベントリスナーは一度だけ追加するように注意。
または、removeEventListenerで削除してから、新しく追加する方法もあります。
ピースの重なり順を管理
ドラッグ中のピースが他のピースに隠れるのを防ぐため、z-indexを調整。
ドラッグ開始時は、z-indexを大きく(例:1000)。
ドラッグ終了時は、z-indexを小さく(例:1)。
これで、常にドラッグ中のピースが前面に表示されます。
- 座標系を混同して、ピースが思い通りの位置に移動しない
- イベントリスナーを重複追加して、動作が不安定になる
- z-indexを設定せずに、ドラッグ中のピースが他のピースに隠れる
パズルゲームが好きなら!頭を使うゲームを自分で作る楽しさを体験しよう
パズルゲームが好きな人は、ゲームの仕組みや戦略を考えるのが得意です。その思考力はゲーム開発にそのまま活かせます。配置・戦略・ロジック設計が楽しめる講座を紹介します。
Unity タワーディフェンスゲームの作り方講座【全10回!配置戦略が楽しい初心者最初の1本!】

- 敵の侵攻ルートを読んで砲台を配置する戦略性がパズル脳に直結
- 全10回のコンパクトな構成で初心者でもゲーム開発の全体像をつかめる
- 敵AI・ステージ設計・アイテムショップ・レベルデザインまで習得
- 丁寧な解説とソースコード付きでサクサク進められる
Unity タワーディフェンスゲームの作り方講座は、敵の侵攻を防ぐために砲台をどこに配置するかを考える戦略性がパズル好きの思考回路と直結するジャンルです。
全10回というコンパクトな構成で、敵AI・ステージ設計・アイテムショップ・レベルデザインまで習得できます。「最初の1本目」として最もおすすめできる入門講座です。
パズルを解く感覚でゲームを作る第一歩を踏み出したい人に、自信を持っておすすめできる一本です。
全10回・配置戦略が楽しいタワーディフェンスをゼロから作る
まず1本完成させてゲーム開発の楽しさを体験しよう
→ タワーディフェンスゲームの作り方講座を見てみる
コンパクトな10回で完成!ゲーム開発の全体像を一気につかもう!
Unity 放置インフレ型クリッカーゲームの作り方講座【全12回!スマホアプリとしてリリースできるレベルのゲームを作れる!】

- 数値が積み上がる快感と段階的な要素解放がパズル好きに刺さる
- スマホ対応・リリースレベルのクリッカーゲームを全12回で完成
- ScriptableObjectやInterfaceなど実践で使えるUnity中級スキルも習得
- セーブ・ロード機能・サウンド実装まで本格的な構成
Unity 放置インフレ型クリッカーゲームの作り方講座は、数値が積み上がる快感・段階的な要素の解放・最適化の楽しさがパズルゲームと相性抜群のジャンルです。
単純なカウンターゲームではなく、本格的な放置系クリッカーゲームをスマホ対応でリリースレベルまで作り上げます。講座専用素材付きで、ダウンロードしてすぐに開発をスタートできます。
「自分で作ったアプリをスマホでプレイしたい!」という人の夢を全12回で叶えられる講座です。
スマホ対応・リリースレベルの放置系クリッカーを完成させる
数値が積み上がる快感をゲームで自分の手で作り上げよう
→ クリッカーゲームの作り方講座を見てみる
スマホで動く自分だけのクリッカーゲームを今すぐ作り始めよう!
Unity ノンフィールドRPG+スレスパ風JRPG講座【Unity6対応!カード戦略×JRPGで思考系ゲームを作れる!】

- カードを選んで戦略を組み立てるデッキ構築がパズル脳と相性抜群
- Unity6対応・スマホ化対応で最新環境のゲーム開発が学べる
- Slay the Spire風のゲームロジック設計を楽しみながら習得
- ノンフィールドRPGとデッキ構築JRPGの2つを合わせて学ぶのがおすすめ
Unity ノンフィールドRPGの作り方講座+Slay the Spire風デッキ構築JRPGの作り方講座は、手札から最適なカードを選んで戦略を組み立てるデッキ構築システムを実装できる講座です。
「どのカードを引いてどう組み合わせるか」を考える思考プロセスはパズルゲームと同じ脳の使い方。Unity6対応・スマホ化対応の最新カリキュラムで、思考系ゲームの設計を楽しみながら学べます。
タワーディフェンス・クリッカーで開発に慣れたら、次のステップとして挑戦したい一本です。
カード戦略×JRPGで思考系ゲームの設計を学ぶ
トレンドのデッキ構築システムをスマホ向けに実装しよう
→ スレスパ風デッキ構築JRPG講座を見てみる
パズル脳を活かしたゲーム設計で、一歩上のゲーム開発スキルを身につけよう!
まとめ

この記事では、JavaScriptでジグソーパズルゲームを作る方法について解説しました。
- 画像の分割はCanvas APIのdrawImageメソッドで実現でき、グリッド状に切り出すことでピースを作成できる
- ドラッグ&ドロップはmousedown、mousemove、mouseupの3つのイベントで実装し、相対座標を記録することで自然な操作感が実現できる
- スナップ機能は距離計算で実装し、閾値を調整することで難易度を変更できる
- 完成判定は配列のeveryメソッドで全ピースの配置状態をチェックし、全て配置されたらクリア
- 座標系の違い(Canvas座標と画面座標)を理解しておくことが、デバッグ時の混乱を防ぐポイント
まずは簡単な3×3のグリッドから始めて、動作を確認しながら難易度を上げていきましょう。
JavaScriptでゲーム制作の基礎を身につけたら、次はUnityに挑戦してみるのもアリ。
Unityなら、より本格的なゲーム制作が可能になります。
あなたのオリジナルゲーム、今年こそ完成させませんか?
RPG・アクション・ホラー…Unityで本格ゲームを作りたい人のための学習サイトです。
実際に完成するゲームを題材に、
ソースコード・素材・プロジェクト一式をすべて公開。
仕事や学校の合間の1〜2時間でも、
「写経→改造」で自分のゲームまで作りきれる環境です。





コメント