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 系统使用说明 ====================
|
* ==================== BUFF 系统使用说明 ====================
|
||||||
*
|
*
|
||||||
* 1. 系统架构
|
* 1. 系统架构 - 支持多次叠加(简化设计)
|
||||||
* - BUFF_V/BUFFS_V: 数值型 buff(持临时
|
* - BUFFS: 持久型buff数组(持久存在,直到主动移除)
|
||||||
* - BUFF_R/BUFFS_R: 百分比型 buff(持临时
|
* - BUFFS_TEMP: 临时型buff数组(持续指定时间,时间到自动移除)
|
||||||
* - DBUFF_V/DBUFFS_V: 数值型 debuff(持临时
|
* - 每个buff实例内包含BType字段,区分数值型(VALUE)和百分比型(RATIO)
|
||||||
* - DBUFF_R/DBUFFS_R: 百分比型 debuff(持临时
|
|
||||||
*
|
*
|
||||||
* 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 {
|
export interface BuffInfo {
|
||||||
@@ -73,13 +106,15 @@ export class HeroViewComp extends CCComp {
|
|||||||
base_dis: number = 100;
|
base_dis: number = 100;
|
||||||
Attrs:any=[]
|
Attrs:any=[]
|
||||||
NeAttrs:any=[]
|
NeAttrs:any=[]
|
||||||
// Buff debuff 统一管理, value是正,debuff 是负数
|
// Buff debuff 统一管理
|
||||||
// 结构: { [attrIndex: number]: { value: number, remainTime?: number } }
|
// 每个buff实例内包含BType,用于区分数值型(VALUE)和百分比型(RATIO)
|
||||||
BUFF_V: Record<number, {value: number}> = {} // 持久型数buff
|
// [attrIndex: number]: Array<BuffInstance> - 允许多个buff实例独立存在
|
||||||
BUFF_R: Record<number, {value: number}> = {} // 持久型百分比 buff
|
|
||||||
|
|
||||||
BUFFS_V: Record<number, {value: number, remainTime: number}> = {} // 临时型数buff
|
/** 持久型buff数组 - 不会自动过期 */
|
||||||
BUFFS_R: Record<number, {value: number, remainTime: number}> = {} // 临时型百分比 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;
|
atk_count: number = 0;
|
||||||
atked_count: number = 0;
|
atked_count: number = 0;
|
||||||
@@ -127,13 +162,9 @@ export class HeroViewComp extends CCComp {
|
|||||||
*/
|
*/
|
||||||
initAttrs() {
|
initAttrs() {
|
||||||
// 清空现有 buff/debuff
|
// 清空现有 buff/debuff
|
||||||
this.BUFF_V = {
|
this.BUFFS = {
|
||||||
};
|
};
|
||||||
this.BUFFS_V = {
|
this.BUFFS_TEMP = {
|
||||||
};
|
|
||||||
this.BUFF_R = {
|
|
||||||
};
|
|
||||||
this.BUFFS_R = {
|
|
||||||
};
|
};
|
||||||
// 获取英雄配置
|
// 获取英雄配置
|
||||||
const heroInfo = HeroInfo[this.hero_uuid];
|
const heroInfo = HeroInfo[this.hero_uuid];
|
||||||
@@ -174,63 +205,38 @@ export class HeroViewComp extends CCComp {
|
|||||||
}
|
}
|
||||||
// ==================== BUFF管理 ====================
|
// ==================== BUFF管理 ====================
|
||||||
/**
|
/**
|
||||||
* 添加 buff 效果(智能覆盖)
|
* 添加 buff 效果(支持多次叠加)
|
||||||
* @param buffConf buff 配置 (来自 SkillSet.BuffConf heroSet.buff)
|
* @param buffConf buff 配置 (来自 SkillSet.BuffConf heroSet.buff)
|
||||||
*
|
*
|
||||||
* 智能覆盖规则
|
* 叠加规则
|
||||||
* 1. 值更小:不添
|
* 1. 持久型buff:直接添加到数组中,与现有buff独立存在
|
||||||
* 2. 值相同且都是临时:叠加时
|
* 2. 临时型buff:直接添加到数组中,保持独立的剩余时间计算
|
||||||
* 3. 值更大:更新为新值(临时则更新值和时间
|
* 3. 所有buff实例在属性计算时都被纳入求和计算
|
||||||
*/
|
*/
|
||||||
addBuff(buffConf: BuffConf) {
|
addBuff(buffConf: BuffConf) {
|
||||||
const isValue = buffConf.BType === BType.VALUE;
|
|
||||||
const isPermanent = buffConf.time === 0;
|
const isPermanent = buffConf.time === 0;
|
||||||
const attrIndex = buffConf.buff;
|
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) {
|
if (isPermanent) {
|
||||||
// 添加持久buff
|
// 添加持久buff到BUFFS - 直接追加到数组
|
||||||
const existing = permanentBuffs[attrIndex];
|
if (!this.BUFFS[attrIndex]) {
|
||||||
if (existing) {
|
this.BUFFS[attrIndex] = [];
|
||||||
// 值更小,不添
|
|
||||||
if (buffConf.value <= existing.value) {
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
// 值更大,更新
|
this.BUFFS[attrIndex].push({ value: buffConf.value, BType: buffConf.BType });
|
||||||
existing.value = buffConf.value;
|
|
||||||
} else {
|
} else {
|
||||||
// 没有同类型,直接添加
|
// 添加临时buff到BUFFS_TEMP - 直接追加到数组
|
||||||
permanentBuffs[attrIndex] = { value: buffConf.value };
|
if (!this.BUFFS_TEMP[attrIndex]) {
|
||||||
|
this.BUFFS_TEMP[attrIndex] = [];
|
||||||
}
|
}
|
||||||
} else {
|
this.BUFFS_TEMP[attrIndex].push({
|
||||||
// 添加临时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,
|
value: buffConf.value,
|
||||||
|
BType: buffConf.BType,
|
||||||
remainTime: buffConf.time
|
remainTime: buffConf.time
|
||||||
};
|
});
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
this.recalculateSingleAttr(buffConf.buff);
|
// 重新计算受影响的属性
|
||||||
|
this.recalculateSingleAttr(attrIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -240,8 +246,8 @@ export class HeroViewComp extends CCComp {
|
|||||||
* @param attrIndex 属性索引
|
* @param attrIndex 属性索引
|
||||||
*
|
*
|
||||||
* 计算公式:
|
* 计算公式:
|
||||||
* - 数值型属性:最终值 = (基础值 + 数值型buff - 数值型debuff) × (1 + 百分比buff/100 - 百分比debuff/100)
|
* - 数值型属性:最终值 = (基础值 + 所有数值型buff之和 - 所有数值型debuff之和) × (1 + 所有百分比buff之和/100 - 所有百分比debuff之和/100)
|
||||||
* - 百分比型属性:最终值 = 基础值 + 数值型buff - 数值型debuff + 百分比buff - 百分比debuff
|
* - 百分比型属性:最终值 = 基础值 + 所有数值型buff之和 - 所有数值型debuff之和 + 所有百分比buff之和 - 所有百分比debuff之和
|
||||||
*/
|
*/
|
||||||
private recalculateSingleAttr(attrIndex: number) {
|
private recalculateSingleAttr(attrIndex: number) {
|
||||||
// 1. 获取基础值
|
// 1. 获取基础值
|
||||||
@@ -257,29 +263,45 @@ export class HeroViewComp extends CCComp {
|
|||||||
|
|
||||||
const baseVal = baseValues[attrIndex] !== undefined ? baseValues[attrIndex] : 0;
|
const baseVal = baseValues[attrIndex] !== undefined ? baseValues[attrIndex] : 0;
|
||||||
|
|
||||||
// 2. 收集所有数值型 buff/debuff
|
// 2. 收集所有数值型 buff/debuff - 遍历所有buff数组并按BType筛选求和
|
||||||
let totalValue = baseVal;
|
let totalValue = baseVal;
|
||||||
|
|
||||||
// Buff:直接使用 Attrs 索引
|
// 遍历持久buff数组
|
||||||
if (this.BUFF_V[attrIndex]) {
|
if (this.BUFFS[attrIndex] && this.BUFFS[attrIndex].length > 0) {
|
||||||
totalValue += this.BUFF_V[attrIndex].value;
|
for (const buff of this.BUFFS[attrIndex]) {
|
||||||
|
if (buff.BType === BType.VALUE) {
|
||||||
|
totalValue += buff.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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (this.BUFFS_V[attrIndex]) {
|
|
||||||
totalValue += this.BUFFS_V[attrIndex].value;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 3. 收集所有百分比型 buff/debuff - 遍历所有buff数组并按BType筛选求和
|
||||||
// 3. 收集所有百分比型 buff/debuff
|
|
||||||
let totalRatio = 0; // 总百分比(可正可负)
|
let totalRatio = 0; // 总百分比(可正可负)
|
||||||
|
|
||||||
// Buff:直接使用 Attrs 索引
|
// 遍历持久buff数组
|
||||||
if (this.BUFF_R[attrIndex]) {
|
if (this.BUFFS[attrIndex] && this.BUFFS[attrIndex].length > 0) {
|
||||||
totalRatio += this.BUFF_R[attrIndex].value;
|
for (const buff of this.BUFFS[attrIndex]) {
|
||||||
|
if (buff.BType === BType.RATIO) {
|
||||||
|
totalRatio += buff.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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (this.BUFFS_R[attrIndex]) {
|
|
||||||
totalRatio += this.BUFFS_R[attrIndex].value;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// 4. 根据属性类型计算最终值
|
// 4. 根据属性类型计算最终值
|
||||||
const attrType = AttrsType[attrIndex];
|
const attrType = AttrsType[attrIndex];
|
||||||
@@ -327,20 +349,38 @@ export class HeroViewComp extends CCComp {
|
|||||||
updateTemporaryBuffsDebuffs(dt: number) {
|
updateTemporaryBuffsDebuffs(dt: number) {
|
||||||
const affectedAttrs = new Set<number>();
|
const affectedAttrs = new Set<number>();
|
||||||
// 更新临时型数buff
|
// 更新临时型数buff
|
||||||
for (const attrIndex in this.BUFFS_V) {
|
for (const attrIndex in this.BUFFS_TEMP) {
|
||||||
const buff = this.BUFFS_V[attrIndex];
|
const buffs = this.BUFFS_TEMP[attrIndex];
|
||||||
|
buffs.forEach(buff => {
|
||||||
buff.remainTime -= dt;
|
buff.remainTime -= dt;
|
||||||
if (buff.remainTime <= 0) {
|
if (buff.remainTime <= 0) {
|
||||||
delete this.BUFFS_V[attrIndex];
|
// 从数组中移除
|
||||||
|
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));
|
affectedAttrs.add(parseInt(attrIndex));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// 更新临时型百分比 buff
|
// 更新临时型百分比 buff
|
||||||
for (const attrIndex in this.BUFFS_R) {
|
for (const attrIndex in this.BUFFS_TEMP) {
|
||||||
const buff = this.BUFFS_R[attrIndex];
|
const buffs = this.BUFFS_TEMP[attrIndex];
|
||||||
|
buffs.forEach(buff => {
|
||||||
buff.remainTime -= dt;
|
buff.remainTime -= dt;
|
||||||
if (buff.remainTime <= 0) {
|
if (buff.remainTime <= 0) {
|
||||||
delete this.BUFFS_R[attrIndex];
|
// 从数组中移除
|
||||||
|
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));
|
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() {
|
public isStun() {
|
||||||
return this.NeAttrs[NeAttrs.IN_STUN].time > 0;
|
return this.NeAttrs[NeAttrs.IN_STUN].time > 0;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user