|
| 1 | +package components |
| 2 | + |
| 3 | +import ( |
| 4 | + "fmt" |
| 5 | + "image/color" |
| 6 | +) |
| 7 | + |
| 8 | +// VisualEffect はビジュアルエフェクトのインターフェース |
| 9 | +type VisualEffect interface { |
| 10 | + // Update はエフェクトを更新し、継続中ならtrueを返す |
| 11 | + Update(deltaMs float64) bool |
| 12 | +} |
| 13 | + |
| 14 | +// VisualEffects はエンティティに紐づくビジュアルエフェクトを管理する |
| 15 | +type VisualEffects struct { |
| 16 | + Effects []VisualEffect |
| 17 | +} |
| 18 | + |
| 19 | +// ScreenTextEffect は画面座標でテキストを表示するエフェクト |
| 20 | +type ScreenTextEffect struct { |
| 21 | + OffsetX float64 // X座標(画面座標) |
| 22 | + OffsetY float64 // Y座標(画面座標) |
| 23 | + Text string // 表示テキスト |
| 24 | + Color color.RGBA // 表示色 |
| 25 | + FadeInMs float64 // フェードイン時間(ミリ秒) |
| 26 | + HoldMs float64 // 表示維持時間(ミリ秒) |
| 27 | + FadeOutMs float64 // フェードアウト時間(ミリ秒) |
| 28 | + TotalMs float64 // 合計時間 |
| 29 | + RemainingMs float64 // 残り時間(ミリ秒) |
| 30 | + Alpha float64 // 現在の透明度(0.0-1.0) |
| 31 | +} |
| 32 | + |
| 33 | +// Update はエフェクトを更新し、継続中ならtrueを返す |
| 34 | +func (e *ScreenTextEffect) Update(deltaMs float64) bool { |
| 35 | + e.RemainingMs -= deltaMs |
| 36 | + elapsed := e.TotalMs - e.RemainingMs |
| 37 | + e.Alpha = calculateFadeAlpha(elapsed, e.FadeInMs, e.HoldMs, e.FadeOutMs) |
| 38 | + return e.RemainingMs > 0 |
| 39 | +} |
| 40 | + |
| 41 | +// DamageTextEffect はエンティティ座標で浮遊テキストを表示するエフェクト |
| 42 | +// ダメージ、回復、ミス、ヒットなどに使用する |
| 43 | +type DamageTextEffect struct { |
| 44 | + OffsetX float64 // X座標オフセット(ピクセル) |
| 45 | + OffsetY float64 // Y座標オフセット(ピクセル) |
| 46 | + Text string // 表示テキスト |
| 47 | + Color color.RGBA // 表示色 |
| 48 | + VelocityY float64 // Y方向の速度(上に浮かぶ効果用) |
| 49 | + FadeInMs float64 // フェードイン時間(ミリ秒) |
| 50 | + HoldMs float64 // 表示維持時間(ミリ秒) |
| 51 | + FadeOutMs float64 // フェードアウト時間(ミリ秒) |
| 52 | + TotalMs float64 // 合計時間 |
| 53 | + RemainingMs float64 // 残り時間(ミリ秒) |
| 54 | + Alpha float64 // 現在の透明度(0.0-1.0) |
| 55 | +} |
| 56 | + |
| 57 | +// Update はエフェクトを更新し、継続中ならtrueを返す |
| 58 | +func (e *DamageTextEffect) Update(deltaMs float64) bool { |
| 59 | + e.RemainingMs -= deltaMs |
| 60 | + e.OffsetY += e.VelocityY |
| 61 | + elapsed := e.TotalMs - e.RemainingMs |
| 62 | + e.Alpha = calculateFadeAlpha(elapsed, e.FadeInMs, e.HoldMs, e.FadeOutMs) |
| 63 | + return e.RemainingMs > 0 |
| 64 | +} |
| 65 | + |
| 66 | +// SpriteFadeoutEffect はスプライトをフェードアウト表示するエフェクト |
| 67 | +// 敵撃破時などに使用する |
| 68 | +type SpriteFadeoutEffect struct { |
| 69 | + SpriteSheetName string // スプライトシート名 |
| 70 | + SpriteKey string // スプライトキー |
| 71 | + FadeInMs float64 // フェードイン時間(ミリ秒) |
| 72 | + HoldMs float64 // 表示維持時間(ミリ秒) |
| 73 | + FadeOutMs float64 // フェードアウト時間(ミリ秒) |
| 74 | + TotalMs float64 // 合計時間 |
| 75 | + RemainingMs float64 // 残り時間(ミリ秒) |
| 76 | + Alpha float64 // 現在の透明度(0.0-1.0) |
| 77 | +} |
| 78 | + |
| 79 | +// Update はエフェクトを更新し、継続中ならtrueを返す |
| 80 | +func (e *SpriteFadeoutEffect) Update(deltaMs float64) bool { |
| 81 | + e.RemainingMs -= deltaMs |
| 82 | + elapsed := e.TotalMs - e.RemainingMs |
| 83 | + e.Alpha = calculateFadeAlpha(elapsed, e.FadeInMs, e.HoldMs, e.FadeOutMs) |
| 84 | + return e.RemainingMs > 0 |
| 85 | +} |
| 86 | + |
| 87 | +// calculateFadeAlpha はフェードシーケンスのAlpha値を計算する |
| 88 | +func calculateFadeAlpha(elapsed, fadeInMs, holdMs, fadeOutMs float64) float64 { |
| 89 | + if elapsed < fadeInMs { |
| 90 | + // フェードイン中 |
| 91 | + if fadeInMs == 0 { |
| 92 | + return 1.0 |
| 93 | + } |
| 94 | + return elapsed / fadeInMs |
| 95 | + } |
| 96 | + if elapsed < fadeInMs+holdMs { |
| 97 | + // ホールド中 |
| 98 | + return 1.0 |
| 99 | + } |
| 100 | + // フェードアウト中 |
| 101 | + if fadeOutMs == 0 { |
| 102 | + return 0.0 |
| 103 | + } |
| 104 | + fadeOutElapsed := elapsed - fadeInMs - holdMs |
| 105 | + return 1.0 - (fadeOutElapsed / fadeOutMs) |
| 106 | +} |
| 107 | + |
| 108 | +// NewScreenTextEffect は画面中央にフェード表示するテキストエフェクトを作成する |
| 109 | +func NewScreenTextEffect(text string, screenW, screenH int) *ScreenTextEffect { |
| 110 | + fadeInMs := 500.0 |
| 111 | + holdMs := 2000.0 |
| 112 | + fadeOutMs := 500.0 |
| 113 | + totalMs := fadeInMs + holdMs + fadeOutMs |
| 114 | + |
| 115 | + return &ScreenTextEffect{ |
| 116 | + OffsetX: float64(screenW) / 2, |
| 117 | + OffsetY: float64(screenH) / 3, |
| 118 | + Text: text, |
| 119 | + Color: color.RGBA{255, 255, 255, 255}, |
| 120 | + FadeInMs: fadeInMs, |
| 121 | + HoldMs: holdMs, |
| 122 | + FadeOutMs: fadeOutMs, |
| 123 | + TotalMs: totalMs, |
| 124 | + RemainingMs: totalMs, |
| 125 | + Alpha: 0.0, |
| 126 | + } |
| 127 | +} |
| 128 | + |
| 129 | +// NewDungeonTitleEffect はダンジョンタイトル表示エフェクトを作成する |
| 130 | +func NewDungeonTitleEffect(dungeonName string, depth int, screenW, screenH int) *ScreenTextEffect { |
| 131 | + text := fmt.Sprintf("%s %dF", dungeonName, depth) |
| 132 | + return NewScreenTextEffect(text, screenW, screenH) |
| 133 | +} |
| 134 | + |
| 135 | +// NewDamageEffect はダメージ数値表示エフェクトを作成する |
| 136 | +func NewDamageEffect(damage int) *DamageTextEffect { |
| 137 | + totalMs := 800.0 |
| 138 | + return &DamageTextEffect{ |
| 139 | + OffsetX: 0, |
| 140 | + OffsetY: -8, |
| 141 | + Text: fmt.Sprintf("%d", damage), |
| 142 | + Color: color.RGBA{255, 80, 80, 255}, // 赤色 |
| 143 | + VelocityY: -0.5, // 上に浮かぶ |
| 144 | + FadeInMs: 100, |
| 145 | + HoldMs: 500, |
| 146 | + FadeOutMs: 200, |
| 147 | + TotalMs: totalMs, |
| 148 | + RemainingMs: totalMs, |
| 149 | + Alpha: 0.0, |
| 150 | + } |
| 151 | +} |
| 152 | + |
| 153 | +// NewMissEffect はミス表示エフェクトを作成する |
| 154 | +func NewMissEffect() *DamageTextEffect { |
| 155 | + totalMs := 600.0 |
| 156 | + return &DamageTextEffect{ |
| 157 | + OffsetX: 0, |
| 158 | + OffsetY: -8, |
| 159 | + Text: "MISS", |
| 160 | + Color: color.RGBA{180, 180, 180, 255}, // グレー |
| 161 | + VelocityY: -0.3, |
| 162 | + FadeInMs: 50, |
| 163 | + HoldMs: 400, |
| 164 | + FadeOutMs: 150, |
| 165 | + TotalMs: totalMs, |
| 166 | + RemainingMs: totalMs, |
| 167 | + Alpha: 0.0, |
| 168 | + } |
| 169 | +} |
| 170 | + |
| 171 | +// NewHealEffect は回復数値表示エフェクトを作成する |
| 172 | +func NewHealEffect(amount int) *DamageTextEffect { |
| 173 | + totalMs := 800.0 |
| 174 | + return &DamageTextEffect{ |
| 175 | + OffsetX: 0, |
| 176 | + OffsetY: -8, |
| 177 | + Text: fmt.Sprintf("+%d", amount), |
| 178 | + Color: color.RGBA{80, 255, 80, 255}, // 緑色 |
| 179 | + VelocityY: -0.5, |
| 180 | + FadeInMs: 100, |
| 181 | + HoldMs: 500, |
| 182 | + FadeOutMs: 200, |
| 183 | + TotalMs: totalMs, |
| 184 | + RemainingMs: totalMs, |
| 185 | + Alpha: 0.0, |
| 186 | + } |
| 187 | +} |
| 188 | + |
| 189 | +// NewSpriteFadeoutEffect はスプライトフェードアウトエフェクトを作成する |
| 190 | +// 敵撃破時などにスプライトを表示してフェードアウトする |
| 191 | +func NewSpriteFadeoutEffect(spriteSheetName, spriteKey string) *SpriteFadeoutEffect { |
| 192 | + totalMs := 400.0 |
| 193 | + return &SpriteFadeoutEffect{ |
| 194 | + SpriteSheetName: spriteSheetName, |
| 195 | + SpriteKey: spriteKey, |
| 196 | + FadeInMs: 0, |
| 197 | + HoldMs: 100, |
| 198 | + FadeOutMs: 300, |
| 199 | + TotalMs: totalMs, |
| 200 | + RemainingMs: totalMs, |
| 201 | + Alpha: 1.0, |
| 202 | + } |
| 203 | +} |
0 commit comments