本文最后更新于39 天前,其中的信息可能已经过时,如有错误请发送邮件到big_fw@foxmail.com
将本来定义在character_data脚本里的人物各种属性分离,用单独的资源类“skillAttribute”来实现角色的每一个属性,然后将人物拥有的属性实例化给人物
SkillAtrribute实现说明:
| 变量名 | 类型 | 作用 | 默认值 |
|---|---|---|---|
attribute_name | StringName | 属性唯一标识名 | &"" |
display_name | String | 属性显示名称 | "" |
description | String | 属性详细描述 | "" |
base_value | float | 属性基础值 | 0.0 |
min_value | float | 属性允许最小值 | -INF |
max_value | float | 属性允许最大值 | INF |
can_be_negative | bool | 属性是否可为负 | false |
current_value | float | 属性当前值(运行时计算) | 0.0 |
_active_modifiers | Array[SkillAttributeModifier] | 当前激活的修改器列表 | [] |
_owner_set | SkillAttributeSet | 所属的属性集引用 | 无 |
@export var base_value: float = 0.0:
set(value):
var old_value = base_value
base_value = value
base_value_changed.emit(old_value, value)
在 GDScript 中, set(value): 是 属性访问器(setter) 的定义语法,用于 自定义属性赋值时的行为 。当你给一个变量赋值时,不是直接修改变量,而是自动调用这个 setter 方法,执行其中的逻辑。比如:
var health: float = 100:
set(value):
# 确保生命值在 0-100 之间
health = clamp(value, 0, 100)
信号
- current_value_changed(old_value, new_value) :属性当前值变化时触发
- base_value_changed(old_value, new_value) :属性基础值变化时触发
主要函数
| 函数名 | 作用 | 参数 | 返回值 |
|---|---|---|---|
_init | 构造函数 | p_owner_set: SkillAttributeSet = null, p_base_value_override: float = -1.0 | void |
get_active_modifiers | 获取当前激活的修改器 | 无 | Array[SkillAttributeModifier] |
add_modifier_internal | 添加修改器并触发重算 | modifier: SkillAttributeModifier | void |
remove_modifier_internal | 移除修改器并触发重算 | modifier: SkillAttributeModifier | void |
set_base_value_internal | 设置基础值并触发重算 | new_base_value: float | void |
get_current_value | 获取当前计算值 | 无 | float |
get_base_value | 获取基础值 | 无 | float |
set_owner_set | 设置所属的属性集 | owner: SkillAttributeSet | void |
_recalculate_current_value | 重新计算当前值 | 无 | bool(值是否变化) |
属性值重算步骤
- 初始化变量
- 步骤 1:计算固定值增减 (Additive)
- 步骤 2:计算百分比修改 (Multiplicative)
- 步骤 3:处理覆盖型修改器 (Override)
- 步骤 4:应用最终钳制 (Clamping)
- 更新值并返回变化状态
SkillAttributeModifier设计
| 变量名 | 类型 | 默认值 | 作用 | 数据流向 |
|---|---|---|---|---|
attribute_id | StringName | &"" | 目标属性的唯一标识符 | 从修改器流向 SkillAttribute(指定修改哪个属性) |
magnitude | float | 0.0 | 修改幅度值 | 从修改器流向 SkillAttribute(影响计算结果) |
operation | ModifierOperation | ADD_ABSOLUTE | 修改操作类型 | 从修改器流向 SkillAttribute(决定计算方式) |
source_id | String | "" | 修改器来源标识 | 从修改器流向 SkillAttributeSet(用于批量移除) |
补充说明:
attribute_id:通常对应属性的唯一名称,如”Strength”、”Health”等magnitude:数值大小,可正可负,表示属性的修改量operation:常见操作类型可能包括:ADD_ABSOLUTE:绝对值相加ADD_PERCENTAGE:百分比相加MULTIPLY:乘法叠加OVERRIDE:覆盖
source_id:用于标识修改器的来源,如”装备剑”、”技能火球术”等,便于通过来源统一移除修改器
SkillAttributeSet实现
- 属性实例管理 :从属性模板创建独立实例,确保每个角色/实体拥有自己的属性副本
- 属性值计算 :协调基础值和修改器的应用,计算最终属性值
- 状态同步 :处理属性间的依赖关系(如当前生命值与最大生命值的同步)
- 事件通知 :通过信号机制广播属性变化,支持外部系统响应
- 钩子系统 :提供扩展点,允许子类定制行为
属性初始化流程:
- 调用 initialize_set()
- 清空现有属性实例
- 遍历 attributes_to_initialize 配置数组
- 对每个模板创建深拷贝实例
- 设置属性实例的所有者引用
- 初始化基础值和当前值
- 连接属性实例的信号到集合的信号
- 处理属性间初始化依赖关系
- 标记初始化完成
属性值计算流程
当基础值改变或应用/移除修改器时:
- 属性基础值改变 -> set_base_value_internal()
- 添加/移除修改器 -> add_modifier_internal()/remove_modifier_internal()
- 触发 _recalculate_current_value()
a. 计算固定值增减 (ADD_ABSOLUTE)
b. 计算百分比修改 (ADD_PERCENTAGE)
c. 处理覆盖型修改器 (OVERRIDE)
d. 应用最终钳制 (Clamping)
e. 更新当前值并发出信号 - 集合接收信号并广播给外部系统(比如角色UI界面)
Character修改
所有属性不再能直接进行修改了,而是通过get方法获取以及通过修改器修改
var current_hp: float:
get: return active_attribute_set.get_current_value(&"CurrentHealth") if active_attribute_set else 0.0
# 链接AttributeSet到Character
active_attribute_set.current_value_changed.connect(_on_attribute_current_value_changed)
active_attribute_set.base_value_changed.connect(_on_attribute_base_value_changed)
#处理生命值/最大生命值/魔法值/最大魔法值改变
func _on_attribute_current_value_changed(attribute_instance: SkillAttribute, old_value: float, new_value: float):
if attribute_instance.attribute_name == &"CurrentHealth":
health_changed.emit(new_value, max_hp, self)
_update_health_display()
if new_value <= 0.0 and old_value > 0.0: # 从存活到死亡
_die()
elif attribute_instance.attribute_name == &"MaxHealth":
# MaxHealth变化也需要通知UI更新,并可能影响CurrentHealth的钳制(已在AttributeSet钩子中处理)
health_changed.emit(current_hp, new_value, self)
_update_health_display()
elif attribute_instance.attribute_name == &"CurrentMana":
mana_changed.emit(new_value, max_mp, self)
_update_mana_display()
elif attribute_instance.attribute_name == &"MaxMana":
mana_changed.emit(current_mp, new_value, self)
_update_mana_display()
注意生命值魔法值的改变(血条收到伤害/治疗,魔法值消耗/回复)是直接改变基础值,其他buff或者debuff是改变当前值

