JavaScriptでゲームを作る!
それでは実際に処理を見ていきます。
先ずはファイル構成です。
ファイルとそれを入れるフォルダーを以下のような構成します。
コード全体で行おうとしていることは以下の通りです。
これにより、動的にゲーム画面やイラストを操作する仕組みが出来上がります。
<!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>
このファイルは、「画像を読み込んで管理するためのクラス」を定義しています。
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 画像要素;
}
}
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 画像要素;
}
このファイルは、「画像をキャンバス上の指定した位置に表示するためのクラス」を定義しています。
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, 描画幅, 描画高さ); // 画像を描画
}
}
if (画像縦横比 > 表示縦横比) {
// 横長画像の場合
描画幅 = 表示幅;
描画高さ = 表示幅 / 画像縦横比;
位置調整Y = (表示高さ - 描画高さ) / 2; // 垂直中央
} else {
// 縦長画像の場合
描画高さ = 表示高さ;
描画幅 = 表示高さ * 画像縦横比;
位置調整X = (表示幅 - 描画幅) / 2; // 水平中央
}
this.ctx.drawImage(画像要素, x1 + 位置調整X, y1 + 位置調整Y, 描画幅, 描画高さ);
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);
以下の流れでコードが進みます:
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);