diff --git a/apps/client/src/scenes/game/GameManager.ts b/apps/client/src/scenes/game/GameManager.ts index 06b088d..74e558c 100644 --- a/apps/client/src/scenes/game/GameManager.ts +++ b/apps/client/src/scenes/game/GameManager.ts @@ -189,6 +189,9 @@ }, onPlayerDeadFromNetwork: (payload) => { this.playerDeathPolicy.applyPlayerDeadEvent(payload); + if (payload.playerId !== this.myId) { + this.playBombHitBlink(payload.playerId); + } }, }); this.networkSync.bind(); @@ -243,10 +246,20 @@ } this.playerDeathPolicy.applyLocalHitStun(); + this.playBombHitBlink(this.myId); socketManager.game.sendBombHitReport({ bombId }); } + private playBombHitBlink(playerId: string): void { + const target = this.players[playerId]; + if (!target) { + return; + } + + target.playBombHitBlink(config.GAME_CONFIG.PLAYER_HIT_STUN_MS); + } + private shouldSendBombHitReport( result: BombHitEvaluationResult | undefined, bombId: string, diff --git a/apps/client/src/scenes/game/entities/bomb/BombHitBlinkRenderer.ts b/apps/client/src/scenes/game/entities/bomb/BombHitBlinkRenderer.ts new file mode 100644 index 0000000..570f959 --- /dev/null +++ b/apps/client/src/scenes/game/entities/bomb/BombHitBlinkRenderer.ts @@ -0,0 +1,65 @@ +/** + * BombHitBlinkRenderer + * 爆弾被弾時の点滅演出を描画オブジェクトへ適用する + * 点滅開始,停止,破棄を一元管理する + */ +import type { Container } from "pixi.js"; + +type BombHitBlinkRendererOptions = { + target: Container; + blinkIntervalMs?: number; + hiddenAlpha?: number; +}; + +/** 爆弾被弾時の点滅演出を制御するレンダラー */ +export class BombHitBlinkRenderer { + private readonly target: Container; + private readonly blinkIntervalMs: number; + private readonly hiddenAlpha: number; + private blinkIntervalId: ReturnType | null = null; + private stopTimeoutId: ReturnType | null = null; + private isVisibleFrame = true; + + constructor({ target, blinkIntervalMs = 100, hiddenAlpha = 0.2 }: BombHitBlinkRendererOptions) { + this.target = target; + this.blinkIntervalMs = blinkIntervalMs; + this.hiddenAlpha = hiddenAlpha; + } + + /** 指定時間だけ点滅演出を再生する */ + public play(durationMs: number): void { + this.stop(); + this.isVisibleFrame = true; + this.target.alpha = 1; + + this.blinkIntervalId = setInterval(() => { + this.isVisibleFrame = !this.isVisibleFrame; + this.target.alpha = this.isVisibleFrame ? 1 : this.hiddenAlpha; + }, this.blinkIntervalMs); + + this.stopTimeoutId = setTimeout(() => { + this.stop(); + }, durationMs); + } + + /** 点滅演出を停止し描画状態を初期化する */ + public stop(): void { + if (this.blinkIntervalId) { + clearInterval(this.blinkIntervalId); + this.blinkIntervalId = null; + } + + if (this.stopTimeoutId) { + clearTimeout(this.stopTimeoutId); + this.stopTimeoutId = null; + } + + this.isVisibleFrame = true; + this.target.alpha = 1; + } + + /** 管理中タイマーを破棄し演出を停止する */ + public destroy(): void { + this.stop(); + } +} \ No newline at end of file diff --git a/apps/client/src/scenes/game/entities/player/PlayerController.ts b/apps/client/src/scenes/game/entities/player/PlayerController.ts index 3bfda08..74bce98 100644 --- a/apps/client/src/scenes/game/entities/player/PlayerController.ts +++ b/apps/client/src/scenes/game/entities/player/PlayerController.ts @@ -5,6 +5,7 @@ */ import type { playerTypes } from "@repo/shared"; import { AppearanceResolver } from "@client/scenes/game/application/AppearanceResolver"; +import { BombHitBlinkRenderer } from "@client/scenes/game/entities/bomb/BombHitBlinkRenderer"; import { PlayerModel } from "./PlayerModel"; import { PlayerView } from "./PlayerView"; @@ -24,6 +25,7 @@ abstract class BasePlayerController { protected readonly model: PlayerModel; protected readonly view: PlayerView; + private readonly bombHitBlinkRenderer: BombHitBlinkRenderer; /** 共通初期化としてModelとViewを生成する */ protected constructor( @@ -37,6 +39,9 @@ data.name, isLocal, ); + this.bombHitBlinkRenderer = new BombHitBlinkRenderer({ + target: this.view.displayObject, + }); const pos = this.model.getPosition(); this.view.syncPosition(pos.x, pos.y); @@ -57,8 +62,14 @@ return this.model.getSnapshot(); } + /** 爆弾被弾時の点滅演出を再生する */ + public playBombHitBlink(durationMs: number): void { + this.bombHitBlinkRenderer.play(durationMs); + } + /** 管理中の描画リソースを破棄する */ public destroy(): void { + this.bombHitBlinkRenderer.destroy(); this.view.destroy(); } }