From e62eecd21458e84b6c38e8e03624f3a1926f2552 Mon Sep 17 00:00:00 2001 From: walkpan Date: Sat, 25 Oct 2025 15:29:25 +0800 Subject: [PATCH] =?UTF-8?q?refactor(buff=E7=B3=BB=E7=BB=9F):=20=E9=87=8D?= =?UTF-8?q?=E6=9E=84=E8=8B=B1=E9=9B=84=E7=9A=84buff=E7=AE=A1=E7=90=86?= =?UTF-8?q?=E9=80=BB=E8=BE=91=EF=BC=8C=E6=94=AF=E6=8C=81=E5=A4=9A=E6=AC=A1?= =?UTF-8?q?=E5=8F=A0=E5=8A=A0=E5=92=8C=E4=B8=B4=E6=97=B6=E6=95=88=E6=9E=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 统一管理持久型和临时型buff,简化buff的添加和移除逻辑 - 更新buff的叠加规则,允许同一属性的多个buff实例共存 - 优化属性计算公式,确保所有buff在计算时被纳入考虑 - 新增清空buff和移除特定buff的辅助方法,提升管理灵活性 - 详细更新文档,提供API使用示例和数据结构说明 --- assets/script/game/hero/HeroViewComp.ts | 292 ++++++++++++++++-------- 1 file changed, 200 insertions(+), 92 deletions(-) diff --git a/assets/script/game/hero/HeroViewComp.ts b/assets/script/game/hero/HeroViewComp.ts index d58e02ab..8592c61c 100644 --- a/assets/script/game/hero/HeroViewComp.ts +++ b/assets/script/game/hero/HeroViewComp.ts @@ -18,17 +18,50 @@ const { ccclass, property } = _decorator; /** * ==================== BUFF 系统使用说明 ==================== * - * 1. 系统架构 - * - BUFF_V/BUFFS_V: 数值型 buff(持临时 - * - BUFF_R/BUFFS_R: 百分比型 buff(持临时 - * - DBUFF_V/DBUFFS_V: 数值型 debuff(持临时 - * - DBUFF_R/DBUFFS_R: 百分比型 debuff(持临时 + * 1. 系统架构 - 支持多次叠加(简化设计) + * - BUFFS: 持久型buff数组(持久存在,直到主动移除) + * - BUFFS_TEMP: 临时型buff数组(持续指定时间,时间到自动移除) + * - 每个buff实例内包含BType字段,区分数值型(VALUE)和百分比型(RATIO) * - * 2. 智能覆盖规则 - * - 值更小:不添加(弱效果不覆盖强效果) - * - 值相同且临时:叠加时 - * - 值更大:更新为新值(临时则更新值和时间 + * 2. 叠加规则 - 核心特性 + * - 同一属性允许多个buff实例同时存在(数值型和百分比型混合) + * - 每个buff实例保持独立的数据(value、BType、remainTime) + * - 所有buff实例在属性计算时都被纳入求和计算 + * - 示例:若添加两个+10 AP的buff,最终会累加 +20 AP * + * 3. 数据结构 - 统一数组设计 + * - BUFFS: Record> + * - BUFFS_TEMP: Record> + * - 每个属性对应一个buff数组,可混合存储数值型和百分比型buff + * + * 4. 属性计算公式 - 按BType区分 + * - 数值型: (基础值 + 所有数值型buff之和) × (1 + 所有百分比buff之和/100) + * - 百分比: 基础值 + 所有数值型buff之和 + 所有百分比buff之和 + * + * 5. API 使用示例 + * // 添加buff(自动根据time判断持久或临时) + * const buffConf: BuffConf = { + * buff: Attrs.AP, + * BType: BType.VALUE, + * value: 10, + * time: 5, // 0表示持久,>0表示临时 + * chance: 100 + * }; + * hero.addBuff(buffConf); + * + * // 移除特定buff + * hero.removeBuff(Attrs.AP, 10, true); // 移除临时的+10 AP + * + * // 清空属性所有buff + * hero.clearBuffs(Attrs.AP); + * + * // 查询激活的buff数量 + * const count = hero.getActiveBuffCount(Attrs.AP); + * + * 6. 临时buff自动更新 + * - 每帧update中自动调用updateTemporaryBuffsDebuffs(dt) + * - 递减所有临时buff的remainTime + * - 当remainTime <= 0时自动移除buff并重新计算属性 */ /** 角色显示组件 */ export interface BuffInfo { @@ -73,13 +106,15 @@ export class HeroViewComp extends CCComp { base_dis: number = 100; Attrs:any=[] NeAttrs:any=[] - // Buff debuff 统一管理, value是正,debuff 是负数 - // 结构: { [attrIndex: number]: { value: number, remainTime?: number } } - BUFF_V: Record = {} // 持久型数buff - BUFF_R: Record = {} // 持久型百分比 buff - - BUFFS_V: Record = {} // 临时型数buff - BUFFS_R: Record = {} // 临时型百分比 buff + // Buff debuff 统一管理 + // 每个buff实例内包含BType,用于区分数值型(VALUE)和百分比型(RATIO) + // [attrIndex: number]: Array - 允许多个buff实例独立存在 + + /** 持久型buff数组 - 不会自动过期 */ + BUFFS: Record> = {} + + /** 临时型buff数组 - 按时间自动过期 */ + BUFFS_TEMP: Record> = {} atk_count: number = 0; atked_count: number = 0; @@ -127,13 +162,9 @@ export class HeroViewComp extends CCComp { */ initAttrs() { // 清空现有 buff/debuff - this.BUFF_V = { + this.BUFFS = { }; - this.BUFFS_V = { - }; - this.BUFF_R = { - }; - this.BUFFS_R = { + this.BUFFS_TEMP = { }; // 获取英雄配置 const heroInfo = HeroInfo[this.hero_uuid]; @@ -174,63 +205,38 @@ export class HeroViewComp extends CCComp { } // ==================== BUFF管理 ==================== /** - * 添加 buff 效果(智能覆盖) + * 添加 buff 效果(支持多次叠加) * @param buffConf buff 配置 (来自 SkillSet.BuffConf heroSet.buff) * - * 智能覆盖规则 - * 1. 值更小:不添 - * 2. 值相同且都是临时:叠加时 - * 3. 值更大:更新为新值(临时则更新值和时间 + * 叠加规则 + * 1. 持久型buff:直接添加到数组中,与现有buff独立存在 + * 2. 临时型buff:直接添加到数组中,保持独立的剩余时间计算 + * 3. 所有buff实例在属性计算时都被纳入求和计算 */ addBuff(buffConf: BuffConf) { - const isValue = buffConf.BType === BType.VALUE; const isPermanent = buffConf.time === 0; const attrIndex = buffConf.buff; - // 根据类型选择对应buff 字典 - const permanentBuffs = isValue ? this.BUFF_V : this.BUFF_R; - const temporaryBuffs = isValue ? this.BUFFS_V : this.BUFFS_R; - if (isPermanent) { - // 添加持久buff - const existing = permanentBuffs[attrIndex]; - if (existing) { - // 值更小,不添 - if (buffConf.value <= existing.value) { - return; - } - // 值更大,更新 - existing.value = buffConf.value; - } else { - // 没有同类型,直接添加 - permanentBuffs[attrIndex] = { value: buffConf.value }; + // 添加持久buff到BUFFS - 直接追加到数组 + if (!this.BUFFS[attrIndex]) { + this.BUFFS[attrIndex] = []; } + this.BUFFS[attrIndex].push({ value: buffConf.value, BType: buffConf.BType }); } else { - // 添加临时buff - const existing = temporaryBuffs[attrIndex]; - if (existing) { - if (buffConf.value < existing.value) { - // 值更小,不添 - return; - } else if (buffConf.value === existing.value) { - // 值相同,叠加时间 - existing.remainTime += buffConf.time; - return; // 时间叠加不需要重算属 - } else { - // 值更大,更新值和时间 - existing.value = buffConf.value; - existing.remainTime = buffConf.time; - } - } else { - // 没有同类型,直接添加 - temporaryBuffs[attrIndex] = { - value: buffConf.value, - remainTime: buffConf.time - }; + // 添加临时buff到BUFFS_TEMP - 直接追加到数组 + if (!this.BUFFS_TEMP[attrIndex]) { + this.BUFFS_TEMP[attrIndex] = []; } + this.BUFFS_TEMP[attrIndex].push({ + value: buffConf.value, + BType: buffConf.BType, + remainTime: buffConf.time + }); } - this.recalculateSingleAttr(buffConf.buff); + // 重新计算受影响的属性 + this.recalculateSingleAttr(attrIndex); } @@ -240,8 +246,8 @@ export class HeroViewComp extends CCComp { * @param attrIndex 属性索引 * * 计算公式: - * - 数值型属性:最终值 = (基础值 + 数值型buff - 数值型debuff) × (1 + 百分比buff/100 - 百分比debuff/100) - * - 百分比型属性:最终值 = 基础值 + 数值型buff - 数值型debuff + 百分比buff - 百分比debuff + * - 数值型属性:最终值 = (基础值 + 所有数值型buff之和 - 所有数值型debuff之和) × (1 + 所有百分比buff之和/100 - 所有百分比debuff之和/100) + * - 百分比型属性:最终值 = 基础值 + 所有数值型buff之和 - 所有数值型debuff之和 + 所有百分比buff之和 - 所有百分比debuff之和 */ private recalculateSingleAttr(attrIndex: number) { // 1. 获取基础值 @@ -257,29 +263,45 @@ export class HeroViewComp extends CCComp { const baseVal = baseValues[attrIndex] !== undefined ? baseValues[attrIndex] : 0; - // 2. 收集所有数值型 buff/debuff + // 2. 收集所有数值型 buff/debuff - 遍历所有buff数组并按BType筛选求和 let totalValue = baseVal; - // Buff:直接使用 Attrs 索引 - if (this.BUFF_V[attrIndex]) { - totalValue += this.BUFF_V[attrIndex].value; + // 遍历持久buff数组 + if (this.BUFFS[attrIndex] && this.BUFFS[attrIndex].length > 0) { + for (const buff of this.BUFFS[attrIndex]) { + if (buff.BType === BType.VALUE) { + totalValue += buff.value; + } + } } - if (this.BUFFS_V[attrIndex]) { - totalValue += this.BUFFS_V[attrIndex].value; + // 遍历临时buff数组 + if (this.BUFFS_TEMP[attrIndex] && this.BUFFS_TEMP[attrIndex].length > 0) { + for (const buff of this.BUFFS_TEMP[attrIndex]) { + if (buff.BType === BType.VALUE) { + totalValue += buff.value; + } + } } - - // 3. 收集所有百分比型 buff/debuff + // 3. 收集所有百分比型 buff/debuff - 遍历所有buff数组并按BType筛选求和 let totalRatio = 0; // 总百分比(可正可负) - // Buff:直接使用 Attrs 索引 - if (this.BUFF_R[attrIndex]) { - totalRatio += this.BUFF_R[attrIndex].value; + // 遍历持久buff数组 + if (this.BUFFS[attrIndex] && this.BUFFS[attrIndex].length > 0) { + for (const buff of this.BUFFS[attrIndex]) { + if (buff.BType === BType.RATIO) { + totalRatio += buff.value; + } + } } - if (this.BUFFS_R[attrIndex]) { - totalRatio += this.BUFFS_R[attrIndex].value; + // 遍历临时buff数组 + if (this.BUFFS_TEMP[attrIndex] && this.BUFFS_TEMP[attrIndex].length > 0) { + for (const buff of this.BUFFS_TEMP[attrIndex]) { + if (buff.BType === BType.RATIO) { + totalRatio += buff.value; + } + } } - // 4. 根据属性类型计算最终值 const attrType = AttrsType[attrIndex]; @@ -327,20 +349,38 @@ export class HeroViewComp extends CCComp { updateTemporaryBuffsDebuffs(dt: number) { const affectedAttrs = new Set(); // 更新临时型数buff - for (const attrIndex in this.BUFFS_V) { - const buff = this.BUFFS_V[attrIndex]; - buff.remainTime -= dt; - if (buff.remainTime <= 0) { - delete this.BUFFS_V[attrIndex]; + for (const attrIndex in this.BUFFS_TEMP) { + const buffs = this.BUFFS_TEMP[attrIndex]; + buffs.forEach(buff => { + buff.remainTime -= dt; + if (buff.remainTime <= 0) { + // 从数组中移除 + const index = buffs.indexOf(buff); + if (index > -1) { + buffs.splice(index, 1); + } + } + }); + if (buffs.length === 0) { + delete this.BUFFS_TEMP[attrIndex]; affectedAttrs.add(parseInt(attrIndex)); } } // 更新临时型百分比 buff - for (const attrIndex in this.BUFFS_R) { - const buff = this.BUFFS_R[attrIndex]; - buff.remainTime -= dt; - if (buff.remainTime <= 0) { - delete this.BUFFS_R[attrIndex]; + for (const attrIndex in this.BUFFS_TEMP) { + const buffs = this.BUFFS_TEMP[attrIndex]; + buffs.forEach(buff => { + buff.remainTime -= dt; + if (buff.remainTime <= 0) { + // 从数组中移除 + const index = buffs.indexOf(buff); + if (index > -1) { + buffs.splice(index, 1); + } + } + }); + if (buffs.length === 0) { + delete this.BUFFS_TEMP[attrIndex]; affectedAttrs.add(parseInt(attrIndex)); } } @@ -360,6 +400,74 @@ export class HeroViewComp extends CCComp { }); } + // ==================== BUFF 辅助方法 ==================== + /** + * 移除特定属性的某个buff实例 + * @param attrIndex 属性索引 + * @param value buff效果值 + * @param isTemporary 是否为临时buff(true=临时,false=持久) + * @return 是否移除成功 + */ + removeBuff(attrIndex: number, value: number, isTemporary: boolean = true): boolean { + const buffDict = isTemporary ? this.BUFFS_TEMP : this.BUFFS; + + if (!buffDict[attrIndex] || buffDict[attrIndex].length === 0) { + return false; + } + + const buffs = buffDict[attrIndex]; + const index = buffs.findIndex(buff => buff.value === value); + + if (index > -1) { + buffs.splice(index, 1); + // 如果该属性所有buff都被移除,删除该属性键 + if (buffs.length === 0) { + delete buffDict[attrIndex]; + } + // 重新计算属性 + this.recalculateSingleAttr(attrIndex); + return true; + } + return false; + } + + /** + * 清空某个属性的所有buff + * @param attrIndex 属性索引 + * @param clearTemporaryOnly 是否仅清空临时buff(true=仅临时,false=全部) + */ + clearBuffs(attrIndex: number, clearTemporaryOnly: boolean = false): void { + if (!clearTemporaryOnly) { + delete this.BUFFS[attrIndex]; + } + delete this.BUFFS_TEMP[attrIndex]; + this.recalculateSingleAttr(attrIndex); + } + + // ==================== NeAttrs(负面状态)管理 ==================== + /** + * 清理单个NeAttr(负面状态) + * @param neAttrIndex NeAttrs索引(如NeAttrs.IN_STUN、NeAttrs.IN_FROST等) + * 清理即将该状态的value和time都设为0 + */ + clearNeAttr(neAttrIndex: number): void { + if (this.NeAttrs[neAttrIndex]) { + this.NeAttrs[neAttrIndex].value = 0; + this.NeAttrs[neAttrIndex].time = 0; + } + } + + /** + * 清理所有NeAttrs(负面状态) + * 清理即将所有状态的value和time都设为0 + */ + clearAllNeAttrs(): void { + for (const key in this.NeAttrs) { + this.NeAttrs[key].value = 0; + this.NeAttrs[key].time = 0; + } + } + public isStun() { return this.NeAttrs[NeAttrs.IN_STUN].time > 0; }