忍者ブログ

JavaScriptゲーム

JavaScriptでゲームを作る!

画像を表示する方法

  • Webブラウザのゲームを作る上で重要になる処理として、画像を表示する方法を記します。
  • 以下の画像は背景と人物の2枚の画像を重ねて表示しています。
  • 背景画像のサイズは480x480pxですが、640x640pxの領域に拡大して表示しています。
  • 人物画像は1024x1024pxですが、少し横長の640x540pxの領域に縮小かつ中央にして表示しています。
  • このように、これから記す方法は画像を指定した領域に拡大したり、または縮小して表示することができます。
  • また、JavaScriptで行っているのでゲーム内の好きなタイミングで表示したり、別の画像に差し替えたり出来ます。

01 ファイル構成

それでは実際に処理を見ていきます。
先ずはファイル構成です。
ファイルとそれを入れるフォルダーを以下のような構成します。

02 ファイル全体の流れ

コード全体で行おうとしていることは以下の通りです。

  1. HTMLファイルに埋め込まれた <canvas> 要素に対して、JavaScriptを使って画像を描画します。
  2. 画像管理クラス を使って画像をあらかじめ読み込みます(プリロード処理)。
  3. 読み込んだ画像を スプライトクラス を使って描画します。

これにより、動的にゲーム画面やイラストを操作する仕組みが出来上がります。

03 各部分の詳細解説

a. sample.html - HTMLファイル

<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>画像を表示する方法</title>
</head>
<body>
    <canvas class="レイヤー1" width="640" height="640"></canvas>
    <script type="module" src="js/sample.js"></script>
</body>
</html>

HTMLファイルは、 <canvas> という描画領域を作るための要素を用意しています:

<canvas class="レイヤー1" width="640" height="640"></canvas>
  • <canvas>の役割
    • 画面上に「絵を描くためのキャンバス」です。
    • ここに画像を描画していきます。

b. imageManager.js - 画像管理

このファイルは、「画像を読み込んで管理するためのクラス」を定義しています。

export class 画像管理クラス {
    constructor() {
        this.画像キャッシュ = new Map();
    }

    async 画像を読み込む(画像情報リスト) {
        const プロミスリスト = 画像情報リスト.map(画像情報 => {
            return new Promise((resolve, reject) => {
                const 画像要素 = new Image();
                画像要素.src = 画像情報.src;

                画像要素.onload = () => {
                    this.画像キャッシュ.set(画像情報.id, 画像要素);
                    resolve();
                };

                画像要素.onerror = () => {
                    console.error(`画像が読み込めませんでした:${画像情報.src}`);
                    reject();
                };
            });
        });

        try {
            // 全ての画像が読み込まれるのを待つ
            await Promise.all(プロミスリスト);
            return true;
        } catch {
            return false;// 一つでも読み込めなかった場合
        }
    }

    取得する(id) {
        const 画像要素 = this.画像キャッシュ.get(id);
        if (!画像要素) console.error(`画像キャッシュにありません:${id}`);
        return 画像要素;
    }
}

1. クラスの宣言

export class 画像管理クラス {
    constructor() {
        this.画像キャッシュ = new Map();
    }
}
  • export: このクラスを他のJavaScriptのファイルからも使えるようにしています。
  • constructor: この部分は、クラスが作られたときに呼ばれる特別な関数です。
  • this.画像キャッシュ: Mapオブジェクトを使って、画像を保存しておきます。Mapは「キー」と「値」をペアで管理する便利なデータ構造です。

2. 画像を読み込む() 関数

async 画像を読み込む(画像情報リスト) {
    const プロミスリスト = 画像情報リスト.map(画像情報 => {
        return new Promise((resolve, reject) => {
            const 画像要素 = new Image();
            画像要素.src = 画像情報.src;
            画像要素.onload = () => {
                this.画像キャッシュ.set(画像情報.id, 画像要素);
                resolve();
            };
            画像要素.onerror = () => {
                console.error(`画像が読み込めませんでした:${画像情報.src}`);
                reject();
            };
        });
    });

    try {
        await Promise.all(プロミスリスト);
        return true;
    } catch {
        return false;
    }
}
  • この関数の役割:
    • 複数の画像を「並列処理」で読み込みます。これにより、全ての画像を同時に効率よく読み込むことができます。
    • 画像は読み込み完了後に「画像キャッシュ」に保存されます。
  • 細かい説明:
    1. 画像情報リスト.map():
      • 画像情報リストの各画像について1つずつPromiseを作ります。
      • Promiseは「画像の読み込み完了を待たずに進む」という非同期処理を行います。
    2. Promise の内容:
      • 読み込みたい画像ファイルのパスを指定し、画像要素という新しいImageオブジェクトを作ります。
      • 画像が正常に読み込まれた場合、resolve()を呼び出して成功として動作します。
      • エラーがあった場合は、reject()を呼び出してエラーとして動作します。
    3. Promise.all():
      • 全てのPromiseの処理結果を受け取る仕組みです。
      • どれか1つでもエラーがあれば、全体が失敗として扱われます。
      • この関数にawaitをつけて、かつ「画像を読み込む()」関数にasyncをつけることで、全てのPromiseの処理結果を待つようになります。

3. 取得する() 関数

