この記事では、Phaser.jsを使って初心者でも簡単に楽しいゲームを作れるよう、ステップごとに分かりやすく解説していきます。最終的に、このシリーズを通して「ぱくぱくフルーツ」という、画面に現れる果物をタップして消していくシンプルなゲームを完成させることができます。「ぱくぱくフルーツ」は、クリックやタッチで果物を消すと、次の果物が出現して繰り返し遊べるような仕組みや、インタラクティブな要素やアニメーションを取り入れたゲームです。
こんにちは!前回までの記事では、Phaser.jsを使ったゲーム開発の基本設定と、テキストを画面に表示するところまでを学びました。
前回の記事▽
今回は、果物の画像を画面に表示し、ユーザーがタップすると果物が消える仕組みを実装していきます。これにより、実際のゲームらしいインタラクションが生まれ、ゲームの基礎を作ることができます。
ゲームのキャンバスサイズを設定する
まず、ゲームがどのデバイスでも快適に表示されるように、キャンバスのサイズを自動で調整する設定を行います。これにより、パソコンやスマートフォンなど、異なる画面サイズのデバイスでもゲームが常に適切なサイズで表示されるようになります。
次のコードを<script>
タグの中に書き込んでいきましょう。
const config = {
type: Phaser.AUTO,
width: window.innerWidth,
height: window.innerHeight,
backgroundColor: '#add8e6',
parent: document.body,
scene: {
preload: preload,
create: create
},
physics: {
default: 'arcade',
arcade: {
gravity: { y: 0 },
debug: false
}
}
};
width
とheight
: それぞれウィンドウの幅と高さを設定します。window.innerWidth
とwindow.innerHeight
を使用することで、ユーザーの画面サイズに応じたキャンバスを自動的に生成できます。backgroundColor
: ゲームの背景色を水色(#add8e6
)に設定しています。physics
オブジェクト: 物理エンジンの設定を行います。ここでは、Phaser.jsに組み込まれているArcade
物理エンジンを使用しています。このエンジンはシンプルで軽量な物理演算を提供し、基本的な重力や衝突判定を処理することができます。default: 'arcade'
: 使用する物理エンジンとしてArcade
を指定します。arcade
:Arcade
エンジンの詳細設定を行います。gravity: { y: 0 }
: ゲーム内のオブジェクトに対して重力を設定します。ここではY方向の重力を0にしているため、オブジェクトが自然に落下することはありません。debug: false
: デバッグモードのオン・オフを指定します。false
に設定すると、デバッグ用の表示(衝突判定の範囲など)は表示されません。
この設定により、ゲームのキャンバスは画面サイズに自動でリサイズされ、常に中央に配置されます。また、軽量な物理エンジンが適用され、今後追加する動きやインタラクションの処理がスムーズに行われます。
果物の画像を読み込み、画面に表示する
次に、果物の画像を読み込み、画面に表示する方法を学びます。ここでは、リンゴやバナナなど、さまざまな果物を画面に表示していきます。
画像の読み込み
まず、preload
関数で果物の画像を読み込みます。画像ファイルはassets
フォルダにあらかじめ保存しておく必要があります。
function preload() {
this.load.image('apple', 'assets/apple.png');
this.load.image('grape', 'assets/grape.png');
this.load.image('banana', 'assets/banana.png');
this.load.image('melon', 'assets/melon.png');
this.load.image('orange', 'assets/orange.png');
}
this.load.image
: 各果物の画像ファイルを読み込みます。画像はassets
フォルダ内に保存しておきます。
画像の準備に困った場合は、フリー素材集で有名な「いらすとや」が便利です。規約の範囲内であれば、個人、法人、商用、非商用問わず無料で使用できます。
変数の設定
let fruits;
let currentFruit = 'apple';
const fruitTypes = ['apple', 'grape', 'banana', 'melon', 'orange'];
const fruitSize = Math.min(game.config.width, game.config.height) / 3;
fruits
: これは、画面に表示されるすべての果物を格納するための変数です。Phaser.jsでは、複数のオブジェクトを扱う際にグループ化することで、管理がしやすくなります。currentFruit
: 現在表示している果物の種類を保持する変数です。初期値としてリンゴ(apple
)を設定しています。この変数を使って、どの果物を表示するかを制御します。fruitTypes
: この配列には、ゲームで使用する果物の種類をリストにしています。リンゴ、ブドウ、バナナ、メロン、オレンジの5種類が含まれています。これらの果物はランダムに選ばれて画面に表示されます。fruitSize
: 画面に表示する果物のサイズを決定するための変数です。game.config.width
とgame.config.height
のどちらか小さい方の1/3の大きさに設定しています。これにより、画面のサイズに応じて果物が適切な大きさで表示されます。
画面に果物を表示する
次に、create
関数で読み込んだ果物を画面に表示します。
function create() {
fruits = this.physics.add.group();
this.input.on('pointerdown', handleTap, this);
generateFruits.call(this);
}
this.physics.add.group()
: 画面上に複数の果物をグループとして管理するために使用します。これにより、果物を一括で操作することが可能になります。this.input.on('pointerdown', handleTap, this)
: ユーザーが画面をクリックまたはタップしたときにhandleTap
関数が呼び出されるように設定します。
果物を生成して表示する
次に、generateFruits
関数を使って、画面に果物をランダムに配置します。
function generateFruits(type) {
for (let i = 0; i < 5; i++) {
let x = Phaser.Math.Between(fruitSize, game.config.width - fruitSize);
let y = Phaser.Math.Between(fruitSize, game.config.height - fruitSize);
const type = Phaser.Utils.Array.GetRandom(fruitTypes);
const fruit = fruits.create(x, y, type).setInteractive();
const originalWidth = fruit.width;
const scaleX = fruitSize / originalWidth;
fruit.setScale(scaleX);
}
}
Phaser.Math.Between(fruitSize, game.config.width - fruitSize)
: 果物を画面内にランダムに配置するために、X座標とY座標をランダムに設定します。このコードにより、果物が画面の端に寄りすぎないように配置されます。Phaser.Utils.Array.GetRandom(fruitTypes)
:fruitTypes
配列からランダムに果物の種類を選びます。これにより、画面に表示される果物の種類がランダムになります。fruits.create(x, y, type).setInteractive()
: 指定された位置に果物を表示し、それをインタラクティブ(タップ可能)に設定します。fruit.setScale(scaleX)
: 果物のサイズを画面のサイズに合わせて調整します。これにより、どのデバイスでも果物が適切なサイズで表示されます。
果物をタップして消す仕組みを実装する
次に、ユーザーが果物をタップすると、その果物が消える仕組みを実装します。
果物をタップして消す
handleTap
関数で、タップされた果物を特定し、それを消す処理を行います。
function handleTap(pointer) {
const children = fruits.getChildren();
for (let i = children.length - 1; i >= 0; i--) {
const fruit = children[i];
if (Phaser.Geom.Rectangle.Contains(fruit.getBounds(), pointer.x, pointer.y)) {
fruit.destroy();
break;
}
}
}
fruits.getChildren()
: グループ内に含まれるすべての果物を取得します。Phaser.Geom.Rectangle.Contains
: ユーザーがタップした位置が果物の範囲内にあるかどうかを確認します。fruit.destroy()
: タップされた果物を削除します。これにより、果物が画面から消えます。
全体のコード
ここまでの、全体のコードを確認しましょう。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Simple Fruit Tap Game</title>
<style>
body {
margin: 0;
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
background-color: #add8e6;
overflow: hidden;
}
</style>
</head>
<body>
<script src="https://cdn.jsdelivr.net/npm/phaser@3/dist/phaser.min.js"></script>
<script>
const config = {
type: Phaser.AUTO,
width: window.innerWidth,
height: window.innerHeight,
backgroundColor: '#add8e6',
parent: document.body,
scene: {
preload: preload,
create: create
},
physics: {
default: 'arcade',
arcade: {
gravity: { y: 0 },
debug: false
}
}
};
const game = new Phaser.Game(config);
let fruits;
let currentFruit = 'apple';
const fruitTypes = ['apple', 'grape', 'banana', 'melon', 'orange'];
const fruitSize = Math.min(game.config.width, game.config.height) / 3;
function preload() {
this.load.image('apple', 'assets/apple.png');
this.load.image('grape', 'assets/grape.png');
this.load.image('banana', 'assets/banana.png');
this.load.image('melon', 'assets/melon.png');
this.load.image('orange', 'assets/orange.png');
}
function create() {
fruits = this.physics.add.group();
this.input.on('pointerdown', handleTap, this);
generateFruits.call(this, currentFruit);
}
function handleTap(pointer) {
const children = fruits.getChildren();
for (let i = children.length - 1; i >= 0; i--) {
const fruit = children[i];
if (Phaser.Geom.Rectangle.Contains(fruit.getBounds(), pointer.x, pointer.y)) {
fruit.destroy();
break;
}
}
}
function generateFruits(type) {
for (let i = 0; i < 5; i++) {
let x = Phaser.Math.Between(fruitSize, game.config.width - fruitSize);
let y = Phaser.Math.Between(fruitSize, game.config.height - fruitSize);
const fruit = fruits.create(x, y, type).setInteractive();
const originalWidth = fruit.width;
const scaleX = fruitSize / originalWidth;
fruit.setScale(scaleX);
}
}
</script>
</body>
</html>
動作確認をしてみよう
最後に、実際にコードをブラウザで実行して、動作確認をしてみましょう。以下の点を確認してください。
- 画面に5つの果物が表示されていること。
- ユーザーが果物をクリックまたはタップすると、その果物が消えること。
これらが正しく動作していれば、今回のコードは成功です!次回の章では、さらにゲームに動きを加えて、より面白いものにしていきます。引き続き、楽しんでゲーム開発を進めていきましょう!
次の記事▽