diff --git a/assets/resources/game/gui/materials/outline-pur.mtl b/assets/resources/game/gui/materials/outline-pur.mtl new file mode 100644 index 00000000..8d17cb95 --- /dev/null +++ b/assets/resources/game/gui/materials/outline-pur.mtl @@ -0,0 +1,41 @@ +{ + "__type__": "cc.Material", + "_name": "", + "_objFlags": 0, + "__editorExtras__": {}, + "_native": "", + "_effectAsset": { + "__uuid__": "22f6acfb-c03d-4213-918a-4d3b0cce76b4", + "__expectedType__": "cc.EffectAsset" + }, + "_techIdx": 0, + "_defines": [ + { + "USE_TEXTURE": true + } + ], + "_states": [ + { + "rasterizerState": {}, + "depthStencilState": {}, + "blendState": { + "targets": [ + {} + ] + } + } + ], + "_props": [ + { + "glowColor": { + "__type__": "cc.Color", + "r": 130, + "g": 46, + "b": 255, + "a": 255 + }, + "glowWidth": 0.002, + "glowThreshold": 0.887 + } + ] +} \ No newline at end of file diff --git a/assets/resources/game/gui/materials/outline-pur.mtl.meta b/assets/resources/game/gui/materials/outline-pur.mtl.meta new file mode 100644 index 00000000..caade83e --- /dev/null +++ b/assets/resources/game/gui/materials/outline-pur.mtl.meta @@ -0,0 +1 @@ +{"ver":"1.0.21","importer":"material","imported":true,"uuid":"acf230af-e9ae-42f8-80a1-552d7d10390f","files":[".json"],"subMetas":{},"userData":{}} diff --git a/assets/resources/game/gui/shaders/builtin-sprite-outline-pur.effect b/assets/resources/game/gui/shaders/builtin-sprite-outline-pur.effect new file mode 100644 index 00000000..beb5620f --- /dev/null +++ b/assets/resources/game/gui/shaders/builtin-sprite-outline-pur.effect @@ -0,0 +1,169 @@ +// Copyright (c) 2017-2020 Xiamen Yaji Software Co., Ltd. +CCEffect %{ + techniques: + - passes: + - vert: sprite-vs:vert + frag: sprite-fs:frag + depthStencilState: + depthTest: false + depthWrite: false + blendState: + targets: + - blend: true + blendSrc: src_alpha + blendDst: one_minus_src_alpha + blendDstAlpha: one_minus_src_alpha + rasterizerState: + cullMode: none + properties: + alphaThreshold: { value: 0.5 } + + glowColor: { value: [1, 1, 1, 1], editor: { type: color } } + glowWidth: { value: 0.05, editor: { slide: true, range: [0, 0.3], step: 0.001 } } + glowThreshold: { value: 1, editor: { slide: true, range: [0, 1], step: 0.001 } } +}% + +CCProgram sprite-vs %{ + precision highp float; + #include + #if USE_LOCAL + #include + #endif + #if SAMPLE_FROM_RT + #include + #endif + in vec3 a_position; + in vec2 a_texCoord; + in vec4 a_color; + + out vec4 color; + out vec2 uv0; + + vec4 vert () { + vec4 pos = vec4(a_position, 1); + + #if USE_LOCAL + pos = cc_matWorld * pos; + #endif + + #if USE_PIXEL_ALIGNMENT + pos = cc_matView * pos; + pos.xyz = floor(pos.xyz); + pos = cc_matProj * pos; + #else + pos = cc_matViewProj * pos; + #endif + + uv0 = a_texCoord; + #if SAMPLE_FROM_RT + CC_HANDLE_RT_SAMPLE_FLIP(uv0); + #endif + color = a_color; + + return pos; + } +}% + +CCProgram sprite-fs %{ + precision highp float; + #include + #include + + in vec4 color; + + uniform FSConstants { + vec4 glowColor; + float glowWidth; + float glowThreshold; + }; + + #if USE_TEXTURE + in vec2 uv0; + #pragma builtin(local) + layout(set = 2, binding = 12) uniform sampler2D cc_spriteTexture; + #endif + + vec4 getTextureColor (sampler2D mainTexture, vec2 uv) { + if (uv.x > 1.0 || uv.x < 0.0 || uv.y > 1.0 || uv.y < 0.0) { + return vec4(0.0, 0.0, 0.0, 0.0); + } + return texture(mainTexture, uv); + } + + float getColorAlpha (float angle, float dist) { + // 角度转弧度,公式为:弧度 = 角度 * (pi / 180) + float radian = angle * 3.14 / 180.0; + vec2 newUV = uv0 + vec2(dist * cos(radian), dist * sin(radian)); + vec4 color = getTextureColor(cc_spriteTexture, newUV); + return color.a; + } + + float getAverageAlpha (float dist) { + float totalAlpha = 0.0; + + totalAlpha += getColorAlpha(0.0, dist); + totalAlpha += getColorAlpha(30.0, dist); + totalAlpha += getColorAlpha(60.0, dist); + totalAlpha += getColorAlpha(90.0, dist); + totalAlpha += getColorAlpha(120.0, dist); + totalAlpha += getColorAlpha(150.0, dist); + totalAlpha += getColorAlpha(180.0, dist); + totalAlpha += getColorAlpha(210.0, dist); + totalAlpha += getColorAlpha(240.0, dist); + totalAlpha += getColorAlpha(270.0, dist); + totalAlpha += getColorAlpha(300.0, dist); + totalAlpha += getColorAlpha(330.0, dist); + + return totalAlpha * 0.0833; + } + + float getGlowAlpha () { + if (glowWidth == 0.0 ) { + return 0.0; + } + + float totalAlpha = 0.0; + totalAlpha += getAverageAlpha(glowWidth * 0.1); + totalAlpha += getAverageAlpha(glowWidth * 0.2); + totalAlpha += getAverageAlpha(glowWidth * 0.3); + totalAlpha += getAverageAlpha(glowWidth * 0.4); + totalAlpha += getAverageAlpha(glowWidth * 0.5); + totalAlpha += getAverageAlpha(glowWidth * 0.6); + totalAlpha += getAverageAlpha(glowWidth * 0.7); + totalAlpha += getAverageAlpha(glowWidth * 0.8); + totalAlpha += getAverageAlpha(glowWidth * 0.9); + totalAlpha += getAverageAlpha(glowWidth * 1.0); + + return totalAlpha * 0.1; + } + + vec4 frag () { + vec4 o = vec4(1, 1, 1, 1); + + #if USE_TEXTURE + o *= CCSampleWithAlphaSeparated(cc_spriteTexture, uv0); + #if IS_GRAY + float gray = 0.2126 * o.r + 0.7152 * o.g + 0.0722 * o.b; + o.r = o.g = o.b = gray; + #endif + #endif + + float alpha = getGlowAlpha(); + + if (alpha <= glowThreshold) { + alpha /= glowThreshold; + alpha = -1.0 * (alpha - 1.0) * (alpha - 1.0) * (alpha - 1.0) * (alpha - 1.0) + 1.0; + } else { + alpha = 0.0; + } + + vec4 dstColor = glowColor * alpha; + vec4 scrColor = o; + + o = scrColor * scrColor.a + dstColor * (1.0 - scrColor.a); + + o *= color; + ALPHA_TEST(o); + return o; + } +}% diff --git a/assets/resources/game/gui/shaders/builtin-sprite-outline-pur.effect.meta b/assets/resources/game/gui/shaders/builtin-sprite-outline-pur.effect.meta new file mode 100644 index 00000000..0ab87a1d --- /dev/null +++ b/assets/resources/game/gui/shaders/builtin-sprite-outline-pur.effect.meta @@ -0,0 +1 @@ +{"ver":"1.7.1","importer":"effect","imported":true,"uuid":"22f6acfb-c03d-4213-918a-4d3b0cce76b4","files":[".json"],"subMetas":{},"userData":{"combinations":[{}]}} diff --git a/assets/resources/game/heros/ha1.prefab b/assets/resources/game/heros/ha1.prefab index f2d9c766..4188a306 100644 --- a/assets/resources/game/heros/ha1.prefab +++ b/assets/resources/game/heros/ha1.prefab @@ -292,6 +292,22 @@ "__uuid__": "8eee8ab1-fe48-4b22-b956-3f5c18fc4810", "__expectedType__": "cc.Material" }, + "outlineMatGreen": { + "__uuid__": "ded728b9-6dd0-4c37-9970-9745c62aa8bf", + "__expectedType__": "cc.Material" + }, + "outlineMatBlue": { + "__uuid__": "0f38817f-a8df-4547-8b37-e7ed29de8216", + "__expectedType__": "cc.Material" + }, + "outlineMatPurple": { + "__uuid__": "acf230af-e9ae-42f8-80a1-552d7d10390f", + "__expectedType__": "cc.Material" + }, + "outlineMatYellow": { + "__uuid__": "2b8a37ee-732c-4c5b-b6a4-136f3e581cde", + "__expectedType__": "cc.Material" + }, "_id": "" }, { diff --git a/assets/script/game/hero/HeroViewComp.ts b/assets/script/game/hero/HeroViewComp.ts index bfdb3b99..a0cfd40a 100644 --- a/assets/script/game/hero/HeroViewComp.ts +++ b/assets/script/game/hero/HeroViewComp.ts @@ -12,6 +12,7 @@ import { HeroAttrsComp } from "./HeroAttrsComp"; import { Tooltip } from "../skill/Tooltip"; import { timedCom } from "../skill/timedCom"; import { oneCom } from "../skill/oncend"; +import { FlashSprite } from "./hit-flash-white/scripts/FlashSprite"; const { ccclass, property } = _decorator; @@ -109,6 +110,12 @@ export class HeroViewComp extends CCComp { // 🔥 重置血条 UI 显示状态 if (this.model) { this.hp_show(); + + // 根据英雄模型中的等级数据设置描边 + const flashSprite = this.node.getComponent(FlashSprite); + if (flashSprite) { + flashSprite.setOutlineByLevel(this.model.lv); + } } } @@ -241,6 +248,14 @@ export class HeroViewComp extends CCComp { /** 升级特效 */ private lv_up() { this.spawnTimedFx("game/skill/buff/buff_lvup", this.node, 1.0); + + // 升级时同步更新描边 + if (this.model) { + const flashSprite = this.node.getComponent(FlashSprite); + if (flashSprite) { + flashSprite.setOutlineByLevel(this.model.lv); + } + } } /** 攻击力提升特效 */ 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 73c98cce..45645a09 100644 --- a/assets/script/game/hero/hit-flash-white/scripts/FlashSprite.ts +++ b/assets/script/game/hero/hit-flash-white/scripts/FlashSprite.ts @@ -5,24 +5,73 @@ const { ccclass, property } = _decorator; export class FlashSprite extends Component { @property(Material) - hitFlashMaterial: Material; - orginalFlashMaterial: Material; - sprite: Sprite; + hitFlashMaterial: Material | null = null; + + @property({ type: Material, tooltip: '绿色描边材质(2级)' }) + outlineMatGreen: Material | null = null; + + @property({ type: Material, tooltip: '蓝色描边材质(3级)' }) + outlineMatBlue: Material | null = null; + + @property({ type: Material, tooltip: '紫色描边材质(4级)' }) + outlineMatPurple: Material | null = null; + + @property({ type: Material, tooltip: '黄色描边材质(5级)' }) + outlineMatYellow: Material | null = null; + + orginalFlashMaterial: Material | null = null; + defaultMaterial: Material | null = null; // 缓存最原始的无描边材质 + sprite: Sprite | null = null; start() { this.sprite = this.node.getComponent(Sprite); - this.orginalFlashMaterial = this.sprite.getRenderMaterial(0); - + // 记录最初的默认材质(无描边) + if (this.sprite) { + this.defaultMaterial = this.sprite.getRenderMaterial(0); + if (!this.orginalFlashMaterial) { + this.orginalFlashMaterial = this.defaultMaterial; + } + } } - update(deltaTime: number) { - + /** + * 根据英雄等级设置描边材质 + * @param level 英雄等级 (1=无描边, 2=绿, 3=蓝, 4=紫, 5=黄) + */ + public setOutlineByLevel(level: number) { + if (!this.sprite) { + this.sprite = this.node.getComponent(Sprite); + } + + let targetMat: Material | null = null; + if (level === 2) { + targetMat = this.outlineMatGreen; + } else if (level === 3) { + targetMat = this.outlineMatBlue; + } else if (level === 4) { + targetMat = this.outlineMatPurple; + } else if (level >= 5) { + targetMat = this.outlineMatYellow; + } else { + // 1级或异常情况,使用最原始的无描边材质 + targetMat = this.defaultMaterial; + } + + if (targetMat && this.sprite) { + // 更新基础材质,确保闪白结束后恢复到这个描边材质 + this.orginalFlashMaterial = targetMat; + this.sprite.setSharedMaterial(targetMat, 0); + } } public clickFlash() { + if (!this.sprite || !this.hitFlashMaterial) return; + this.sprite.setSharedMaterial(this.hitFlashMaterial, 0); this.scheduleOnce(() => { - this.sprite.setSharedMaterial(this.orginalFlashMaterial, 0); + if (this.sprite && this.orginalFlashMaterial) { + this.sprite.setSharedMaterial(this.orginalFlashMaterial, 0); + } }, 0.1); } }