| name | godot-buff-system |
| description | Godot 4 Buff系统完整实现,支持状态效果管理、持续时间、循环效果、层叠机制等。使用Resource+Component架构。 |
Godot 4 Buff系统
完整的Buff/状态效果系统,支持增益和减益效果、持续时间、循环触发、层叠管理等。
何时使用此技能
- 实现游戏状态效果系统
- 玩家/敌人增益减益管理
- DOT/AOE持续伤害
- 护盾、回复、加速等效果
架构设计
Resource + Component 架构
Buff系统
├── Buff Resource(数据层)
│ ├── BuffData - Buff定义数据
│ └── BuffStack - 运行时Buff实例
├── BuffComponent(逻辑层)
│ ├── BuffStore - Buff注册中心
│ └── BuffTimer - 时间管理
└── BuffEffect(效果层)
└── 各种效果实现
分层职责
- BuffData: 定义Buff的静态属性(名称、持续时间、效果类型等)
- BuffStack: 管理Buff实例的运行时状态(当前层数、剩余时间等)
- BuffComponent: 挂在目标身上,执行Buff逻辑
Buff数据结构
BuffData Buff定义资源
# buff_data.gd
class_name BuffData
extends Resource
## Buff唯一标识符
@export var id: StringName
## Buff显示名称
@export var buff_name: String
## Buff描述
@export_multiline var description: String
## Buff图标
@export var icon: Texture2D
## 持续时间(秒),0表示永久
@export var duration: float = 0.0
## 循环间隔(秒),0表示不循环
@export var tick_interval: float = 0.0
## 最大层数
@export var max_stack: int = 1
## 层叠方式
@export_enum("None", "Duration", "Intensity", "Both") var stack_type: int = 0
## 效果类型
@export_enum("Beneficial", "Harmful", "Neutral") var buff_type: int = 0
## 是否可以被驱散
@export var can_be_dispelled: bool = true
## 是否显示在UI上
@export var show_in_ui: bool = true
## 效果生效时调用
@export var effect_script: GDScript
## Buff优先级(高优先级先执行)
@export var priority: int = 0
BuffStack Buff实例
# buff_stack.gd
class_name BuffStack
extends RefCounted
## 所属的Buff数据
var buff_data: BuffData
## 当前层数
var current_stack: int = 1
## 剩余时间
var remaining_time: float
## 是否正在运行
var is_active: bool = false
## 父节点(BuffComponent持有者)
var owner_node: Node
## 内部计时器
var _tick_timer: float = 0.0
signal stack_changed(new_stack: int)
signal buff_ended(buff_id: StringName)
signal tick_elapsed
func _init(data: BuffData, owner: Node) -> void:
buff_data = data
owner_node = owner
remaining_time = data.duration
_tick_timer = data.tick_interval
func start() -> void:
is_active = true
_apply_effect()
func stop() -> void:
is_active = false
_remove_effect()
buff_ended.emit(buff_data.id)
func update(delta: float) -> bool:
if not is_active:
return false
# 更新剩余时间
remaining_time -= delta
# 循环效果
if buff_data.tick_interval > 0:
_tick_timer -= delta
if _tick_timer <= 0:
_tick_timer = buff_data.tick_interval
_on_tick()
tick_elapsed.emit()
# 永久Buff
if buff_data.duration <= 0:
return true
# 时间到则结束
if remaining_time <= 0:
stop()
return false
return true
func add_stack(amount: int = 1) -> void:
var new_stack := mini(current_stack + amount, buff_data.max_stack)
match buff_data.stack_type:
1: # Duration - 刷新持续时间
remaining_time = buff_data.duration
2: # Intensity - 增加层数
current_stack = new_stack
_apply_effect() # 重新应用效果
3: # Both - 刷新时间并增加层数
remaining_time = buff_data.duration
current_stack = new_stack
_apply_effect()
_:
current_stack = new_stack
stack_changed.emit(current_stack)
func _apply_effect() -> void:
if buff_data.effect_script:
# 执行效果脚本
pass
func _remove_effect() -> void:
if buff_data.effect_script:
# 移除效果脚本
pass
func _on_tick() -> void:
# 每次循环时调用
pass
func get_time_percent() -> float:
if buff_data.duration <= 0:
return 1.0
return remaining_time / buff_data.duration
效果执行机制
BuffStore 注册中心
# buff_store.gd
class_name BuffStore
extends Node
## 注册所有可用的Buff
@export var registered_buffs: Array[BuffData]
## 全局Buff字典
var _buff_registry: Dictionary = {}
func _ready() -> void:
_register_all_buffs()
func _register_all_buffs() -> void:
for buff in registered_buffs:
_buff_registry[buff.id] = buff
func get_buff_data(buff_id: StringName) -> BuffData:
return _buff_registry.get(buff_id)
func create_buff_stack(buff_id: StringName, owner: Node) -> BuffStack:
var data := get_buff_data(buff_id)
if data == null:
push_error("Buff not found: ", buff_id)
return null
var stack := BuffStack.new(data, owner)
return stack
BuffComponent Buff组件
# buff_component.gd
class_name BuffComponent
extends Node
## 信号:Buff被添加
signal buff_added(buff_id: StringName, stack: BuffStack)
## 信号:Buff被移除
signal buff_removed(buff_id: StringName)
## 信号:Buff层数变化
signal buff_stack_changed(buff_id: StringName, new_stack: int)
## 信号:Buff刷新
signal buff_refreshed(buff_id: StringName)
## 活跃的Buff字典
var active_buffs: Dictionary = {}
## 父节点引用
var _owner: Node
func _ready() -> void:
_owner = get_parent()
func _process(delta: float) -> void:
_update_buffs(delta)
func _update_buffs(delta: float) -> void:
var to_remove: Array[StringName] = []
for buff_id in active_buffs.keys():
var stack: BuffStack = active_buffs[buff_id]
if not stack.update(delta):
to_remove.append(buff_id)
for buff_id in to_remove:
_remove_buff(buff_id)
## 添加Buff
func add_buff(buff_id: StringName, duration: float = -1) -> BuffStack:
# 检查是否已存在
if active_buffs.has(buff_id):
var existing: BuffStack = active_buffs[buff_id]
existing.add_stack(1)
if duration > 0:
existing.remaining_time = duration
buff_refreshed.emit(buff_id)
return existing
# 创建新Buff
var stack := BuffStore.create_buff_stack(buff_id, _owner)
if stack == null:
return null
if duration > 0:
stack.buff_data.duration = duration
active_buffs[buff_id] = stack
stack.start()
buff_added.emit(buff_id, stack)
return stack
## 移除Buff
func remove_buff(buff_id: StringName) -> bool:
if not active_buffs.has(buff_id):
return false
var stack: BuffStack = active_buffs[buff_id]
stack.stop()
active_buffs.erase(buff_id)
buff_removed.emit(buff_id)
return true
## 清除所有指定类型的Buff
func remove_buffs_by_type(buff_type: int) -> void:
var to_remove: Array[StringName] = []
for buff_id in active_buffs.keys():
var stack: BuffStack = active_buffs[buff_id]
if stack.buff_data.buff_type == buff_type:
to_remove.append(buff_id)
for buff_id in to_remove:
remove_buff(buff_id)
## 清除所有可驱散的减益Buff
func dispel_harmful() -> int:
var count := 0
var to_remove: Array[StringName] = []
for buff_id in active_buffs.keys():
var stack: BuffStack = active_buffs[buff_id]
if stack.buff_data.buff_type == 1 and stack.buff_data.can_be_dispelled: # Harmful
to_remove.append(buff_id)
for buff_id in to_remove:
remove_buff(buff_id)
count += 1
return count
## 获取当前层数
func get_buff_stack(buff_id: StringName) -> int:
if active_buffs.has(buff_id):
return active_buffs[buff_id].current_stack
return 0
## 是否有指定Buff
func has_buff(buff_id: StringName) -> bool:
return active_buffs.has(buff_id)
## 获取所有活跃Buff
func get_all_buffs() -> Array[BuffStack]:
return active_buffs.values()
## 获取指定类型的Buff
func get_buffs_by_type(buff_type: int) -> Array[BuffStack]:
var result: Array[BuffStack] = []
for stack in active_buffs.values():
if stack.buff_data.buff_type == buff_type:
result.append(stack)
return result
时间机制
持续时间处理
# 持续时间逻辑
func update(delta: float) -> bool:
remaining_time -= delta
if remaining_time <= 0:
stop()
return false
return true
循环触发机制
# 循环效果示例 - 持续伤害
class_name DOTBuffStack
extends BuffStack
var damage_per_tick: float
func _init(data: BuffData, owner: Node, damage: float):
super(data, owner)
damage_per_tick = damage
func _on_tick() -> void:
if owner_node.has_method("take_damage"):
owner_node.take_damage(damage_per_tick * current_stack)
定时器管理
# buff_timer_manager.gd
class_name BuffTimerManager
extends Node
## 全局定时器管理,避免每个Buff单独创建Timer
var _timers: Dictionary = {}
func add_timer(buff_id: StringName, interval: float, callback: Callable) -> void:
if _timers.has(buff_id):
return
var timer := Timer.new()
timer.wait_time = interval
timer.autostart = true
timer.timeout.connect(callback)
add_child(timer)
_timers[buff_id] = timer
func remove_timer(buff_id: StringName) -> void:
if _timers.has(buff_id):
var timer: Timer = _timers[buff_id]
timer.stop()
timer.queue_free()
_timers.erase(buff_id)
func clear_all() -> void:
for timer in _timers.values():
timer.stop()
timer.queue_free()
_timers.clear()
移除机制
手动移除
func remove_buff(buff_id: StringName) -> bool:
if not active_buffs.has(buff_id):
return false
var stack: BuffStack = active_buffs[buff_id]
stack.stop()
active_buffs.erase(buff_id)
return true
条件移除
# 特定条件触发移除
func check_removal_conditions() -> void:
for buff_id in active_buffs.keys():
var stack: BuffStack = active_buffs[buff_id]
# 示例:生命值低于阈值移除增益
if stack.buff_data.buff_type == 0: # Beneficial
if _owner.health < 10:
remove_buff(buff_id)
死亡移除
# 死亡时清除减益
func _on_owner_died() -> void:
remove_buffs_by_type(1) # Harmful buffs
实际效果实现示例
护盾效果
# shield_buff.gd
class_name ShieldBuffData
extends BuffData
@export var shield_amount: float = 50.0
func _init():
super()
buff_name = "护盾"
description = "获得临时护盾,吸收伤害"
duration = 10.0
buff_type = 0 # Beneficial
# shield_effect.gd
class_name ShieldEffect
extends Node
var shield_component: Node
var shield_amount: float
func apply(target: Node, amount: float) -> void:
if target.has_node("ShieldComponent"):
shield_component = target.get_node("ShieldComponent")
else:
shield_component = Node.new()
shield_component.set_script(load("res://shield_component.gd"))
target.add_child(shield_component)
shield_component.add_shield(amount)
func remove(target: Node) -> void:
if target.has_node("ShieldComponent"):
target.get_node("ShieldComponent").clear_shield()
攻击加速效果
# attack_speed_buff.gd
class_name AttackSpeedBuffData
extends BuffData
@export var speed_multiplier: float = 1.5
func _init():
super()
buff_name = "攻击加速"
description = "攻击速度提升50%"
duration = 5.0
buff_type = 0 # Beneficial
max_stack = 3
stack_type = 2 # Intensity
持续伤害效果
# dot_buff.gd
class_name DOTBuffData
extends BuffData
@export var damage_per_tick: float = 5.0
@export var tick_interval: float = 1.0
func _init():
super()
buff_name = "灼烧"
description = "每秒受到伤害"
duration = 5.0
tick_interval = 1.0
buff_type = 1 # Harmful
# dot_buff_stack.gd
class_name DOTBuffStack
extends BuffStack
var damage_per_tick: float
func _init(data: BuffData, owner: Node, damage: float):
super(data, owner)
damage_per_tick = damage
func _on_tick() -> void:
if owner_node.has_method("take_damage"):
owner_node.take_damage(damage_per_tick * current_stack)
使用示例
玩家角色
# player.gd
extends CharacterBody2D
@export var buff_component: BuffComponent
func take_damage(amount: float) -> void:
# 检查护盾
if has_node("ShieldComponent"):
var shield: Node = get_node("ShieldComponent")
amount = shield.absorb(amount)
_health -= amount
if _health <= 0:
die()
func _ready() -> void:
buff_component = BuffComponent.new()
add_child(buff_component)
func apply_poison() -> void:
var poison_data := BuffStore.get_buff_data("poison")
if poison_data:
buff_component.add_buff("poison")
应用Buff
# 使用示例
func use_item(item: InventoryItem) -> void:
match item.id:
"health_potion":
player.buff_component.add_buff("healing", 5.0)
"speed_boost":
player.buff_component.add_buff("speed_up", 10.0)
UI集成
Buff图标显示
# buff_icon_ui.gd
class_name BuffIconUI
extends Control
@export var icon_texture: TextureRect
@export var time_label: Label
@export var stack_label: Label
@export var duration_bar: ProgressBar
var buff_stack: BuffStack
func set_buff(stack: BuffStack) -> void:
buff_stack = stack
_update_display()
stack.stack_changed.connect(_on_stack_changed)
stack.buff_ended.connect(_on_buff_ended)
func _update_display() -> void:
if buff_stack == null:
return
icon_texture.texture = buff_stack.buff_data.icon
if buff_stack.buff_data.duration > 0:
time_label.text = "%.1f" % buff_stack.remaining_time
duration_bar.value = buff_stack.get_time_percent() * 100
if buff_stack.current_stack > 1:
stack_label.text = "x%d" % buff_stack.current_stack
stack_label.visible = true
func _on_stack_changed(new_stack: int) -> void:
stack_label.text = "x%d" % new_stack
func _on_buff_ended(_buff_id: StringName) -> void:
queue_free()
最佳实践
- Resource存储静态数据 - BuffData作为Resource便于编辑器配置
- Component管理运行时 - BuffComponent统一管理生命周期
- 使用RefCounted - BuffStack继承RefCounted自动管理内存
- 优先级处理 - 高优先级Buff先执行
- 循环池化 - 避免频繁创建Timer节点
- 信号解耦 - UI通过信号监听变化,不直接查询