取得する(id) {
    const 画像要素 = this.画像キャッシュ.get(id);
    if (!画像要素) console.error(`画像キャッシュにありません:${id}`);
    return 画像要素;
}
  • この関数の役割:
    • 引数で渡された id を使って、画像キャッシュから特定の画像を取り出します。
    • 見つからない場合はエラーを表示します。
  • 動き方:
    1. this.画像キャッシュ.get(id) でキャッシュを確認します。
    2. 画像があればそれを返し、なければエラーを表示します。

4. 初心者向けのポイント

  • 非同期処理を同期させる (async と await):
    • 時間がかかる処理(例: サーバーから画像データを取得する処理)を非同期で行っている場合に、その処理を待つために使います。
    • これにより、非同期の処理を順番どおり(同期的)に実行できるようになります。
  • Mapオブジェクト:
    • キー(ここでは id)を使ってデータを管理できます。
    • 例えば、「キーが "背景画像" のときはこの画像を取り出す」といった処理が簡単に行えます。

c. sprite.js - 描画処理

このファイルは、「画像をキャンバス上の指定した位置に表示するためのクラス」を定義しています。

export class スプライトクラス {
    constructor(canvas) {
        this.ctx = canvas.getContext('2d');
    }

    描画する(画像要素, x1, y1, x2, y2) {
        const 表示幅 = x2 - x1;
        const 表示高さ = y2 - y1;

        const 画像縦横比 = 画像要素.width / 画像要素.height;
        const 表示縦横比 = 表示幅 / 表示高さ;

        let 描画幅, 描画高さ, 位置調整X, 位置調整Y;

        if (画像縦横比 > 表示縦横比) {
            描画幅 = 表示幅;
            描画高さ = 表示幅 / 画像縦横比;
            位置調整X = 0;
            位置調整Y = (表示高さ - 描画高さ) / 2; // 垂直中央揃え
        } else {
            描画高さ = 表示高さ;
            描画幅 = 表示高さ * 画像縦横比;
            位置調整X = (表示幅 - 描画幅) / 2; // 水平中央揃え
            位置調整Y = 0;
        }

        this.ctx.drawImage(画像要素, x1 + 位置調整X, y1 + 位置調整Y, 描画幅, 描画高さ); // 画像を描画
    }
}

1. 縦横比を維持して画像を中央揃えにする処理

if (画像縦横比 > 表示縦横比) {
    // 横長画像の場合
    描画幅 = 表示幅;
    描画高さ = 表示幅 / 画像縦横比;
    位置調整Y = (表示高さ - 描画高さ) / 2; // 垂直中央
} else {
    // 縦長画像の場合
    描画高さ = 表示高さ;
    描画幅 = 表示高さ * 画像縦横比;
    位置調整X = (表示幅 - 描画幅) / 2; // 水平中央
}
  • 縦横比の計算:
    • 画像の元々の縦横比を計算し、それを使って適切なサイズに縮小または拡大します。
  • 中央揃え:
    • 画像の描画位置を調整して、表示領域の中央に配置します。

2. 画像を描画する

this.ctx.drawImage(画像要素, x1 + 位置調整X, y1 + 位置調整Y, 描画幅, 描画高さ);
  • drawImage メソッド: 指定した位置(表示したい領域の左上の座標)とサイズ(幅と高さ)に画像を描画します。

d. sample.js - メインの処理

import { 画像管理クラス } from './imageManager.js'; // クラスをインポート
import { スプライトクラス } from './sprite.js'; // クラスをインポート

class サンプル {
    static async main() {
        const canvas = document.querySelector('canvas.レイヤー1');
        const スプライト = new スプライトクラス(canvas);
        const 画像管理 = new 画像管理クラス();

        // 事前に読み込む画像の配列
        const 画像情報リスト = [
            { id: '像の背景', src: 'img/statue.jpg' },
            { id: '人物01', src: 'img/chara_L.png' }
        ];

        // ページ初期化時に画像をプリロード
        if (!await 画像管理.画像を読み込む(画像情報リスト)) return;

        // プリロード完了後に画像を描画
        スプライト.描画する(画像管理.取得する('像の背景'), 0, 0, 640, 640);
        スプライト.描画する(画像管理.取得する('人物01'), 0, 100, 640, 640);
    }
}

addEventListener('load', サンプル.main);

以下の流れでコードが進みます:

1. キャンバスと描画に必要なクラスを準備

const canvas = document.querySelector('canvas.レイヤー1');
const スプライト = new スプライトクラス(canvas);
const 画像管理 = new 画像管理クラス();
  • document.querySelector(): HTML内の特定の要素(ここでは <canvas>)を取得します。
  • スプライトクラス: 画像を描画する役割を持つクラスです。
  • 画像管理クラス: 画像を管理・読み込みするクラスです。

2. 画像のプリロード処理

const 画像情報リスト = [
    { id: '像の背景', src: 'img/statue.jpg' },
    { id: '人物01', src: 'img/chara_L.png' }
];
if (!await 画像管理.画像を読み込む(画像情報リスト)) return;
  • 画像情報リスト: 読み込む画像のファイル名を指定します。
  • await 画像管理.画像を読み込む(): すべての画像が読み込まれるまで処理を待ちます。

3. 画像の描画処理

スプライト.描画する(画像管理.取得する('像の背景'), 0, 0, 640, 640);
スプライト.描画する(画像管理.取得する('人物01'), 0, 100, 640, 640);
  • スプライト.描画する(): 指定した座標(左上と右下の座標を指定)に画像を描画します。
  • 画像管理.取得する(): 画像キャッシュから指定した画像を取得します。

拍手[0回]

PR

コメント

P R

プロフィール

HN:
No Name Ninja
性別:
非公開