こんにちは、アンオブタニウムシードの管理人あんちゃんです!今回はHTML5で作るシューティングゲームの最新バージョン、Ver1.4を紹介します。このバージョンでは、ゲーム体験をさらに盛り上げるために、敵機や自機が破壊されたときの爆発エフェクトを完全に実装しました。Ver1.3からのバージョンアップで、ビジュアルとゲームプレイの両方に大きな進化を遂げました。
バージョン1.4の新機能:爆発エフェクトの追加
HTML5とJavaScriptを使ったシューティングゲームのバージョン1.4では、新たに爆発エフェクトが追加され、ゲーム体験がさらに向上しました。本記事では、この最新バージョンのシューティングゲームの開発過程と、実装した爆発エフェクトの詳細について解説しますね。
バージョン1.4の開発において、最も注目すべき追加機能は爆発エフェクトです。このエフェクトは、プレイヤーが敵機を撃墜したときに表示される視覚効果で、ゲームプレイにおける達成感と興奮を増幅させる役割を果たしますよ。
新機能の開発は、まずゲームの現状分析から始まりました。バージョン1.3では、敵機を倒した際に視覚的なフィードバックが不足しているというフィードバックがあったんです。これを受けて、よりダイナミックで視覚的に魅力的な爆発エフェクトを導入することが決定されました。
爆発エフェクトの実装は、いくつかのステップを踏んで行われました。開発チームはまず爆発エフェクトのデザインを考案し、エフェクトを複数のフレームで構成されるアニメーションとして実装しました。各フレームは異なる爆発の段階を表現しており、このアニメーションはHTML5の<canvas>要素とJavaScriptを用いて実現されました。
爆発エフェクトの追加により、プレイヤーの没入感とゲームの満足度が大幅に向上しました。敵機を撃墜するたびに表示される華やかな爆発は、プレイヤーに大きな達成感を与え、ゲームプレイを一層楽しませるものとなっています。バージョン1.4のアップデートは、これまでのシューティングゲームに新たな次元を追加し、プレイヤーにより魅力的な体験を提供します。
今回も、zキーが弾丸発射!
左矢印キーが機体を左に、
右矢印キーが機体を右に動かすよ!
スタートするときはマウスで「スタート」をクリックしてね!
シューティングゲームの概要
HTML5とJavaScriptを使ったシューティングゲームのバージョン1.4がリリースされました。このバージョンでは、新たに爆発エフェクトが追加され、ゲームの楽しさがさらにアップしました!
このシューティングゲームは、プレイヤーが自機を操作して、画面上から現れる敵機を撃ち倒していくシンプルで楽しいゲームです。敵機は一定の速度で上から下に向かって進み、プレイヤーはそれを避けつつ、攻撃を当てて倒します。敵機に弾が命中すると、爆発して消え、プレイヤーにスコアが加算されます。
操作方法はとても簡単です。プレイヤーはキーボードの矢印キーで自機を上下左右に移動させ、Zキーで弾を発射します。敵機に衝突するとゲームオーバーになるので、注意しながら進めてくださいね。バージョン1.4では、敵機を倒したときの爆発エフェクトが追加され、視覚的にとても魅力的になりました。華やかな爆発が画面に広がると、達成感が一層高まります。
このゲームは、特別なソフトウェアのインストールは不要で、インターネットに接続されていればすぐにプレイを始めることができます。これにより、手軽に楽しめるゲームとして、多くのユーザーに親しまれています。
バージョン1.4の開発において、爆発エフェクトはプレイヤーの没入感を高めるために追加されました。開発チームは、エフェクトがゲーム全体のテンポを損なわないように注意しながら、視覚的にインパクトのある演出を目指しました。爆発エフェクトは、複数のフレームで構成されたアニメーションとして実装され、プレイヤーが敵機を撃墜した瞬間に表示されます。
具体的には、HTML5の<canvas>要素とJavaScriptを使用して、このアニメーションを実現しました。スプライトシートを用いて複数のフレームを一つの画像にまとめ、JavaScriptでその画像を適切なタイミングで描画することで、滑らかなアニメーションを実現しました。
この新しい爆発エフェクトにより、プレイヤーは敵機を撃破するたびに視覚的な満足感を得ることができ、ゲームプレイがよりエキサイティングになります。敵機を次々と倒していく中で、このエフェクトがプレイヤーのモチベーションを高め、ゲームのリプレイ性を向上させる効果も期待できます。
シューティングゲームのバージョン1.4は、これまで以上に楽しい体験を提供します。今後のアップデートでは、さらに多様な敵機のパターンや新しい要素が追加される予定です。プレイヤーの皆さんにとって、シューティングゲームが一層魅力的なものになること間違いなしです!
ゲームの基本仕様
このシューティングゲームの基本仕様について詳しく説明しますね。バージョン1.4では、操作方法からゲームの流れまで、プレイヤーが直感的に楽しめるよう工夫されています。
まず、プレイヤーはキーボードの矢印キーを使って自機を上下左右に移動させます。敵機が画面上から下に向かって降りてくるので、これを避けながら撃ち落とす必要があります。弾を発射するには、Zキーを使用します。シンプルな操作で、誰でもすぐにゲームに慣れることができます。
ゲームの目的は、できるだけ多くの敵機を撃墜して高得点を獲得することです。敵機は一定の速度で移動し、画面外に出ると再び上部から現れます。敵機に自機がぶつかるとゲームオーバーとなりますが、プレイヤーはスコアを記録し、再挑戦することができます。
バージョン1.4では、敵機を撃墜した際に発生する爆発エフェクトが新たに追加されました。このエフェクトは、視覚的に非常に魅力的で、プレイヤーの達成感を高める効果があります。エフェクトは複数のフレームで構成されるアニメーションとして実装され、スプライトシートを使用して滑らかに表示されます。
以下は、基本的なゲームループの流れです:
- ゲーム開始: プレイヤーはスタートボタンを押してゲームを開始します。
- 自機の操作: 矢印キーで自機を操作し、Zキーで弾を発射します。
- 敵機の出現: 敵機が画面上部から出現し、一定の速度で下に移動します。
- 攻撃と回避: プレイヤーは敵機を撃墜しつつ、敵機との衝突を避けます。
- 爆発エフェクト: 敵機を撃墜すると、爆発エフェクトが表示されます。
- ゲームオーバー: 自機が敵機にぶつかるとゲームオーバーとなり、スコアが表示されます。
この基本仕様により、プレイヤーは直感的にゲームを楽しむことができます。また、爆発エフェクトの追加により、ゲームプレイがさらにエキサイティングになっています。次のセクションでは、この爆発エフェクトの実装方法について詳しく見ていきましょう。
使用技術とツール
このシューティングゲームの開発には、以下の技術とツールが使用されています。それぞれの技術とツールについて詳しく説明しますね。
- HTML5 Canvas:
HTML5の - JavaScript:
ゲームのロジックやインタラクションの実装に使用されています。JavaScriptは、ブラウザ上で動作するプログラミング言語で、ユーザーの入力に応じてリアクションを行うことができます。自機の操作や弾の発射、敵機の動きなど、ゲーム全体の動作を管理しています 【7†source】。 - 使用しているライブラリ:
このゲームでは、特定のJavaScriptライブラリは使用されていません。ゲームの開発は主に純粋なJavaScriptとHTML5 Canvasを使って行われており、外部ライブラリは使用していないようです 。
実際のコード例
以下は、基本的なゲームループの一部を示すコード例です:
const canvas = document.getElementById("gameCanvas");
const ctx = canvas.getContext("2d");
let ship = { x: canvas.width / 2 - 25, y: canvas.height - 60, width: 50, height: 50, dx: 0 };
let bullets = [];
let enemies = [];
let score = 0;
let gameOver = false;
document.addEventListener("keydown", keyDownHandler);
document.addEventListener("keyup", keyUpHandler);
document.addEventListener("keypress", keyPressHandler);
function keyDownHandler(e) {
if (e.key === "Right" || e.key === "ArrowRight") {
ship.dx = 5;
} else if (e.key === "Left" || e.key === "ArrowLeft") {
ship.dx = -5;
}
}
function keyUpHandler(e) {
if (e.key === "Right" || e.key === "ArrowRight" || e.key === "ArrowLeft" || e.key === "Left") {
ship.dx = 0;
}
}
function keyPressHandler(e) {
if (e.key === "z" || e.key === "Z") {
bullets.push({ x: ship.x + ship.width / 2 - 2.5, y: ship.y, width: 5, height: 10, dy: -5 });
}
}
function drawShip() {
ctx.drawImage(shipImage, ship.x, ship.y, ship.width, ship.height);
}
function drawBullets() {
bullets.forEach((bullet, index) => {
ctx.fillRect(bullet.x, bullet.y, bullet.width, bullet.height);
bullet.y += bullet.dy;
if (bullet.y < 0) {
bullets.splice(index, 1);
}
});
}
function drawEnemies() {
enemies.forEach((enemy, index) => {
ctx.drawImage(enemyImage, enemy.x, enemy.y, enemy.width, enemy.height);
enemy.y += enemy.dy;
if (enemy.y > canvas.height) {
enemies.splice(index, 1);
}
});
}
function createEnemies() {
if (Math.random() < 0.02) {
let enemyX = Math.random() * (canvas.width - 30);
enemies.push({ x: enemyX, y: 0, width: 30, height: 30, dy: 2 });
}
}
function detectCollisions() {
bullets.forEach((bullet, bulletIndex) => {
enemies.forEach((enemy, enemyIndex) => {
if (bullet.x < enemy.x + enemy.width && bullet.x + bullet.width > enemy.x &&
bullet.y < enemy.y + enemy.height && bullet.y + bullet.height > enemy.y) {
bullets.splice(bulletIndex, 1);
enemies.splice(enemyIndex, 1);
score += 10;
}
});
});
}
function update() {
if (gameOver) return;
ctx.clearRect(0, 0, canvas.width, canvas.height);
drawShip();
drawBullets();
drawEnemies();
createEnemies();
detectCollisions();
requestAnimationFrame(update);
}
document.addEventListener("DOMContentLoaded", (event) => {
update();
});
このコードは、基本的なゲームループと主要な関数の実装例です。HTML5 CanvasとJavaScriptを活用して、敵機の生成、衝突判定、スコア管理を行っています。
ゲームの進行とスコアリング
このシューティングゲームでは、プレイヤーの進行状況やスコアリングが非常に重要です。以下に、スコアの表示と更新、ゲームオーバー条件とリセットについて詳しく説明しますね。
スコアの表示と更新
ゲームが開始されると、画面の上部にスコアが表示されます。プレイヤーが敵機を撃墜するたびにスコアが加算され、リアルタイムで表示が更新されます。これにより、プレイヤーは現在の得点を常に確認することができ、ゲームの進行状況を把握しやすくなっています。
スコアの更新は、以下のようなコードで実装されています:
let score = 0;
function drawScore() {
ctx.font = "16px Arial";
ctx.fillStyle = "#0095DD";
ctx.fillText("Score: " + score, 8, 20);
}
function updateScore(points) {
score += points;
drawScore();
}
このコードでは、drawScore
関数がスコアを画面に描画し、updateScore
関数がスコアを更新しています。敵機を撃墜すると、updateScore
関数が呼び出され、スコアが増加します。
ゲームオーバー条件とリセット
ゲームオーバーの条件は、プレイヤーの自機が敵機に衝突した場合です。敵機が自機にぶつかるとゲームオーバーとなり、画面に「Game Over」のメッセージが表示されます。プレイヤーは、再度挑戦するためにゲームをリセットすることができます。
ゲームオーバーとリセットの実装は、以下のようになっています:
let gameOver = false;
function checkGameOver() {
enemies.forEach((enemy) => {
if (ship.x < enemy.x + enemy.width &&
ship.x + ship.width > enemy.x &&
ship.y < enemy.y + enemy.height &&
ship.y + ship.height > enemy.y) {
gameOver = true;
}
});
}
function drawGameOver() {
if (gameOver) {
ctx.font = "48px Arial";
ctx.fillStyle = "#FF0000";
ctx.fillText("Game Over", canvas.width / 2 - 120, canvas.height / 2);
}
}
function resetGame() {
gameOver = false;
score = 0;
ship.x = canvas.width / 2 - ship.width / 2;
ship.y = canvas.height - ship.height - 10;
bullets = [];
enemies = [];
update();
}
checkGameOver
関数は、敵機と自機の衝突を検出し、衝突が発生するとgameOver
フラグをtrue
に設定します。drawGameOver
関数は、ゲームオーバーのメッセージを画面に描画します。resetGame
関数は、ゲームをリセットし、初期状態に戻します。
これにより、プレイヤーはゲームの進行中にスコアを確認しながら、衝突に注意してゲームを進めることができます。また、ゲームオーバーになった場合も、すぐに再挑戦することが可能です。
爆発エフェクトの実装
バージョン1.4で追加された最大の特徴は、敵機が撃墜されたときに表示される爆発エフェクトです。このセクションでは、この爆発エフェクトの実装方法について詳しく説明しますね。
爆発エフェクトの概要
爆発エフェクトは、敵機が弾に命中した瞬間に表示され、プレイヤーに視覚的なフィードバックを提供します。このエフェクトにより、ゲームプレイがよりダイナミックでエキサイティングなものになります。
実装のステップ
- 爆発エフェクト画像の読み込み:
爆発エフェクトは、複数の画像を使用してアニメーションとして表現されています。以下のコードは、爆発エフェクトの画像を読み込む部分です:
const explosionImages = [
new Image(),
new Image()
];
explosionImages[0].src = 'https://tokodomo.xyz/wp-content/uploads/2024/07/shooting_ver1.4_bom1.png';
explosionImages[1].src = 'https://tokodomo.xyz/wp-content/uploads/2024/07/shooting_ver1.4_bom2.png';
- アニメーションの設定:
爆発エフェクトの各フレームを描画するために、フレーム数と各フレームのサイズを設定します。以下のコードはその設定部分です:
const explosionFrameWidth = 64; // 各フレームの幅
const explosionFrameHeight = 64; // 各フレームの高さ
const explosionFrameCount = 16; // フレーム数
- アニメーションの描画:
爆発エフェクトの各フレームを適切なタイミングで描画するためのロジックを実装します。以下のコードは、アニメーションの描画部分です:
function drawExplosions() {
explosions.forEach((explosion, index) => {
const frame = Math.floor(explosion.frame / 4);
if (frame >= explosionFrameCount) {
explosions.splice(index, 1);
return;
}
const image = explosionImages[Math.floor(Math.random() * explosionImages.length)];
const explosionX = explosion.x - (explosion.width / 2);
const explosionY = explosion.y - (explosion.height / 2);
ctx.drawImage(image, 0, 0, image.width, image.height, explosionX, explosionY, explosion.width, explosion.height);
explosion.frame++;
explosion.y += 1; // 爆発が少し下に流れるようにする
});
}
- エフェクトのトリガー:
敵機が撃墜されたときに爆発エフェクトをトリガーします。以下のコードは、敵機が撃墜された瞬間に爆発エフェクトを表示する部分です:
function detectCollisions() {
bullets.forEach((bullet, bulletIndex) => {
enemies.forEach((enemy, enemyIndex) => {
if (bullet.x < enemy.x + enemy.width && bullet.x + bullet.width > enemy.x &&
bullet.y < enemy.y + enemy.height && bullet.y + bullet.height > enemy.y) {
bullets.splice(bulletIndex, 1);
enemies.splice(enemyIndex, 1);
score += 10;
updateScore();
explosions.push({ x: enemy.x + enemy.width / 2, y: enemy.y + enemy.height / 2, width: enemy.width, height: enemy.height, frame: 0 });
}
});
});
}
- ゲームループへの統合:
爆発エフェクトをゲームループに統合します。これにより、毎フレームの更新で爆発エフェクトが適切に描画されます:
function update() {
if (gameOver && explosions.length === 0) {
cancelAnimationFrame(animationFrameId);
return;
}
ctx.clearRect(0, 0, canvas.width, canvas.height);
drawStars();
if (!gameOver) {
ship.x += ship.dx;
if (ship.x < 0) ship.x = 0;
if (ship.x + ship.width > canvas.width) ship.x = canvas.width - ship.width;
drawShip();
}
drawBullets();
drawEnemies();
drawExplosions(); // 爆発エフェクトを描画
createEnemies();
detectCollisions();
animationFrameId = requestAnimationFrame(update);
}
gameLoop();
これらのステップを通じて、プレイヤーが敵機を撃墜するたびに視覚的に魅力的な爆発エフェクトが表示されるようになります。このエフェクトはゲームのリアリズムと興奮を高め、プレイヤーの体験を一層豊かにします 。
爆発エフェクトの概要
爆発エフェクトの役割と重要性
爆発エフェクトは、シューティングゲームにおいて視覚的な魅力を高める非常に重要な要素です。敵機が撃墜されたときに発生する華やかな爆発は、プレイヤーに達成感と興奮をもたらします。このエフェクトにより、ゲームプレイがよりダイナミックで臨場感あふれるものになります。
爆発エフェクトの役割は以下の通りです:
- 視覚的なフィードバック: プレイヤーが敵機を撃墜したことを明確に伝えるため、視覚的なフィードバックとして機能します。これにより、プレイヤーは自分の行動が成功したことを直感的に理解できます。
- ゲームの没入感向上: 視覚的なエフェクトは、ゲームの世界にプレイヤーを引き込み、より深い没入感を提供します。爆発のリアリズムが高まるほど、プレイヤーはゲームに対して強い関心を持ち続けます。
- エキサイティングな体験: 華やかな爆発は、ゲームプレイにエキサイティングな要素を追加します。敵を倒すたびに表示される派手なエフェクトは、プレイヤーのモチベーションを高め、再挑戦を促す効果もあります。
爆発エフェクトの実装方法
バージョン1.4では、爆発エフェクトの実装に関してスプライトシートやパーティクルアニメーションは使用していません。実際には、静的な画像を用いて爆発エフェクトを表現しています。以下にその実装の基本的な流れを説明します。
- 爆発エフェクト画像の読み込み:
爆発エフェクトの画像を事前に用意し、それを読み込みます。
const explosionImage1 = new Image();
explosionImage1.src = 'https://tokodomo.xyz/wp-content/uploads/2024/07/shooting_ver1.4_bom1.png';
const explosionImage2 = new Image();
explosionImage2.src = 'https://tokodomo.xyz/wp-content/uploads/2024/07/shooting_ver1.4_bom2.png';
- 爆発エフェクトの表示:
敵機が撃墜された際に、爆発エフェクトの画像を表示します。以下のコードは、爆発エフェクトを描画する部分です。
let explosions = [];
function createExplosion(x, y) {
explosions.push({ x: x, y: y, image: Math.random() < 0.5 ? explosionImage1 : explosionImage2, frame: 0 });
}
function drawExplosions(ctx) {
explosions.forEach((explosion, index) => {
ctx.drawImage(explosion.image, explosion.x, explosion.y);
explosion.frame++;
if (explosion.frame > 20) {
explosions.splice(index, 1); // 爆発エフェクトを削除
}
});
}
- エフェクトのトリガー:
敵機が撃墜されたときに爆発エフェクトをトリガーします。
function checkCollisions() {
bullets.forEach((bullet, bulletIndex) => {
enemies.forEach((enemy, enemyIndex) => {
if (bullet.x < enemy.x + enemy.width && bullet.x + bullet.width > enemy.x &&
bullet.y < enemy.y + enemy.height && bullet.y + bullet.height > enemy.y) {
bullets.splice(bulletIndex, 1);
enemies.splice(enemyIndex, 1);
createExplosion(enemy.x, enemy.y); // 爆発エフェクトを作成
score += 10;
updateScore();
}
});
});
}
- ゲームループへの統合:
爆発エフェクトをゲームループに統合します。
function gameLoop() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
drawShip();
drawBullets();
drawEnemies();
drawExplosions(ctx); // 爆発エフェクトを描画
createEnemies();
checkCollisions();
requestAnimationFrame(gameLoop);
}
gameLoop();
このようにして、静的な爆発画像を用いることでシンプルに爆発エフェクトを実装しています。スプライトシートやパーティクルアニメーションを使用しないため、実装が容易でありながら、視覚的な効果を高めることができます。
爆発エフェクトの画像素材と準備
爆発画像の準備と読み込み
バージョン1.4では、シューティングゲームの爆発エフェクトを実現するために静的な爆発画像を使用しています。このセクションでは、爆発エフェクトの画像素材の準備と読み込みについて詳しく説明しますね。
- 爆発画像の準備:
まず、ゲームに使用する爆発エフェクトの画像素材を準備します。これらの画像は、爆発の瞬間を表現するために用いられます。インターネット上のフリー素材サイトや自作の画像を使用することができます。以下のような2つの爆発画像を使用しました:
- 画像の読み込み:
ゲーム内でこれらの画像を使用するために、JavaScriptで画像を読み込みます。以下のコードは、爆発画像を読み込む部分です:
const explosionImage1 = new Image();
explosionImage1.src = 'https://tokodomo.xyz/wp-content/uploads/2024/07/shooting_ver1.4_bom1.png';
const explosionImage2 = new Image();
explosionImage2.src = 'https://tokodomo.xyz/wp-content/uploads/2024/07/shooting_ver1.4_bom2.png';
- 爆発エフェクトの表示準備:
爆発エフェクトを表示するための配列と関数を準備します。敵機が撃墜された際に、これらの爆発画像を表示するようにします。
let explosions = [];
function createExplosion(x, y) {
explosions.push({ x: x, y: y, image: Math.random() < 0.5 ? explosionImage1 : explosionImage2, frame: 0 });
}
- 爆発エフェクトの描画:
爆発エフェクトを画面に描画する関数を作成します。これにより、爆発の瞬間がリアルに表現されます。
function drawExplosions(ctx) {
explosions.forEach((explosion, index) => {
ctx.drawImage(explosion.image, explosion.x, explosion.y);
explosion.frame++;
if (explosion.frame > 20) {
explosions.splice(index, 1); // 爆発エフェクトを削除
}
});
}
- エフェクトのトリガーとゲームループへの統合:
爆発エフェクトをゲームループに統合します。敵機が撃墜されたときに爆発エフェクトが表示されるようにします。
function checkCollisions() {
bullets.forEach((bullet, bulletIndex) => {
enemies.forEach((enemy, enemyIndex) => {
if (bullet.x < enemy.x + enemy.width && bullet.x + bullet.width > enemy.x &&
bullet.y < enemy.y + enemy.height && bullet.y + bullet.height > enemy.y) {
bullets.splice(bulletIndex, 1);
enemies.splice(enemyIndex, 1);
createExplosion(enemy.x, enemy.y); // 爆発エフェクトを作成
score += 10;
updateScore();
}
});
});
}
function gameLoop() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
drawShip();
drawBullets();
drawEnemies();
drawExplosions(ctx); // 爆発エフェクトを描画
createEnemies();
checkCollisions();
requestAnimationFrame(gameLoop);
}
gameLoop();
このようにして、爆発エフェクトの画像素材を準備し、適切に読み込むことで、シューティングゲームに視覚的な魅力を加えることができます。爆発の瞬間がリアルに表現されることで、プレイヤーの体験が一層豊かになります。
爆発エフェクトのアニメーション
フレームアニメーションの実装
爆発エフェクトのアニメーションは、ゲームの視覚的な魅力を高める重要な要素です。バージョン1.4では、フレームアニメーションを使用して爆発エフェクトを実装しています。この方法では、爆発の各瞬間を表現する複数の画像(フレーム)を順次表示することで、リアルな爆発の動きを再現します。
- フレーム画像の準備:
まず、爆発の各瞬間を表現する複数の画像を用意します。これらの画像は、アニメーションの各フレームとして使用されます。
const explosionFrames = [
new Image(),
new Image(),
new Image(),
new Image(),
// さらに多くのフレーム画像を追加
];
explosionFrames[0].src = 'explosion_frame1.png';
explosionFrames[1].src = 'explosion_frame2.png';
explosionFrames[2].src = 'explosion_frame3.png';
explosionFrames[3].src = 'explosion_frame4.png';
// さらに画像のソースを設定
- アニメーションの管理:
各爆発エフェクトに対して現在のフレームとその表示位置を管理します。
let explosions = [];
function createExplosion(x, y) {
explosions.push({ x: x, y: y, frameIndex: 0 });
}
function updateExplosions() {
explosions.forEach((explosion, index) => {
explosion.frameIndex++;
if (explosion.frameIndex >= explosionFrames.length) {
explosions.splice(index, 1); // 爆発が終わったら削除
}
});
}
- フレームの描画:
現在のフレームを画面に描画します。requestAnimationFrame
を使用してスムーズなアニメーションを実現します。
function drawExplosions(ctx) {
explosions.forEach((explosion) => {
const frame = explosionFrames[explosion.frameIndex];
ctx.drawImage(frame, explosion.x, explosion.y);
});
}
requestAnimationFrameを使ったスムーズなアニメーション
requestAnimationFrame
は、ブラウザが最適なタイミングでアニメーションを更新するためのAPIです。これを使用することで、スムーズで効率的なアニメーションを実現できます。
- アニメーションループの設定:
requestAnimationFrame
を使って、ゲームループを設定します。これにより、各フレームでゲームの状態を更新し、再描画します。
function gameLoop() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
drawShip();
drawBullets();
drawEnemies();
drawExplosions(ctx); // 爆発エフェクトを描画
createEnemies();
checkCollisions();
updateExplosions(); // 爆発エフェクトを更新
requestAnimationFrame(gameLoop);
}
gameLoop();
- アニメーションのスムーズな更新:
requestAnimationFrame
を使用することで、アニメーションのフレームが一定間隔でスムーズに更新され、視覚的に滑らかな動きを実現します。
このようにして、フレームアニメーションとrequestAnimationFrame
を組み合わせることで、リアルでダイナミックな爆発エフェクトを実装しています。これにより、プレイヤーはゲームプレイ中に視覚的な満足感を得ることができ、ゲームの没入感がさらに向上します。
コード解説とサンプル
バージョン1.4での爆発エフェクトの実装について、実際のコードを基に正確に説明します。このバージョンでは、静的な画像を使用して爆発エフェクトを表現しています。以下は、実際のコードを基にした解説です。
爆発画像の読み込み
爆発エフェクトに使用する画像を読み込むコードです。このバージョンでは2種類の爆発画像を使用しています:
const explosionImages = [
new Image(),
new Image()
];
explosionImages[0].src = 'https://tokodomo.xyz/wp-content/uploads/2024/07/shooting_ver1.4_bom1.png';
explosionImages[1].src = 'https://tokodomo.xyz/wp-content/uploads/2024/07/shooting_ver1.4_bom2.png';
爆発エフェクトの生成
敵機が撃墜された際に爆発エフェクトを生成するコードです。爆発の位置と画像を設定します。
let explosions = [];
function detectCollisions() {
bullets.forEach((bullet, bulletIndex) => {
enemies.forEach((enemy, enemyIndex) => {
if (
bullet.x < enemy.x + enemy.width &&
bullet.x + bullet.width > enemy.x &&
bullet.y < enemy.y + enemy.height &&
bullet.y + bullet.height > enemy.y
) {
bullets.splice(bulletIndex, 1);
enemies.splice(enemyIndex, 1);
score += 10;
updateScore();
explosions.push({
x: enemy.x + enemy.width / 2,
y: enemy.y + enemy.height / 2,
width: enemy.width,
height: enemy.height,
frame: 0,
});
}
});
});
}
爆発エフェクトの描画
爆発エフェクトを画面に描画するためのコードです。静的な画像を使用して爆発を表現します:
function drawExplosions() {
explosions.forEach((explosion, index) => {
const frame = Math.floor(explosion.frame / 4); // フレームをスローダウン
if (frame >= explosionFrameCount) {
explosions.splice(index, 1); // 爆発アニメーション終了
return;
}
const image = explosionImages[Math.floor(Math.random() * explosionImages.length)];
const explosionX = explosion.x - (explosion.width / 2);
const explosionY = explosion.y - (explosion.height / 2);
ctx.drawImage(image, 0, 0, image.width, image.height, explosionX, explosionY, explosion.width, explosion.height);
explosion.frame++;
});
}
ゲームループ
ゲームの状態を更新し、各フレームで再描画するためのメインループです。爆発エフェクトもここで更新されます:
function update() {
if (gameOver && explosions.length === 0) {
cancelAnimationFrame(animationFrameId);
return;
}
ctx.clearRect(0, 0, canvas.width, canvas.height);
drawStars();
if (!gameOver) {
ship.x += ship.dx;
if (ship.x < 0) ship.x = 0;
if (ship.x + ship.width > canvas.width) ship.x = canvas.width - ship.width;
drawShip();
}
drawBullets();
drawEnemies();
drawExplosions(); // 爆発エフェクトを描画
createEnemies();
detectCollisions();
animationFrameId = requestAnimationFrame(update);
}
function startGame() {
gameOver = false;
score = 0;
ship = { x: shipX, y: shipY, width: shipWidth, height: shipHeight, dx: 0 };
bullets = [];
enemies = [];
explosions = [];
document.getElementById("finalScore").style.display = "none";
cancelAnimationFrame(animationFrameId); // 既存のアニメーションフレームをキャンセル
update();
}
document.addEventListener("DOMContentLoaded", (event) => {
drawStars();
startGame();
});
このように、バージョン1.4では静的な画像を用いて爆発エフェクトを実装しています。各セクションは、特定の役割を果たしながら連携し、プレイヤーに視覚的な満足感を提供します。
自機と敵機の描画
このセクションでは、バージョン1.4での自機と敵機の描画について説明します。ゲームの画面に自機と敵機を描画するための具体的なコードを解説します。
自機の描画
自機はプレイヤーが操作する宇宙船で、キーボードの矢印キーで左右に移動し、Zキーで弾を発射します。以下のコードは、自機の画像を読み込み、キャンバス上に描画する部分です。
// 自機画像の読み込み
const shipImage = new Image();
shipImage.src = 'https://tokodomo.xyz/wp-content/uploads/2024/07/jiki.png';
// 自機の初期位置とサイズ
const shipWidth = 50;
const shipHeight = 50;
let shipX = canvas.width / 2 - shipWidth / 2;
const shipY = canvas.height - shipHeight - 10;
let ship = { x: shipX, y: shipY, width: shipWidth, height: shipHeight, dx: 0 };
// 自機の描画関数
function drawShip() {
ctx.drawImage(shipImage, ship.x, ship.y, ship.width, ship.height);
}
敵機の描画
敵機は画面上部から出現し、一定の速度で下方向に移動します。以下のコードは、敵機の画像を読み込み、キャンバス上に描画する部分です。
// 敵機画像の読み込み
const enemyImage = new Image();
enemyImage.src = 'https://tokodomo.xyz/wp-content/uploads/2024/07/teki.png';
// 敵機の描画関数
function drawEnemies() {
enemies.forEach((enemy, index) => {
ctx.drawImage(enemyImage, enemy.x, enemy.y, enemy.width, enemy.height);
enemy.y += enemy.dy;
if (enemy.y > canvas.height) {
enemies.splice(index, 1);
}
});
}
// 敵機の生成関数
function createEnemies() {
if (Math.random() < 0.02) {
let enemyX = Math.random() * (canvas.width - 30);
enemies.push({ x: enemyX, y: 0, width: 30, height: 30, dy: 2 });
}
}
キーボード入力のハンドリング
プレイヤーがキーボードで自機を操作するためのコードです。
document.addEventListener("keydown", keyDownHandler);
document.addEventListener("keyup", keyUpHandler);
document.addEventListener("keypress", keyPressHandler);
function keyDownHandler(e) {
if (e.key === "Right" || e.key === "ArrowRight") {
ship.dx = 5;
} else if (e.key === "Left" || e.key === "ArrowLeft") {
ship.dx = -5;
}
}
function keyUpHandler(e) {
if (e.key === "Right" || e.key === "ArrowRight" || e.key === "ArrowLeft" || e.key === "Left") {
ship.dx = 0;
}
}
function keyPressHandler(e) {
if (e.key === "z" || e.key === "Z") {
bullets.push({ x: ship.x + ship.width / 2 - 2.5, y: ship.y, width: 5, height: 10, dy: -5 });
}
}
ゲームループへの統合
自機と敵機の描画をゲームループに統合し、画面上で連続的に表示します。
function update() {
if (gameOver) return;
ctx.clearRect(0, 0, canvas.width, canvas.height);
drawStars();
ship.x += ship.dx;
if (ship.x < 0) ship.x = 0;
if (ship.x + ship.width > canvas.width) ship.x = canvas.width - ship.width;
drawShip();
drawBullets();
drawEnemies();
drawExplosions();
createEnemies();
detectCollisions();
requestAnimationFrame(update);
}
function startGame() {
gameOver = false;
score = 0;
ship = { x: shipX, y: shipY, width: shipWidth, height: shipHeight, dx: 0 };
bullets = [];
enemies = [];
explosions = [];
update();
}
document.addEventListener("DOMContentLoaded", (event) => {
drawStars();
startGame();
});
このようにして、自機と敵機を画面に描画し、ゲームの進行に合わせて動かすことができます。
衝突判定の実装
衝突判定アルゴリズム
衝突判定は、ゲーム内でプレイヤーの自機や弾が敵機と接触したときに、その接触を検出するために必要です。バージョン1.4では、シンプルな矩形衝突判定アルゴリズムを使用しています。
function detectCollisions() {
bullets.forEach((bullet, bulletIndex) => {
enemies.forEach((enemy, enemyIndex) => {
if (
bullet.x < enemy.x + enemy.width &&
bullet.x + bullet.width > enemy.x &&
bullet.y < enemy.y + enemy.height &&
bullet.y + bullet.height > enemy.y
) {
// 衝突が発生した場合の処理
bullets.splice(bulletIndex, 1);
enemies.splice(enemyIndex, 1);
score += 10;
updateScore();
explosions.push({
x: enemy.x + enemy.width / 2,
y: enemy.y + enemy.height / 2,
width: enemy.width,
height: enemy.height,
frame: 0,
});
}
});
});
enemies.forEach((enemy, enemyIndex) => {
if (
ship.x < enemy.x + enemy.width &&
ship.x + ship.width > enemy.x &&
ship.y < enemy.y + enemy.height &&
ship.y + ship.height > enemy.y
) {
// 自機が敵機にぶつかった場合の処理
gameOver = true;
explosions.push({
x: ship.x + ship.width / 2,
y: ship.y + ship.height / 2,
width: ship.width,
height: ship.height,
frame: 0,
});
setTimeout(displayFinalScore, 2000);
}
});
}
衝突時の処理と爆発エフェクトの呼び出し
衝突が検出されたときに、適切な処理を行い、爆発エフェクトを呼び出します。以下のコードは、敵機が弾や自機と衝突したときの処理を含んでいます。
- 弾と敵機の衝突:
弾が敵機に当たった場合、弾と敵機を配列から削除し、スコアを更新します。その後、爆発エフェクトを生成します。
if (
bullet.x < enemy.x + enemy.width &&
bullet.x + bullet.width > enemy.x &&
bullet.y < enemy.y + enemy.height &&
bullet.y + bullet.height > enemy.y
) {
bullets.splice(bulletIndex, 1);
enemies.splice(enemyIndex, 1);
score += 10;
updateScore();
explosions.push({
x: enemy.x + enemy.width / 2,
y: enemy.y + enemy.height / 2,
width: enemy.width,
height: enemy.height,
frame: 0,
});
}
- 自機と敵機の衝突:
自機が敵機に当たった場合、ゲームオーバーとし、爆発エフェクトを生成します。一定時間後にゲームオーバー画面を表示します。
if (
ship.x < enemy.x + enemy.width &&
ship.x + ship.width > enemy.x &&
ship.y < enemy.y + enemy.height &&
ship.y + ship.height > enemy.y
) {
gameOver = true;
explosions.push({
x: ship.x + ship.width / 2,
y: ship.y + ship.height / 2,
width: ship.width,
height: ship.height,
frame: 0,
});
setTimeout(displayFinalScore, 2000);
}
ゲームループでの統合
ゲームループにおいて、各フレームで衝突判定を行い、必要に応じて爆発エフェクトを表示します。
function update() {
if (gameOver && explosions.length === 0) {
cancelAnimationFrame(animationFrameId);
return;
}
ctx.clearRect(0, 0, canvas.width, canvas.height);
drawStars();
if (!gameOver) {
ship.x += ship.dx;
if (ship.x < 0) ship.x = 0;
if (ship.x + ship.width > canvas.width) ship.x = canvas.width - ship.width;
drawShip();
}
drawBullets();
drawEnemies();
drawExplosions(); // 爆発エフェクトを描画
createEnemies();
detectCollisions();
animationFrameId = requestAnimationFrame(update);
}
function startGame() {
gameOver = false;
score = 0;
ship = { x: shipX, y: shipY, width: shipWidth, height: shipHeight, dx: 0 };
bullets = [];
enemies = [];
explosions = [];
document.getElementById("finalScore").style.display = "none";
cancelAnimationFrame(animationFrameId); // 既存のアニメーションフレームをキャンセル
update();
}
document.addEventListener("DOMContentLoaded", (event) => {
drawStars();
startGame();
});
このようにして、衝突判定と爆発エフェクトの実装を統合し、ゲームのリアリズムとエキサイティングな体験を提供します。
爆発エフェクトの詳細なコード解説
バージョン1.4では、敵機が撃墜されたときに爆発エフェクトを生成し、そのエフェクトが完了するまで表示を続けます。以下は、爆発エフェクトの生成と終了に関するコードの詳細です。
爆発エフェクトの発生と終了
- 爆発エフェクトの生成:
敵機が弾に命中したときに、爆発エフェクトを生成します。生成された爆発はexplosions
配列に追加されます。
function detectCollisions() {
bullets.forEach((bullet, bulletIndex) => {
enemies.forEach((enemy, enemyIndex) => {
if (
bullet.x < enemy.x + enemy.width &&
bullet.x + bullet.width > enemy.x &&
bullet.y < enemy.y + enemy.height &&
bullet.y + bullet.height > enemy.y
) {
bullets.splice(bulletIndex, 1);
enemies.splice(enemyIndex, 1);
score += 10;
updateScore();
explosions.push({
x: enemy.x + enemy.width / 2,
y: enemy.y + enemy.height / 2,
width: enemy.width,
height: enemy.height,
frame: 0,
});
}
});
});
}
- 爆発エフェクトの終了:
爆発エフェクトが一定のフレーム数に達したときに、explosions
配列から削除されます。
function drawExplosions() {
explosions.forEach((explosion, index) => {
const frame = Math.floor(explosion.frame / 4); // フレームをスローダウン
if (frame >= explosionFrameCount) {
explosions.splice(index, 1); // 爆発アニメーション終了
return;
}
const image = explosionImages[Math.floor(Math.random() * explosionImages.length)];
const explosionX = explosion.x - (explosion.width / 2);
const explosionY = explosion.y - (explosion.height / 2);
ctx.drawImage(image, 0, 0, image.width, image.height, explosionX, explosionY, explosion.width, explosion.height);
explosion.frame++;
});
}
パーティクルの動きと透明度の変化
バージョン1.4では、パーティクルアニメーションは使用していませんが、爆発エフェクトの実装を将来的にパーティクルアニメーションに拡張するための参考コードを以下に示します。
- パーティクルの生成:
爆発が発生した地点にパーティクルを生成し、各パーティクルには位置、速度、透明度などの属性を設定します。
class Particle {
constructor(x, y, dx, dy, lifespan) {
this.x = x;
this.y = y;
this.dx = dx;
this.dy = dy;
this.lifespan = lifespan;
this.opacity = 1;
}
update() {
this.x += this.dx;
this.y += this.dy;
this.lifespan--;
this.opacity = this.lifespan / 100; // 透明度の変化
}
draw(ctx) {
if (this.lifespan > 0) {
ctx.fillStyle = `rgba(255, 165, 0, ${this.opacity})`;
ctx.beginPath();
ctx.arc(this.x, this.y, 3, 0, Math.PI * 2);
ctx.fill();
}
}
}
const particles = [];
function createExplosion(x, y) {
for (let i = 0; i < 50; i++) {
const dx = (Math.random() - 0.5) * 10;
const dy = (Math.random() - 0.5) * 10;
particles.push(new Particle(x, y, dx, dy, 100));
}
}
function updateParticles(ctx) {
particles.forEach((particle, index) => {
if (particle.lifespan <= 0) {
particles.splice(index, 1);
} else {
particle.update();
particle.draw(ctx);
}
});
}
- ゲームループでのパーティクルの更新:
各フレームでパーティクルの位置と透明度を更新し、再描画します。
function update() {
if (gameOver && explosions.length === 0) {
cancelAnimationFrame(animationFrameId);
return;
}
ctx.clearRect(0, 0, canvas.width, canvas.height);
drawStars();
if (!gameOver) {
ship.x += ship.dx;
if (ship.x < 0) ship.x = 0;
if (ship.x + ship.width > canvas.width) ship.x = canvas.width - ship.width;
drawShip();
}
drawBullets();
drawEnemies();
drawExplosions();
createEnemies();
detectCollisions();
updateParticles(ctx); // パーティクルの更新
animationFrameId = requestAnimationFrame(update);
}
このコードにより、爆発エフェクトがよりリアルでダイナミックになります。現バージョンでは静的な画像を用いていますが、将来的にパーティクルアニメーションを導入することで、さらに視覚的な魅力を高めることができます。
実際のゲームプレイと改善点
実際のゲームプレイ
バージョン1.4のシューティングゲームは、プレイヤーが自機を操作して、画面上から降りてくる敵機を撃ち落とすシンプルなゲームです。自機はキーボードの矢印キーで左右に移動し、Zキーで弾を発射します。敵機に弾が命中すると、爆発エフェクトが発生し、スコアが加算されます。
ゲームプレイ中には以下の要素が含まれます:
- 自機の操作: プレイヤーは自機を左右に動かし、敵機の攻撃を避けながら弾を発射します。
- 敵機の出現: 敵機はランダムに画面上部から出現し、下方向に移動します。敵機の速度は一定で、画面外に出ると消えます。
- 弾の発射: プレイヤーはZキーを押すことで弾を発射し、敵機を撃ち落とします。弾が画面上部に達すると消えます。
- 爆発エフェクト: 敵機が撃墜されると、爆発エフェクトが表示されます。このエフェクトにより、視覚的なフィードバックが提供され、ゲームの臨場感が高まります。
- スコアの加算: 敵機を撃墜するたびにスコアが加算され、画面上部に表示されます。
改善点
バージョン1.4のシューティングゲームにはいくつかの改善点があります。以下に、その主要なポイントを挙げます:
- グラフィックスの向上:
現在の爆発エフェクトは静的な画像を使用していますが、パーティクルアニメーションを導入することで、よりリアルでダイナミックなエフェクトを実現できます。また、敵機や自機のデザインも高解像度の画像に変更することで、視覚的な魅力を高めることができます。 - アニメーションのスムーズ化:
爆発エフェクトのフレームレートを調整し、アニメーションの滑らかさを向上させることで、プレイヤーの体験がさらに向上します。現在はフレームをスローダウンしていますが、より高精度なアニメーションを実装することが目指されます。 - インタラクティブな要素の追加:
パワーアップアイテムや障害物を追加することで、ゲームプレイに多様性を持たせることができます。これにより、プレイヤーは異なる戦略を試すことができ、ゲームのリプレイ価値が高まります。 - 難易度の調整:
ゲームの進行に応じて、敵機の速度や出現頻度を調整することで、難易度を段階的に上げることができます。これにより、プレイヤーは挑戦しがいのあるゲーム体験を享受できます。
これらの改善点を実装することで、シューティングゲームはさらに魅力的でエキサイティングなものになるでしょう。
ゲームプレイの流れ
バージョン1.4のシューティングゲームでは、スタートからゲームオーバーまでの流れがシンプルかつエキサイティングに設計されています。以下は、その具体的な流れです。
スタートからゲームオーバーまでの流れ
- スタート画面:
ゲームを開始するために、プレイヤーはスタートボタンを押します。スタート画面にはゲームのタイトルや操作方法が表示されます。
function startGame() {
gameOver = false;
score = 0;
ship = { x: shipX, y: shipY, width: shipWidth, height: shipHeight, dx: 0 };
bullets = [];
enemies = [];
explosions = [];
document.getElementById("finalScore").style.display = "none";
cancelAnimationFrame(animationFrameId); // 既存のアニメーションフレームをキャンセル
update();
}
document.addEventListener("DOMContentLoaded", (event) => {
drawStars();
startGame();
});
- ゲームプレイ:
ゲームが始まると、自機を左右に動かしながら敵機を撃ち落とします。プレイヤーはスコアを稼ぎつつ、敵機の攻撃を避ける必要があります。
function update() {
if (gameOver && explosions.length === 0) {
cancelAnimationFrame(animationFrameId);
return;
}
ctx.clearRect(0, 0, canvas.width, canvas.height);
drawStars();
if (!gameOver) {
ship.x += ship.dx;
if (ship.x < 0) ship.x = 0;
if (ship.x + ship.width > canvas.width) ship.x = canvas.width - ship.width;
drawShip();
}
drawBullets();
drawEnemies();
drawExplosions(); // 爆発エフェクトを描画
createEnemies();
detectCollisions();
animationFrameId = requestAnimationFrame(update);
}
- 敵機の出現と撃墜:
敵機はランダムに画面上部から出現し、下方向に移動します。プレイヤーは敵機を撃ち落としてスコアを稼ぎます。
function createEnemies() {
if (Math.random() < 0.02) {
let enemyX = Math.random() * (canvas.width - 30);
enemies.push({ x: enemyX, y: 0, width: 30, height: 30, dy: 2 });
}
}
- ゲームオーバー:
自機が敵機に衝突するとゲームオーバーとなります。スコアが表示され、再挑戦するためのオプションが提供されます。
function detectCollisions() {
bullets.forEach((bullet, bulletIndex) => {
enemies.forEach((enemy, enemyIndex) => {
if (
bullet.x < enemy.x + enemy.width &&
bullet.x + bullet.width > enemy.x &&
bullet.y < enemy.y + enemy.height &&
bullet.y + bullet.height > enemy.y
) {
bullets.splice(bulletIndex, 1);
enemies.splice(enemyIndex, 1);
score += 10;
updateScore();
explosions.push({
x: enemy.x + enemy.width / 2,
y: enemy.y + enemy.height / 2,
width: enemy.width,
height: enemy.height,
frame: 0,
});
}
});
});
enemies.forEach((enemy, enemyIndex) => {
if (
ship.x < enemy.x + enemy.width &&
ship.x + ship.width > enemy.x &&
ship.y < enemy.y + enemy.height &&
ship.y + ship.height > enemy.y
) {
gameOver = true;
explosions.push({
x: ship.x + ship.width / 2,
y: ship.y + ship.height / 2,
width: ship.width,
height: ship.height,
frame: 0,
});
setTimeout(displayFinalScore, 2000);
}
});
}
- スコア表示とリスタート:
ゲームオーバー時には、最終スコアが表示され、プレイヤーは再挑戦することができます。
function displayFinalScore() {
const finalScoreElement = document.getElementById("finalScore");
finalScoreElement.innerText = `GAME OVER\nSCORE: ${score}`;
finalScoreElement.style.display = "block";
}
ゲームプレイのスクリーンショット
ボカーンボカーンボカーーーン!
まとめ
全体のまとめ
本記事の振り返りとポイント
この記事では、HTML5とJavaScriptを使用して開発されたシューティングゲームのバージョン1.4について詳しく解説しました。特に、新たに追加された爆発エフェクトの実装に焦点を当てました。
- シューティングゲームの概要:
プレイヤーが自機を操作し、敵機を撃墜するシンプルなゲームです。矢印キーで左右に移動し、Zキーで弾を発射します。 - 使用技術とツール:
HTML5 CanvasとJavaScriptを使用し、画像の読み込みやアニメーションの実装を行いました。 - 爆発エフェクトの実装:
静的な画像を使用して爆発エフェクトを表現しました。パーティクルアニメーションの導入も将来的に検討されています。 - ゲームプレイの流れ:
スタート画面からゲームオーバーまでの流れを説明し、敵機の出現、弾の発射、衝突判定などの具体的なコードを解説しました。
シューティングゲーム開発の楽しさ
シューティングゲームの開発は、プログラミングの基礎から高度なアニメーションの実装まで幅広いスキルを活用する楽しいプロジェクトです。以下に、開発の楽しさをいくつか挙げてみます。
- クリエイティビティの発揮:
ゲームデザインやグラフィックの作成を通じて、クリエイティビティを存分に発揮できます。特に、爆発エフェクトや敵機のデザインなど、ビジュアル面での工夫が求められます。 - 問題解決能力の向上:
バグの修正や最適なアルゴリズムの実装を通じて、問題解決能力が向上します。特に、衝突判定やアニメーションのスムーズ化など、技術的な課題に取り組むことでスキルが磨かれます。 - 達成感と満足感:
ゲームが完成し、プレイヤーが楽しんでくれる様子を見ると大きな達成感と満足感を得られます。自分の作品が形になる喜びは、開発者にとって大きなモチベーションとなります。 - 学びの多さ:
シューティングゲームの開発を通じて、プログラミング言語の使い方やデザインの基本、ユーザーエクスペリエンスの重要性など、多くのことを学べます。
シューティングゲームの開発は、技術的なチャレンジとクリエイティブな要素が融合した、非常にやりがいのあるプロジェクトです。ぜひ、皆さんもゲーム開発の楽しさを体験してみてください。
学んだことと次のステップ
HTML5とJavaScriptの活用
このシューティングゲームの開発を通じて、HTML5とJavaScriptのさまざまな機能とその活用方法を学びました。以下は、具体的に学んだポイントです:
- HTML5 Canvas:
Canvas要素を使ってグラフィックを描画する方法を学びました。特に、ゲームオブジェクト(自機、敵機、弾など)を描画し、リアルタイムで更新する技術を習得しました。 - JavaScriptの基礎と応用:
ゲームロジックの実装を通じて、JavaScriptの基本的な構文やイベントハンドリング、タイマーやアニメーションフレームの使い方を学びました。また、オブジェクト指向プログラミングの概念も理解しやすくなりました。 - アニメーションと衝突判定:
requestAnimationFrameを使ってスムーズなアニメーションを実現し、矩形衝突判定アルゴリズムを使ってゲームオブジェクト間の衝突を検出する方法を学びました。
他のプロジェクトへの応用
このシューティングゲームの開発で得た知識とスキルは、他のプロジェクトにも応用できます。以下にいくつかの応用例を示します:
- インタラクティブなWebアプリケーション:
CanvasとJavaScriptを使って、インタラクティブなデータビジュアライゼーションやアニメーションを含むWebアプリケーションを開発できます。例えば、リアルタイムデータを表示するダッシュボードなどが考えられます。 - ゲーム開発:
他の種類のゲーム(例えば、パズルゲームやプラットフォーマーゲーム)にも、今回学んだ技術を応用できます。特に、アニメーションやユーザー入力の処理は、さまざまなゲームで重要な要素です。 - 教育ツール:
ゲームの技術を使って、教育目的のインタラクティブなツールを開発できます。例えば、プログラミング学習用のシミュレーションゲームや、科学実験を仮想的に体験できるツールなどです。 - ユーザーエクスペリエンスの向上:
Webサイトのユーザーインターフェースにアニメーションを追加することで、ユーザーエクスペリエンスを向上させることができます。例えば、ボタンのホバーアニメーションやスクロール時のエフェクトなどです。
それではいよいよシューティングゲームのVer1.4_6の全コードです。
これでーす!!
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Shooting Game Ver1.4</title>
<style>
canvas {
background-color: black;
border: 1px solid #000000;
}
#gameContainer {
position: relative;
display: inline-block;
width: 480px;
height: 600px;
}
#finalScore {
font-size: 30px;
color: red;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
background-color: rgba(255, 255, 255, 0.7);
padding: 10px;
display: none;
width: 480px;
text-align: center;
}
#scoreContainer {
font-size: 20px;
background-color: rgba(255, 255, 255, 0.7);
padding: 10px;
position: absolute;
top: 0;
width: 100%;
display: flex;
justify-content: space-between;
box-sizing: border-box;
}
</style>
</head>
<body>
<div id="gameContainer">
<canvas id="gameCanvas" width="480" height="600"></canvas>
<div id="finalScore">GAME OVER<br>SCORE: 0</div>
<div id="scoreContainer">
<div id="scoreDisplay">スコア: 0</div>
<button id="startButton" onclick="startGame()">START</button>
</div>
</div>
<script>
// ゲーム設定
const canvas = document.getElementById("gameCanvas");
const ctx = canvas.getContext("2d");
// アニメーションフレームID
let animationFrameId;
// 背景の星を描画する関数
function drawStars() {
const numStars = 100;
for (let i = 0; i < numStars; i++) {
const x = Math.random() * canvas.width;
const y = Math.random() * canvas.height;
const radius = Math.random() * 1.5;
ctx.beginPath();
ctx.arc(x, y, radius, 0, Math.PI * 2);
ctx.fillStyle = "white";
ctx.fill();
}
}
// 自機画像の読み込み
const shipImage = new Image();
shipImage.src = 'https://tokodomo.xyz/wp-content/uploads/2024/07/jiki.png';
const shipWidth = 50;
const shipHeight = 50;
let shipX = canvas.width / 2 - shipWidth / 2;
const shipY = canvas.height - shipHeight - 10;
let ship = { x: shipX, y: shipY, width: shipWidth, height: shipHeight, dx: 0 };
let bullets = [];
let enemies = [];
let explosions = [];
let score = 0;
let gameOver = false;
let gameOverTimeout;
// 敵機画像の読み込み
const enemyImage = new Image();
enemyImage.src = 'https://tokodomo.xyz/wp-content/uploads/2024/07/teki.png';
// 爆発エフェクト画像の読み込み
const explosionImages = [
new Image(),
new Image()
];
explosionImages[0].src = 'https://tokodomo.xyz/wp-content/uploads/2024/07/shooting_ver1.4_bom1.png'; // 1枚目の画像のパスを指定
explosionImages[1].src = 'https://tokodomo.xyz/wp-content/uploads/2024/07/shooting_ver1.4_bom2.png'; // 2枚目の画像のパスを指定
// 爆発エフェクトの設定
const explosionFrameWidth = 64; // 各フレームの幅
const explosionFrameHeight = 64; // 各フレームの高さ
const explosionFrameCount = 16; // フレーム数
document.addEventListener("keydown", keyDownHandler);
document.addEventListener("keyup", keyUpHandler);
document.addEventListener("keypress", keyPressHandler);
function keyDownHandler(e) {
if (e.key === "Right" || e.key === "ArrowRight") {
ship.dx = 5;
} else if (e.key === "Left" || e.key === "ArrowLeft") {
ship.dx = -5;
}
}
function keyUpHandler(e) {
if (e.key === "Right" || e.key === "ArrowRight" || e.key === "ArrowLeft" || e.key === "Left") {
ship.dx = 0;
}
}
function keyPressHandler(e) {
if (e.key === "z" || e.key === "Z") {
bullets.push({ x: ship.x + ship.width / 2 - 2.5, y: ship.y, width: 5, height: 10, dy: -5 });
}
}
function drawShip() {
ctx.drawImage(shipImage, ship.x, ship.y, ship.width, ship.height);
}
function drawBullets() {
ctx.fillStyle = "#FF0000";
bullets.forEach((bullet, index) => {
ctx.fillRect(bullet.x, bullet.y, bullet.width, bullet.height);
bullet.y += bullet.dy;
if (bullet.y < 0) {
bullets.splice(index, 1);
}
});
}
function drawEnemies() {
enemies.forEach((enemy, index) => {
ctx.drawImage(enemyImage, enemy.x, enemy.y, enemy.width, enemy.height);
enemy.y += enemy.dy;
if (enemy.y > canvas.height) {
enemies.splice(index, 1);
}
});
}
function createEnemies() {
if (Math.random() < 0.02) {
let enemyX = Math.random() * (canvas.width - 30);
enemies.push({ x: enemyX, y: 0, width: 30, height: 30, dy: 2 });
}
}
function drawExplosions() {
explosions.forEach((explosion, index) => {
const frame = Math.floor(explosion.frame / 4); // フレームをスローダウン
if (frame >= explosionFrameCount) {
explosions.splice(index, 1); // 爆発アニメーション終了
return;
}
// ランダムに爆発画像を選択
const image = explosionImages[Math.floor(Math.random() * explosionImages.length)];
// 爆発画像の中心を敵機の中心に合わせる
const explosionX = explosion.x - (explosion.width / 2);
const explosionY = explosion.y - (explosion.height / 2);
ctx.drawImage(
image,
0, 0, image.width, image.height,
explosionX, explosionY, explosion.width, explosion.height
);
explosion.frame++;
explosion.y += 1; // 爆発が少し下に流れるようにする
});
}
function detectCollisions() {
bullets.forEach((bullet, bulletIndex) => {
enemies.forEach((enemy, enemyIndex) => {
if (
bullet.x < enemy.x + enemy.width &&
bullet.x + bullet.width > enemy.x &&
bullet.y < enemy.y + enemy.height &&
bullet.y + bullet.height > enemy.y
) {
bullets.splice(bulletIndex, 1);
enemies.splice(enemyIndex, 1);
score += 10;
updateScore();
// ランダムに爆発画像を選択し、爆発の大きさを敵機のサイズにリサイズ
const useFirstImage = Math.random() < 0.5;
explosions.push({
x: enemy.x + enemy.width / 2,
y: enemy.y + enemy.height / 2,
width: enemy.width,
height: enemy.height,
frame: 0,
useFirstImage
});
}
});
});
enemies.forEach((enemy, enemyIndex) => {
if (
ship.x < enemy.x + enemy.width &&
ship.x + ship.width > enemy.x &&
ship.y < enemy.y + enemy.height &&
ship.y + ship.height > enemy.y
) {
// 自機が敵機にぶつかったときの処理
gameOver = true;
// ランダムに爆発画像を選択し、爆発の大きさを自機のサイズにリサイズ
const useFirstImage = Math.random() < 0.5;
explosions.push({
x: ship.x + ship.width / 2,
y: ship.y + ship.height / 2,
width: ship.width,
height: ship.height,
frame: 0,
useFirstImage
});
// 2秒後にゲームオーバー画面を表示
gameOverTimeout = setTimeout(displayFinalScore, 2000);
}
});
}
function updateScore() {
document.getElementById("scoreDisplay").innerText = `スコア: ${score}`;
}
function displayFinalScore() {
const finalScoreElement = document.getElementById("finalScore");
finalScoreElement.innerText = `GAME OVER\nSCORE: ${score}`;
finalScoreElement.style.display = "block";
}
function update() {
if (gameOver && explosions.length === 0) {
cancelAnimationFrame(animationFrameId);
return;
}
ctx.clearRect(0, 0, canvas.width, canvas.height);
drawStars();
if (!gameOver) {
ship.x += ship.dx;
if (ship.x < 0) ship.x = 0;
if (ship.x + ship.width > canvas.width) ship.x = canvas.width - ship.width;
drawShip();
}
drawBullets();
drawEnemies();
drawExplosions(); // 爆発エフェクトを描画
createEnemies();
detectCollisions();
animationFrameId = requestAnimationFrame(update);
}
function startGame() {
gameOver = false;
score = 0;
ship = { x: shipX, y: shipY, width: shipWidth, height: shipHeight, dx: 0 };
bullets = [];
enemies = [];
explosions = [];
document.getElementById("finalScore").style.display = "none";
clearTimeout(gameOverTimeout); // 既存のタイムアウトをクリア
cancelAnimationFrame(animationFrameId); // 既存のアニメーションフレームをキャンセル
update();
}
// ゲーム開始準備
document.addEventListener("DOMContentLoaded", (event) => {
drawStars();
});
</script>
</body>
</html>
ゲーム「Shooting Game Ver1.4」の概要
このゲームは、HTMLとJavaScriptを使った縦スクロールのシューティングゲームです。以下は、ゲームの主要な要素と機能の概要です。
ゲーム要素
- ゲームコンテナとキャンバス
gameContainer
: ゲームの全体のコンテナ。gameCanvas
: 実際のゲームが描画されるキャンバス。
- スコア表示
scoreContainer
: スコアとスタートボタンを含むコンテナ。scoreDisplay
: ゲーム中のスコアを表示。finalScore
: ゲームオーバー時のスコアを表示。
- 画像リソース
- 自機(プレイヤーのキャラクター):
shipImage
- 敵機:
enemyImage
- 爆発エフェクト:
explosionImages
ゲームの設定と機能
- ゲームの開始と終了
- スタートボタンを押すことでゲームが開始される (
startGame
関数)。 - 自機が敵機にぶつかるとゲームオーバーになる。
- ゲームのループと描画
update
関数がゲームのメインループを管理し、描画や衝突判定などを行う。- アニメーションは
requestAnimationFrame
を使ってスムーズに行われる。
- 操作方法
- キーボードの左右キーで自機を左右に移動 (
keyDownHandler
,keyUpHandler
)。 z
キーで弾を発射 (keyPressHandler
)。
- ゲームの主な機能
- 背景の星をランダムに描画 (
drawStars
)。 - 自機の描画 (
drawShip
)。 - 弾の発射と移動 (
drawBullets
)。 - 敵機の生成と移動 (
createEnemies
,drawEnemies
)。 - 爆発エフェクトの描画 (
drawExplosions
)。 - 衝突判定とスコアの更新 (
detectCollisions
,updateScore
)。
コメント