From a122d0904c68fcb15688e3545483b6db38db7ed0 Mon Sep 17 00:00:00 2001 From: panFD Date: Sat, 10 Oct 2026 00:33:41 +0800 Subject: [PATCH] feat(boss): tint boss sprite outline by phase (P2 green, P3 red) Add FlashSprite.setOutlineByBossPhase with light/dark green shade option and red fallback; BossPhaseSystem drives HeroViewComp.applyBossPhaseOutline directly after phase switch (MON faction, no hero-level event semantics, pool-node safe); extract getFlashSprite/applyOutlineByLevel to dedupe the three outline call sites. --- assets/script/game/hero/BossPhaseSystem.ts | 8 ++++ assets/script/game/hero/HeroViewComp.ts | 45 ++++++++++++------- .../hit-flash-white/scripts/FlashSprite.ts | 40 +++++++++++++++++ 3 files changed, 76 insertions(+), 17 deletions(-) diff --git a/assets/script/game/hero/BossPhaseSystem.ts b/assets/script/game/hero/BossPhaseSystem.ts index 53d3dea0..6b2631bf 100644 --- a/assets/script/game/hero/BossPhaseSystem.ts +++ b/assets/script/game/hero/BossPhaseSystem.ts @@ -19,6 +19,7 @@ import { SkillSet } from "../common/config/SkillSet"; import { mLogger } from "../common/Logger"; import { HeroAttrsComp } from "./HeroAttrsComp"; import { BossPhaseComp } from "./BossPhaseComp"; +import { HeroViewComp } from "./HeroViewComp"; /** 阶段模块调试日志开关 */ const DEBUG = true; @@ -119,6 +120,13 @@ export class BossPhaseSystem extends ecs.ComblockSystem implements ecs.ISystemUp fromPhase: from, toPhase: phase.index, }); + + // 直接驱动描边:Boss 是 MON 阵营,不走英雄 HeroLvUp 语义; + // 且对象池节点上挂全局事件监听易漏注销,直接调视图组件最稳 + const view = e.get(HeroViewComp); + if (view && view.node && view.node.isValid) { + view.applyBossPhaseOutline(phase.index); + } } /** TODO: 阶段退场表现(震屏/变暗等),后续迭代实现 */ diff --git a/assets/script/game/hero/HeroViewComp.ts b/assets/script/game/hero/HeroViewComp.ts index ad23fd2c..cb2c4ad3 100644 --- a/assets/script/game/hero/HeroViewComp.ts +++ b/assets/script/game/hero/HeroViewComp.ts @@ -99,15 +99,7 @@ export class HeroViewComp extends CCComp { this.node.setScale(this.scale * Math.abs(this.node.scale.x), 1 * this.node.scale.y); // 确保 scale.x 为正后再乘方向 // 🔥 重置描边 - if (this.model) { - let flashSprite = this.node.getComponent(FlashSprite); - if (!flashSprite) { - flashSprite = this.node.getComponentInChildren(FlashSprite); - } - if (flashSprite) { - flashSprite.setOutlineByLevel(this.model.lv); - } - } + this.applyOutlineByLevel(); } /** @@ -171,17 +163,36 @@ export class HeroViewComp extends CCComp { this.spawnTimedFx("game/skill/buff/buff_lvup", this.node, 1.0); // 升级时同步更新描边 - if (this.model) { - let flashSprite = this.node.getComponent(FlashSprite); - if (!flashSprite) { - flashSprite = this.node.getComponentInChildren(FlashSprite); - } - if (flashSprite) { - flashSprite.setOutlineByLevel(this.model.lv); - } + this.applyOutlineByLevel(); + } + + /** 按英雄等级应用描边(升级/初始化共用入口) */ + private applyOutlineByLevel() { + if (!this.model) return; + const flashSprite = this.getFlashSprite(); + if (flashSprite) { + flashSprite.setOutlineByLevel(this.model.lv); } } + /** + * 按 Boss 阶段应用描边(P2 绿 / P3 红) + * Why: 由 BossPhaseSystem 直接调用,不走英雄 HeroLvUp 事件语义 + * (Boss 是 MON 阵营,且对象池节点挂全局监听易漏注销) + * @param phase 阶段索引:1=P2 / 2=P3 + */ + public applyBossPhaseOutline(phase: number) { + const flashSprite = this.getFlashSprite(); + if (flashSprite) { + flashSprite.setOutlineByBossPhase(phase); + } + } + + /** 查找 FlashSprite 组件(优先自身,回退子节点),描边三处调用点统一入口 */ + private getFlashSprite(): FlashSprite | null { + return this.node.getComponent(FlashSprite) ?? this.node.getComponentInChildren(FlashSprite); + } + /** 攻击力提升特效 */ private ap_up() { this.spawnTimedFx("game/skill/buff/buff_apup", this.node, 1.0); diff --git a/assets/script/game/hero/hit-flash-white/scripts/FlashSprite.ts b/assets/script/game/hero/hit-flash-white/scripts/FlashSprite.ts index 45645a09..1d043e28 100644 --- a/assets/script/game/hero/hit-flash-white/scripts/FlashSprite.ts +++ b/assets/script/game/hero/hit-flash-white/scripts/FlashSprite.ts @@ -10,6 +10,12 @@ export class FlashSprite extends Component { @property({ type: Material, tooltip: '绿色描边材质(2级)' }) outlineMatGreen: Material | null = null; + @property({ type: Material, tooltip: '淡绿描边材质(Boss阶段2)' }) + outlineMatGreenLight: Material | null = null; + + @property({ type: Material, tooltip: '深绿描边材质(Boss阶段2备选)' }) + outlineMatGreenDark: Material | null = null; + @property({ type: Material, tooltip: '蓝色描边材质(3级)' }) outlineMatBlue: Material | null = null; @@ -19,6 +25,9 @@ export class FlashSprite extends Component { @property({ type: Material, tooltip: '黄色描边材质(5级)' }) outlineMatYellow: Material | null = null; + @property({ type: Material, tooltip: '红色描边材质(Boss阶段3)' }) + outlineMatRed: Material | null = null; + orginalFlashMaterial: Material | null = null; defaultMaterial: Material | null = null; // 缓存最原始的无描边材质 sprite: Sprite | null = null; @@ -64,6 +73,37 @@ export class FlashSprite extends Component { } } + /** + * 按 Boss 阶段设置描边材质(优先级高于等级描边) + * Why: Boss 阶段是战斗强反馈(绿色警告 / 红色危险),与英雄等级成长是两套视觉语言, + * 用独立通道避免污染 setOutlineByLevel 的等级语义 + * @param phase 阶段索引:1=P2 绿 / 2=P3 红 / 0=P1 恢复默认 + * @param greenShade P2 绿色色调:'light' 淡绿(默认)/ 'dark' 深绿 + */ + public setOutlineByBossPhase(phase: number, greenShade: 'light' | 'dark' = 'light') { + if (!this.sprite) { + this.sprite = this.node.getComponent(Sprite); + } + if (!this.sprite) return; + + let targetMat: Material | null = null; + if (phase === 1) { + targetMat = greenShade === 'dark' + ? (this.outlineMatGreenDark ?? this.outlineMatGreenLight ?? this.outlineMatGreen) + : (this.outlineMatGreenLight ?? this.outlineMatGreenDark ?? this.outlineMatGreen); + } else if (phase >= 2) { + targetMat = this.outlineMatRed ?? this.outlineMatYellow; + } else { + targetMat = this.defaultMaterial; + } + + if (targetMat) { + // 更新基础材质,确保闪白结束后恢复到这个阶段描边材质 + this.orginalFlashMaterial = targetMat; + this.sprite.setSharedMaterial(targetMat, 0); + } + } + public clickFlash() { if (!this.sprite || !this.hitFlashMaterial) return;