この記事では、Phaser.jsを使って初心者でも簡単に楽しいゲームを作れるよう、ステップごとに分かりやすく解説していきます。最終的に、このシリーズを通して「ぱくぱくフルーツ」という、画面に現れる果物をタップして消していくシンプルなゲームを完成させることができます。「ぱくぱくフルーツ」は、クリックやタッチで果物を消すと、次の果物が出現して繰り返し遊べるような仕組みや、インタラクティブな要素やアニメーションを取り入れたゲームです。
こんにちは!前回の記事では、果物がすべて消えると次の果物が現れ、背景色が変わるという仕組みを実装しました。
前回の記事▽
この章では、さらにゲームを楽しくするために、果物に動きとアニメーションを追加します。具体的には、果物が画面の中を動き回り、端にぶつかると跳ね返るアニメーションと、果物が画面に現れる際に「ぽよよん」としたアニメーション効果を追加します。
果物が画面の中を動き回るアニメーション
まず、果物が画面の中をランダムに動き回り、画面の端にぶつかると跳ね返るアニメーションを追加します。これにより、果物が静止しているだけでなく、動きのあるインタラクティブな要素が加わり、プレイヤーを引きつけることができます。
create
関数の強化
以下のコードでは、果物が画面内を動き回り、端にぶつかると跳ね返るように設定しています。
function create() {
fruits = this.physics.add.group({
bounceX: 1,
bounceY: 1,
collideWorldBounds: true
});
this.input.on('pointerdown', handleTap, this);
// 最初の果物を生成
generateFruits.call(this, currentFruit);
}
bounceX: 1, bounceY: 1
: 果物が画面の端にぶつかったときに、X軸とY軸の両方で跳ね返るように設定します。1
という値は、完全に跳ね返る(エネルギー損失がない)ことを意味します。collideWorldBounds: true
: 果物が画面の端(境界)に衝突することを有効にします。これにより、果物が画面の外に出ることなく、常に画面内で動き回ります。
果物が画面の中を動くアニメーション
function generateFruits(type) {
fruits.clear(true, true);
// 果物ごとの背景色を設定
// ...
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;
// 果物が画面の中を動く
this.tweens.add({
targets: fruit,
duration: Phaser.Math.Between(4000, 6000),
x: x + Phaser.Math.Between(-100, 100),
y: y + Phaser.Math.Between(-100, 100),
yoyo: true,
repeat: -1,
ease: 'Sine.easeInOut',
});
}
}
x: x + Phaser.Math.Between(-100, 100), y: y + Phaser.Math.Between(-100, 100)
: このコードでは、果物が画面内をランダムに移動する距離を設定します。X軸とY軸の両方で、果物が元の位置から-100ピクセルから100ピクセルの範囲で移動するようになっています。yoyo: true
: 果物が指定された位置に移動した後、元の位置に戻る動きを繰り返すように設定します。これにより、果物が画面内を行ったり来たりする動きが生まれ、ゲームに動きが加わります。repeat: -1
: この設定は、アニメーションが無限に繰り返されることを意味します。-1
は無限ループを示しています。ease: 'Sine.easeInOut'
: アニメーションの動きが滑らかになるように、サイン波に基づいたイージング関数を使用します。これにより、果物が自然な動きで移動します。
果物が現れる際の「ぽよよん」アニメーション
次に、果物が画面に現れるときに「ぽよよん」としたアニメーションを追加します。これにより、果物が現れる際の視覚的な楽しさが増し、プレイヤーがよりインタラクションを楽しむことができます。
generateFruits
関数の強化
以下のコードでは、果物が現れる際に「ぽよよん」としたスケールアニメーションを追加しています。
function generateFruits(type) {
fruits.clear(true, true);
// 果物ごとの背景色を設定
// ...
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(0); // 初期スケールを0に設定
// 果物の「ぽよよん」アニメーション
this.tweens.add({
targets: fruit,
scaleX: scaleX,
scaleY: scaleX,
duration: 500,
ease: 'Bounce.easeOut'
});
// 果物が画面の中を動く
this.tweens.add({
targets: fruit,
duration: Phaser.Math.Between(4000, 6000),
x: x + Phaser.Math.Between(-100, 100),
y: y + Phaser.Math.Between(-100, 100),
yoyo: true,
repeat: -1,
ease: 'Sine.easeInOut',
});
}
}
fruit.setScale(0)
: 果物の初期スケールを0
に設定します。これにより、果物が最初は画面に表示されない状態になります。this.tweens.add({ ... })
: Phaser.jsのtweens
機能を使って、果物が現れる際にスケールアニメーションを適用します。Bounce.easeOut
は、弾むようなエフェクトを与えるイージング関数です。このアニメーションによって、果物が「ぽよよん」と弾むようにして画面に登場します。x: x + Phaser.Math.Between(-100, 100), y: y + Phaser.Math.Between(-100, 100)
: 果物が画面内をランダムに移動する距離を設定します。X軸とY軸の両方で、果物が元の位置から-100ピクセルから100ピクセルの範囲で移動するようになっています。yoyo: true
: 果物が指定された位置に移動した後、元の位置に戻る動きを繰り返す設定です。これにより、果物が画面内を行ったり来たりする動きを実現します。repeat: -1
: アニメーションが無限に繰り返される設定です。ease: 'Sine.easeInOut'
: 自然で滑らかな動きを実現するためのイージング関数です。
全体のコード
以下は、この章で完成した全体のコードです。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>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({
bounceX: 1,
bounceY: 1,
collideWorldBounds: true
});
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();
if (fruits.countActive(true) === 0) {
const nextIndex = (fruitTypes.indexOf(currentFruit) + 1) % fruitTypes.length;
currentFruit = fruitTypes[nextIndex];
generateFruits.call(this, currentFruit);
}
break;
}
}
}
function generateFruits(type) {
fruits.clear(true, true);
switch (type) {
case 'apple':
this.cameras.main.setBackgroundColor('#ffbcbc');
document.body.style.backgroundColor = '#ffbcbc';
break;
case 'grape':
this.cameras.main.setBackgroundColor('#9ee3b2');
document.body.style.backgroundColor = '#9ee3b2';
break;
case 'banana':
this.cameras.main.setBackgroundColor('#fff4b3');
document.body.style.backgroundColor = '#fff4b3';
break;
case 'melon':
this.cameras.main.setBackgroundColor('#ffd1dc');
document.body.style.backgroundColor = '#ffd1dc';
break;
case 'orange':
this.cameras.main.setBackgroundColor('#ffe6b3');
document.body.style.backgroundColor = '#ffe6b3';
break;
}
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(0); // 初期スケールを0に設定
// 果物の「ぽよよん」アニメーション
this.tweens.add({
targets: fruit,
scaleX: scaleX,
scaleY: scaleX,
duration: 500,
ease: 'Bounce.easeOut'
});
// 果物が画面の中を動く
this.tweens.add({
targets: fruit,
duration: Phaser.Math.Between(4000, 6000),
x: x + Phaser.Math.Between(-100, 100),
y: y + Phaser.Math.Between(-100, 100),
yoyo: true,
repeat: -1,
ease: 'Sine.easeInOut',
});
}
}
</script>
</body>
</html>
動作確認をしてみよう
最後に、実際にコードをブラウザで実行して、動作確認をしてみましょう。以下の点を確認してください。
- 画面に5つの果物が表示されていること。
- 果物が画面の中を動き、端にぶつかると跳ね返ること。
- ユーザーが果物をクリックまたはタップすると、その果物が消えること。
- すべての果物が消えると、次の種類の果物が表示されること。
- 果物が現れるときに「ぽよよん」としたアニメーションが実行されること。
これらが正しく動作していれば、今回のコードは成功です!次回の章では、さらに効果音を追加して、ゲームの臨場感を高めていきます。引き続き、楽しんでゲーム開発を進めていきましょう!
次の記事▽