refactor(buff系统): 重构英雄的buff管理逻辑,支持多次叠加和临时效果
- 统一管理持久型和临时型buff,简化buff的添加和移除逻辑 - 更新buff的叠加规则,允许同一属性的多个buff实例共存 - 优化属性计算公式,确保所有buff在计算时被纳入考虑 - 新增清空buff和移除特定buff的辅助方法,提升管理灵活性 - 详细更新文档,提供API使用示例和数据结构说明
This commit is contained in:
@@ -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<attrIndex: number, Array<{value, BType}>>
|
||||
* - BUFFS_TEMP: Record<attrIndex: number, Array<{value, BType, remainTime}>>
|
||||
* - 每个属性对应一个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<number, {value: number}> = {} // 持久型数buff
|
||||
BUFF_R: Record<number, {value: number}> = {} // 持久型百分比 buff
|
||||
|
||||
BUFFS_V: Record<number, {value: number, remainTime: number}> = {} // 临时型数buff
|
||||
BUFFS_R: Record<number, {value: number, remainTime: number}> = {} // 临时型百分比 buff
|
||||
// Buff debuff 统一管理
|
||||
// 每个buff实例内包含BType,用于区分数值型(VALUE)和百分比型(RATIO)
|
||||
// [attrIndex: number]: Array<BuffInstance> - 允许多个buff实例独立存在
|
||||
|
||||
/** 持久型buff数组 - 不会自动过期 */
|
||||
BUFFS: Record<number, Array<{value: number, BType: BType}>> = {}
|
||||
|
||||
/** 临时型buff数组 - 按时间自动过期 */
|
||||
BUFFS_TEMP: Record<number, Array<{value: number, BType: BType, remainTime: number}>> = {}
|
||||
|
||||
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<number>();
|
||||
// 更新临时型数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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user