|
| 1 | +import Phaser from 'phaser'; |
| 2 | +import { BaseSummon, type ISummonInitData } from './BaseSummon'; |
| 3 | +import { VFXTextureKeys } from '../../config/AssetKeys'; |
| 4 | +import { PipelineID } from '../../managers/RenderManager'; |
| 5 | + |
| 6 | +export class SubaruTrail extends BaseSummon { |
| 7 | + private trailPlane?: Phaser.GameObjects.Plane; |
| 8 | + private baseVertices?: { x: number; y: number; z: number }[]; |
| 9 | + |
| 10 | + private readonly gridWidth = 8; |
| 11 | + private readonly gridHeight = 12; |
| 12 | + private readonly trailDisplayWidth = 100; |
| 13 | + private readonly trailDisplayHeight = 400; |
| 14 | + private readonly expandStrength = 0.6; |
| 15 | + |
| 16 | + constructor(scene: Phaser.Scene, x: number, y: number) { |
| 17 | + super(scene, x, y, VFXTextureKeys.VfxTrail); |
| 18 | + } |
| 19 | + |
| 20 | + protected onStart(_data: ISummonInitData): void { |
| 21 | + this.setVisible(false); |
| 22 | + this.setAlpha(0); |
| 23 | + |
| 24 | + if (this.trailPlane) { |
| 25 | + this.trailPlane.destroy(); |
| 26 | + } |
| 27 | + |
| 28 | + this.trailPlane = this.scene.add.plane( |
| 29 | + this.x, |
| 30 | + this.y, |
| 31 | + VFXTextureKeys.VfxTrail, |
| 32 | + undefined, |
| 33 | + this.gridWidth, |
| 34 | + this.gridHeight, |
| 35 | + false |
| 36 | + ); |
| 37 | + |
| 38 | + this.trailPlane.setDisplaySize(this.trailDisplayWidth, this.trailDisplayHeight); |
| 39 | + this.trailPlane.setPipeline(PipelineID.SubaruTrail); |
| 40 | + this.trailPlane.setBlendMode(Phaser.BlendModes.ADD); |
| 41 | + this.trailPlane.setTint(0x004422); |
| 42 | + this.trailPlane.setAlpha(1); |
| 43 | + this.trailPlane.ignoreDirtyCache = true; |
| 44 | + |
| 45 | + // Plane origin fixed at center; shift so top aligns to this.y |
| 46 | + this.trailPlane.setPosition(this.x, this.y + this.trailDisplayHeight / 2); |
| 47 | + |
| 48 | + this.captureBaseVertices(); |
| 49 | + this.applyCurveShape(); |
| 50 | + } |
| 51 | + |
| 52 | + private captureBaseVertices() { |
| 53 | + if (!this.trailPlane) return; |
| 54 | + this.baseVertices = this.trailPlane.vertices.map((v) => ({ x: v.x, y: v.y, z: v.z })); |
| 55 | + } |
| 56 | + |
| 57 | + // 中上部分外展 |
| 58 | + private applyCurveShape() { |
| 59 | + if (!this.trailPlane || !this.baseVertices) return; |
| 60 | + |
| 61 | + const verts = this.trailPlane.vertices; |
| 62 | + let minY = Infinity; |
| 63 | + let maxY = -Infinity; |
| 64 | + |
| 65 | + for (const v of this.baseVertices) { |
| 66 | + if (v.y < minY) minY = v.y; |
| 67 | + if (v.y > maxY) maxY = v.y; |
| 68 | + } |
| 69 | + |
| 70 | + const range = Math.max(1, maxY - minY); |
| 71 | + |
| 72 | + for (let i = 0; i < verts.length; i++) { |
| 73 | + const base = this.baseVertices[i]; |
| 74 | + const yNorm = (base.y - minY) / range; // 0 top, 1 bottom |
| 75 | + |
| 76 | + const rise = Phaser.Math.Clamp((yNorm - 0.12) / 0.22, 0, 1); |
| 77 | + const fall = Phaser.Math.Clamp((0.58 - yNorm) / 0.26, 0, 1); |
| 78 | + const bulge = rise * fall; |
| 79 | + |
| 80 | + const bottomTaper = Phaser.Math.Clamp((yNorm - 0.7) / 0.3, 0, 1); |
| 81 | + const taper = 1 - bottomTaper * 0.3; |
| 82 | + |
| 83 | + const expand = 1 + bulge * this.expandStrength; |
| 84 | + const factor = expand * taper; |
| 85 | + |
| 86 | + verts[i].x = base.x * factor; |
| 87 | + verts[i].y = base.y; |
| 88 | + verts[i].z = base.z; |
| 89 | + } |
| 90 | + } |
| 91 | + |
| 92 | + protected onUpdate(_dt: number): void { |
| 93 | + if (this.target && this.target.active) { |
| 94 | + const offsetY = this.target.height * 0.5; |
| 95 | + this.setPosition(this.target.x, this.target.y + offsetY); |
| 96 | + } |
| 97 | + |
| 98 | + if (this.trailPlane) { |
| 99 | + this.trailPlane.setPosition(this.x, this.y + this.trailDisplayHeight / 2); |
| 100 | + } |
| 101 | + } |
| 102 | + |
| 103 | + protected onDespawn(): void { |
| 104 | + if (this.trailPlane) { |
| 105 | + this.scene.tweens.add({ |
| 106 | + targets: this.trailPlane, |
| 107 | + alpha: 0, |
| 108 | + duration: 300, |
| 109 | + onComplete: () => { |
| 110 | + this.trailPlane?.destroy(); |
| 111 | + this.trailPlane = undefined; |
| 112 | + this.baseVertices = undefined; |
| 113 | + this.kill(); |
| 114 | + } |
| 115 | + }); |
| 116 | + } else { |
| 117 | + this.kill(); |
| 118 | + } |
| 119 | + } |
| 120 | +} |
0 commit comments