MainScene
import { Scene } from 'phaser';
export default class MainScene extends Scene {
private sprite1: any;
private sprite2: any;
private sprite3: any;
private container!: Phaser.GameObjects.Container;
private bullets!: Phaser.Physics.Arcade.Group;
private player!: Phaser.Physics.Arcade.Sprite & { body: Phaser.Physics.Arcade.Body };
private scoreText!: Phaser.GameObjects.Text;
private joystick: any;
private rexVirtualJoystick: any;
private score = 0;
private cursors?: Phaser.Types.Input.Keyboard.CursorKeys;
private joystickArea!: Phaser.Geom.Rectangle;
private enemies!: Phaser.Physics.Arcade.Group;
private enemyBullets!: Phaser.Physics.Arcade.Group;
private bunkers!: Phaser.Physics.Arcade.StaticGroup;
private maxHealth = 10;
private hearts: Phaser.GameObjects.Image[] = [];
private currentHealth = 10;
private playerLife = 10;
private bunkerGroup!: Phaser.Physics.Arcade.StaticGroup;
private myship!: Phaser.Types.Physics.Arcade.ImageWithDynamicBody;
private ufoBullets!: Phaser.Physics.Arcade.Group;
constructor() {
super('MainScene');
}
preload() {
this.load.spritesheet('invader', 'assets/spaceinvaders2.png',
{frameWidth: 52, frameHeight: 36});
this.load.image('player', 'assets/yousai2.png');
this.load.image('jump', 'assets/jump.png');
this.load.image("bullet", "assets/bullet.png");
this.load.image("bullet2", "assets/laser2.png");
this.load.image('heart', 'assets/heart.png');
this.load.image('myship', 'assets/ship.png');
this.load.image("bulletufo", "assets/ufo.png");
}
create() {
this.anims.create({
key: 'invader1',
frames: this.anims.generateFrameNumbers('invader', { start: 0, end: 1 }),
frameRate: 2,
repeat: -1
});
this.anims.create({
key: 'invader2',
frames: this.anims.generateFrameNumbers('invader', { start: 2, end: 3 }),
frameRate: 2,
repeat: -1
});
this.anims.create({
key: 'invader3',
frames: this.anims.generateFrameNumbers('invader', { start: 4, end: 5 }),
frameRate: 2,
repeat: -1
});
// ハート画像を配置
for (let i = 0; i < this.maxHealth; i++) {
const heart = this.add.image(186 + i * 25, 19, 'heart').setScale(0.08).setScrollFactor(0);
this.hearts.push(heart);
}
this.myship = this.physics.add.image(800*0.2, 50, 'myship');
this.myship.setTint(0xFF0000);
// this.ship.depth = 2;
this.myship.setDepth(2);
this.myship.setCollideWorldBounds(true);
this.myship.setBounce(1).setScale(1.2);
this.myship.setVelocityX(200);
this.myship.setDepth(1);// 右方向に移動開始
this.ufoBullets = this.physics.add.group();
// 2秒(2000ms)ごとに発射判定を行うタイマー
this.time.addEvent({
delay: 500,
callback: this.shootUfoBullet,
callbackScope: this,
loop: true
});
this.enemies = this.physics.add.group();
this.enemyBullets = this.physics.add.group();
this.time.addEvent({
delay: 500, // 1000ミリ秒(1秒)ごとに実行
callback: this.enemyShoot, // 実行するメソッド
callbackScope: this,
loop: true // 繰り返す
});
for (let y = 0; y < 2; y++) {
for (let x = 0; x < 10; x++) {
this.sprite1 = this.enemies.create(40 + x * 56, 100+ y * 40, 'invader');
this.sprite1.play('invader1');
this.sprite1.setTint(0x0000FF);
this.sprite2 = this.enemies.create(40 + x * 56, 180+ y * 40, 'invader');
this.sprite2.play('invader2');
this.sprite2.setTint(0x8B008B);
this.sprite3 = this.enemies.create(40 + x * 56, 260+ y * 40, 'invader');
this.sprite3.play('invader3');
this.sprite3.setTint(0x4B0082);
this.container = this.add.container(800*0.01-10, 600*0.04-40, [ this.sprite1, this.sprite2, this.sprite3 ]);
this.tweens.add({
targets: this.container,
x: 180,
duration: 3000,
yoyo: true,
repeat: -1
});
}
}
this.bullets = this.physics.add.group({
defaultKey: "bullet",
allowGravity: false,
maxSize: 100
});
this.player = this.physics.add.sprite(800/2, 600 - 110, 'player').setDepth(1).setScale(0.9);
this.player.setInteractive();
this.player.setTint(0xffffff);
this.player.body.allowGravity = false;
this.player.setCollideWorldBounds(true);
this.player.setImmovable(true);
this.scoreText = this.add.text(14, 14, 'score: 0', { fontSize: '25px' });
this.scoreText.setScrollFactor(0);
if (this.input.keyboard) {
this.cursors = this.input.keyboard.createCursorKeys();
}
this.joystickArea = new Phaser.Geom.Rectangle(0, 200, 800, 280);
const graphics = this.add.graphics();
graphics.lineStyle(2, 0x00ff00);
graphics.strokeRectShape(this.joystickArea);
this.joystick = this.rexVirtualJoystick.add(this, {
x: 100,
y: 400,
radius: 100,
base: this.add.circle(0, 0, 100, 0x888888).setAlpha(0.1),
thumb: this.add.circle(0, 0, 50, 0xaaaaaa),
dir:'left&right',
forceMin: 16,
fixed: false, // ここをfalseに設定
enable: true
}).setVisible(false); // 最初は非表示にしておく
this.input.on('pointerdown', (pointer: Phaser.Input.Pointer) => {
if (this.joystickArea.contains(pointer.x, pointer.y)) {
this.joystick.setPosition(pointer.x, pointer.y);
this.joystick.setVisible(true);
this.joystick.base.depth = 100; // 必要に応じて重なり順を調整
this.joystick.thumb.depth = 101;
}
});
// 指を離した時に非表示にする場合
this.input.on('pointerup', () => {
this.joystick.setVisible(false);
});
this.input.addPointer(2);
const jumpButton = this.add.image(800/1.2, 600 - 62, 'jump').setScale(0.4).setInteractive();
jumpButton.on("pointerdown", () => {
this.shoot();
});
jumpButton.on("pointerup", () => {
this.shoot();
});
this.physics.add.collider( this.enemies, this.bullets,(alien, bullet)=>{
const en = bullet as Phaser.Physics.Arcade.Sprite;
const aln = alien as Phaser.Physics.Arcade.Sprite;
en.disableBody(true, true);
// en.disableBody(true, true);
// aliens.setActive(false);
aln.disableBody(true, true);
this.score += 5;
this.scoreText.setText('Score: ' + this.score);
if (this.enemies.countActive(true) === 0) {
this.gameWin();
}
this.time.delayedCall(500, () => {
en.setActive(false);
en.alpha = 0;
});
});
this.physics.add.collider(this.player, [this.enemyBullets,this.ufoBullets], (myplayer, myenemy) =>
{
const tar =myplayer as Phaser.Physics.Arcade.Sprite;
const myeny =myenemy as Phaser.Physics.Arcade.Sprite;
myeny.disableBody(true, true);
this.time.delayedCall(500, () => {
if (myeny.active) {
// myeny.destroy();
myeny.setAlpha(0);
}
});
// targetPlayer.disableBody(true, true);
this.playerLife -= 1;
this.damage();
// const tar = myplayer as Phaser.Physics.Arcade.Sprite;
tar.setTint(0xff0000);
this.time.addEvent({
delay: 200,
callback: () => {
tar.setTint(0xffffff);
}
});
// ライフが0になったらゲームオーバー
if (this.playerLife <= 0) {
this.physics.pause(); // 物理エンジンを停止
this.add.text(400, 250, 'GAME OVER', { fontSize: '84px', color: '#ff0000' }).setOrigin(0.5).setScrollFactor(0);
this.time.delayedCall(3000, () => {
tar.clearTint();
this.scene.start('MainScene');
this.playerLife = 10;
this.currentHealth = 10;
});
}
});
this.bunkerGroup = this.physics.add.staticGroup().setDepth(1);
const bunkerLocations = [150, 400, 650];
bunkerLocations.forEach(x => {
this.createBunker(x, 400);
});
this.physics.add.overlap([this.enemyBullets,this.ufoBullets], this.bunkerGroup, (bullet, block) => {
(bullet as Phaser.Physics.Arcade.Sprite).destroy();
// 弾を消す// block.destroy();
(block as Phaser.Physics.Arcade.Sprite).destroy();
});
this.physics.add.overlap(this.bullets, this.myship, (bullet, ship) => {
(bullet as Phaser.Physics.Arcade.Sprite).destroy(); // 弾を消す
// block.destroy();
(ship as Phaser.Physics.Arcade.Sprite).destroy();
this.gameWin();
});
}
private gameWin() {
this.physics.pause();
this.add.text(400, 300, 'YOU WIN!\nClick to Restart',
{ fontSize: '54px', color: '#002aff' }).setOrigin(0.5).setAlign('center');
this.input.once('pointerdown', () => {
this.scene.restart();
});
}
private shootUfoBullet() {
const bullet = this.ufoBullets.get(this.myship.x, this.myship.y, "bulletufo");
if (bullet) {
bullet.setScale(0.08);
bullet.setActive(true).setVisible(true);
bullet.body.allowGravity = false;
// bullet.setVelocityY(500); // 下方向に発射
bullet.body.velocity.y = 600;
}
}
createBunker( x: number, y: number) {
// バンカーの形をビットマップ形式の配列で定義 (1がブロック、0が空白)
const layout = [
[0, 0, 1, 1, 1, 1, 1, 1, 0, 0],
[0, 1, 1, 1, 1, 1, 1, 1, 1, 0],
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
[1, 1, 1, 0, 0, 0, 0, 1, 1, 1],
[1, 1, 0, 0, 0, 0, 0, 0, 1, 1]
];
const blockSize = 8; // 1つのブロックのサイズ(px)
const color = 0x20ff20; // インベーダー風の緑色
layout.forEach((row, rowIndex) => {
row.forEach((value, colIndex) => {
if (value === 1) {
// 各ドットを矩形(Rectangle)として作成
const block = this.add.rectangle(
x + colIndex * blockSize,
y + rowIndex * blockSize,
blockSize,
blockSize,
color
);
// 物理ボディを有効化
this.bunkerGroup.add(block);
}
});
});
return this.bunkerGroup;
}
damage() {
if (this.currentHealth > 0) {
this.currentHealth -= 1;
// 配列の最後のハートを空のハートに切り替えるか、破棄する
const heartToRemove = this.hearts.pop();
if (heartToRemove) {
heartToRemove.destroy(); // 画像を破棄
}
if (this.currentHealth === 0) {
this.player.setVisible(false);
}
}
}
private enemyShoot() {
// 生き残っている敵をすべて取得
const livingEnemies = this.enemies.getChildren() as Phaser.Physics.Arcade.Sprite[];
if (livingEnemies.length > 0) {
//ランダムに1体選ぶ
const shooter = Phaser.Utils.Array.GetRandom(livingEnemies);
const randomInt = Phaser.Math.Between(2, 58);
// 弾を発射
const bullet = this.enemyBullets.get(shooter.x+randomInt, shooter.y, 'bullet2');
if (bullet) {
bullet.setScale(0.3);
bullet.setActive(true);
bullet.setVisible(true);
bullet.body.velocity.y = 500; // 下方向に発射
}
}
}
shoot() {
let bullet = this.bullets.get(this.player.x, this.player.y + 5);
if (bullet) {
bullet.setActive(true);
bullet.setVisible(true);
bullet.body.velocity.y = -500;
// 2000ミリ秒(2秒)後に弾を消滅させる
this.time.delayedCall(2000, () => {
if (bullet.active) {
bullet.destroy(); // または bullet.disableBody(true, true);
}
});
}
}
update() {
if (this.cursors?.left.isDown || this.joystick.left) {
this.player.setVelocityX(-200);
}
else if (this.cursors?.right.isDown || this.joystick.right) {
this.player.setVelocityX(200);
}
else {
this.player.setVelocityX(0);
// this.player.setVelocityY(0);
}
}
}