import * as Phaser from 'phaser';
import {Player} from './Player';
import { Bullet } from './Bullet';
import {ScoreText} from "./ScoreText";
import { HeartsManager } from './HeartsManager';
import { Boss2 } from './Boss2';
import { BossBullet } from './BossBullet';
export class GameScene extends Phaser.Scene {
private map!: Phaser.Tilemaps.Tilemap;
private tileset!: Phaser.Tilemaps.Tileset;
private tiles!: Phaser.Tilemaps.Tileset;
private Foreground!: Phaser.Tilemaps.TilemapLayer;
private player!: Player;
private joystick: any;
private bullets!: Phaser.Physics.Arcade.Group;
private rexVirtualJoystick: any;
private joystickArea!: Phaser.Geom.Rectangle;
private lastDirection: Phaser.Math.Vector2 = new Phaser.Math.Vector2(1, 0);
private scoreText!: ScoreText;
private heartsManager!: HeartsManager;
private hitCount: number = 0;
private isGameOver: boolean = false;
private boss: Boss2 | null = null;
private playerHp: number = 5;
constructor() {
super({ key: 'GameScene' });
}
preload() {
this.load.tilemapTiledJSON('map', 'assets/tilemap.json');
this.load.image('tile', 'assets/tilesets.png');
this.load.spritesheet('player', 'assets/player.png', {
frameWidth: 64, frameHeight: 64
});
this.load.image('shoot', 'assets/myshoot.png');
this.load.image('bullet', 'assets/bullet.png');
this.load.image('heart', 'assets/heart.png');
this.load.image('boss', 'assets/boss.png');
this.load.image('bossbullet', 'assets/laser.png');
}
create() {
this.scoreText = new ScoreText(this, 20, 20);
this.scoreText.setScrollFactor(0);
this.scoreText.setDepth(3);
// スコア表示
const currentScore = this.registry.get('playerScore');
this.hitCount = 0;
this.isGameOver = false;
if (!this.registry.has('playerLife')) {
this.registry.set('playerLife', 10); // 初期ライフ
}
const maxLife = 20; // 最大ライフ枠数
const currentLife = this.registry.get('playerLife');
// ハートクラスのインスタンス化 (X: 20, Y: 20 の位置に配置)
this.heartsManager = new HeartsManager(this, 500, 20, maxLife, 'heart');
this.heartsManager.setDepth(3);
this.heartsManager.updateHearts(currentLife);
const map = this.make.tilemap({key: 'map'});
this.tiles = map.addTilesetImage('tileset', 'tile') as Phaser.Tilemaps.Tileset;
const ground = map.createLayer('ground', this.tiles, 0, 0) as Phaser.Tilemaps.TilemapLayer;
const Background = map.createLayer('Background', this.tiles, 0, 0) as Phaser.Tilemaps.TilemapLayer;
this.Foreground = map.createLayer('Foreground', this.tiles, 0, 0) as Phaser.Tilemaps.TilemapLayer;
// ground.setCollisionByProperty({ collides: true });
ground.setCollisionByExclusion([-1], true);
this.Foreground.setCollisionByExclusion([-1], true);
this.player = new Player(this, 790, 660, 'player');
this.physics.add.collider(this.player, ground);
this.physics.add.collider(this.player, this.Foreground);
this.boss = new Boss2(this, 100, 400);
// ボスの弾がプレイヤーに当たった時の処理
this.physics.add.overlap(
this.player,
this.boss.getBullets(),
this.handlePlayerDamage,
undefined,
this
);
this.joystickArea = new Phaser.Geom.Rectangle(0, 0, 700, 1000);
const graphics = this.add.graphics();
graphics.lineStyle(2, 0x00ff00, 0);
graphics.strokeRectShape(this.joystickArea);
// 1. ジョイスティックの作成
this.joystick = (this.plugins.get('rexVirtualJoystick') as any).add(this, {
x: 100,
y: 400,
radius: 100,
base: this.add.circle(0, 0, 100, 0x0000FF).setAlpha(0.1),
thumb: this.add.circle(0, 0, 50, 0xFFA500).setAlpha(0.5),
dir: '8dir', // 8方向入力
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.joystick.on('pointerup', () => {
this.player.setVelocity(0, 0);
this.player.anims.stop(); // アニメーションを停止
// this.player.setFrame(0);
});
// 指を離した時に非表示にする場合
this.input.on('pointerup', () => {
this.joystick.setVisible(false);
});
this.input.addPointer(2);
const jumpButton = this.add.image(850, 880, 'shoot').setScale(0.6).setAlpha(0.5);
jumpButton.setInteractive().setScrollFactor(0);
jumpButton.on("pointerdown", () => {
this.shootBullet();
});
jumpButton.on("pointerup", () => {
// this.shoot();
});
this.bullets = this.physics.add.group({
classType: Bullet,
maxSize: 50, // 画面内の最大弾数
runChildUpdate: true
});
this.physics.add.collider(this.bullets, this.boss.getBullets(), (bullet, bulletObj) => {
// 型アサーション(キャスト)を使ってTypeScriptに型を伝える
(bulletObj as Phaser.Physics.Arcade.Sprite).disableBody(true, true);
});
this.physics.add.collider(this.bullets, this.Foreground, (bullet, tileObj) => {
// 型アサーション(キャスト)を使ってTypeScriptに型を伝える
const tile = tileObj as Phaser.Tilemaps.Tile;
// 衝突したタイルの座標を指定して削除
if (tile) {
this.Foreground.removeTileAt(tile.x, tile.y);
this.scoreText.addScore(10);
// 必要に応じて弾(bullet)も消去
bullet.destroy();
}
});
this.physics.add.overlap(
this.bullets,
this.boss,
(bossObj, bulletObj) => {
const bullet = bulletObj as Bullet;
const boss = bossObj as Boss2;
bullet.setActive(false);
bullet.setVisible(false);
bullet.body!.enable = false;
// bullet.disableBody(true, true); // 弾を消す
boss.takeDamage();
},
undefined,
this
);
}
private handlePlayerDamage(playerObj: any, bulletObj: any) {
const bullet = bulletObj as BossBullet;
let life = this.registry.get('playerLife');
if (life > 0) {
life--;
this.registry.set('playerLife', life); // registryを更新
this.heartsManager.updateHearts(life); // 見た目を更新
}
if (life === 0) {
this.add.text(450, 240, 'GAME OVER',
{fontSize: '80px', color: '#ff0000'}).setOrigin(0.5).setScrollFactor(0);
this.time.delayedCall(3000, () => {
this.registry.set('playerLife', 10);
this.registry.set('score', 0);
this.scene.start('GameScene');
});
}
// 弾を消滅させる
bullet.destroy();
this.player.setAlpha(0.5);
this.time.delayedCall(500, () => this.player.setAlpha(1));
}
// プレイヤーの被弾処理
private damagePlayer() {
this.playerHp--;
this.player.setTint(0xff0000);
this.time.delayedCall(100, () => this.player.clearTint());
if (this.playerHp <= 0) {
this.player.disableBody(true, true);
console.log("ゲームオーバー");
}
}
private triggerGameOver(): void {
this.isGameOver = true;
this.physics.pause(); // 物理演算を停止
this.player.setTint(0x333333); // プレイヤーを灰色の死亡状態にする
// ゲームオーバーテキスト
this.add.text(450, 300, 'GAME OVER', {
fontSize: '74px',
color: '#ff0000',
backgroundColor: '#000000'
}).setOrigin(0.5);
}
private shootBullet() {
// プールから非アクティブな弾を取得
const bullet = this.bullets.get(this.player.x, this.player.y).setScale(0.15) as Bullet;
if (bullet) {
bullet.setDepth(3);
// if (this.lastDirection.x!== 0 && this.lastDirection.y!== 0) {
// return; // 斜め45度方向には弾を飛ばさない
// }
// プレイヤーの現在位置から、最後に記憶した方向に向けて発射
bullet.fire(this.player.x-2, this.player.y-3, this.lastDirection);
const angle = Math.atan2(this.lastDirection.y, this.lastDirection.x);
// 3. 弾のオブジェクトの回転角度を設定(※弾の元画像が「右向き」であることが前提)
bullet.setRotation(angle);
}
}
update() {
if (this.boss) {
this.boss.update();
}
const cursors = this.joystick.createCursorKeys();
const moveDirection = new Phaser.Math.Vector2(0, 0);
this.player.setVelocity(0);
const speed = 180;
let vX = 0, vY = 0;
// 水平方向の移動
if (cursors.left.isDown) {
vX = -speed;
moveDirection.x = -1;
}
else if (cursors.right.isDown) {
vX = speed;
moveDirection.x = 1;
}
// 垂直方向の移動
if (cursors.up.isDown ) {
vY = -speed;
moveDirection.y = -1;
} else if (cursors.down.isDown) {
vY = speed;
moveDirection.y = 1;
}
if (moveDirection.length() > 0) {
moveDirection.normalize();
// 移動しているので、その方向を最新の向きとして記憶
this.lastDirection.copy(moveDirection);
}
// プレイヤーが斜め方向に速く移動できないように速度を設定して正規化します
// this.player.setVelocity(vX, vY).body.velocity.normalize().scale(speed);
const body = this.player.body as Phaser.Physics.Arcade.Body;
this.player.setVelocity(vX, vY);
body.velocity.normalize().scale(speed);
// 適切なプレイヤーのアニメーションを選択する
if (vX < 0) {
this.player.anims.play('left', true);
this.player.setDepth(4);
}
else if (vX > 0){
this.player.anims.play('right', true);
this.player.setDepth(4);
}
else if (vY < 0){
this.player.anims.play('up', true);
this.player.setDepth(4);
}
else if (vY > 0){
this.player.anims.play('down', true);
this.player.setDepth(2);
}
}
}