Invader


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);
}
 }
}