第十六部分:装备系统基础:武器、防具、属性加成、装备栏、攻击力计算
这一部分开始做 RPG 里非常重要的“角色成长系统”。
前面我们已经有了:
背包
金币
商店
任务奖励
怪物掉落
玩家攻击
玩家血量
现在装备系统要接上这些内容:
玩家获得武器
↓
武器进入背包
↓
玩家装备武器
↓
攻击力增加
↓
打怪伤害提高
玩家获得防具
↓
装备防具
↓
最大血量或防御增加
↓
受到伤害减少
装备系统会让玩家从“捡东西”进一步变成“角色变强”。
1. 装备系统第一阶段要做什么
第一版先做这些:
1. 定义装备数据
2. 武器能增加攻击力
3. 防具能增加最大血量或防御
4. 玩家有装备栏
5. 背包中的装备可以被穿上
6. 穿上装备后属性刷新
7. 攻击伤害使用最终攻击力
8. 受到伤害时使用防御减伤
先不要做:
装备强化
随机词条
品质颜色
套装效果
耐久度
宝石镶嵌
双手武器
职业限制
等级限制
比较面板
拖拽换装
这些都后面再说。
第一版核心就是:
装备能穿
穿了能改变属性
2. 装备和普通物品的关系
装备本质上也是一种物品。
所以我们之前的 ItemData 里已经预留了:
enum ItemType {
CONSUMABLE,
MATERIAL,
CURRENCY,
QUEST,
EQUIPMENT,
CARD
}
装备就是:
item_type = EQUIPMENT
不过装备还需要额外属性:
装备部位
攻击力
防御力
最大生命加成
所以有两种做法:
做法 1:
直接在 ItemData 里加装备字段
做法 2:
创建 EquipmentData 继承 ItemData
第一版为了简单,可以直接在 ItemData 里加字段。
但从结构上说,EquipmentData 会更干净。
因为 Godot 支持自定义 Resource,并且 Resource 适合作为数据容器保存到磁盘,也能在 Inspector 里编辑导出的属性;之前物品、任务、商店数据都用 Resource,是同一个思路。(Godot Engine documentation)
这一部分我建议你用:
EquipmentData 继承 ItemData
这样更像正式项目。
3. EquipmentData 是什么
EquipmentData 是装备物品的数据。
比如一把木剑:
id = wooden_sword
display_name = 木剑
item_type = EQUIPMENT
slot_type = WEAPON
attack_bonus = 5
defense_bonus = 0
max_hp_bonus = 0
比如一件皮甲:
id = leather_armor
display_name = 皮甲
item_type = EQUIPMENT
slot_type = ARMOR
attack_bonus = 0
defense_bonus = 2
max_hp_bonus = 20
也就是说:
ItemData 负责所有物品的通用字段
EquipmentData 负责装备特有字段
4. EquipmentData.gd
创建:
res://scripts/items/equipment_data.gd
class_name EquipmentData
extends ItemData
enum SlotType {
WEAPON,
ARMOR,
ACCESSORY
}
@export var slot_type: SlotType = SlotType.WEAPON
@export var attack_bonus: int = 0
@export var defense_bonus: int = 0
@export var max_hp_bonus: int = 0
因为它继承 ItemData,所以它也拥有:
id
display_name
description
icon
item_type
stackable
max_stack
sell_price
创建装备资源时,要记得:
item_type = EQUIPMENT
stackable = false
max_stack = 1
装备一般不堆叠。
5. 创建第一把武器
创建资源:
res://data/items/equipment/wooden_sword.tres
类型选择:
EquipmentData
设置:
id = wooden_sword
display_name = 木剑
description = 一把普通木剑。攻击力 +5。
item_type = EQUIPMENT
stackable = false
max_stack = 1
sell_price = 5
slot_type = WEAPON
attack_bonus = 5
defense_bonus = 0
max_hp_bonus = 0
6. 创建第一件防具
创建:
res://data/items/equipment/leather_armor.tres
设置:
id = leather_armor
display_name = 皮甲
description = 简单的皮革护甲。防御 +2,最大生命 +20。
item_type = EQUIPMENT
stackable = false
max_stack = 1
sell_price = 8
slot_type = ARMOR
attack_bonus = 0
defense_bonus = 2
max_hp_bonus = 20
7. 玩家基础属性和最终属性
玩家属性应该分两层:
基础属性
装备加成
最终属性
比如:
基础攻击力:10
武器攻击力:5
最终攻击力:15
比如:
基础防御:1
防具防御:2
最终防御:3
所以 Player 里可以写:
@export var base_attack: int = 10
@export var base_defense: int = 0
@export var base_max_hp: int = 100
var final_attack: int = 10
var final_defense: int = 0
var max_hp: int = 100
不过你前面已经有:
@export var max_hp: int = 100
var current_hp: int
现在我们要稍微调整:
base_max_hp:基础最大生命
max_hp:最终最大生命
8. Player 属性推荐写法
Player.gd:
signal stats_changed
@export var base_attack: int = 10
@export var base_defense: int = 0
@export var base_max_hp: int = 100
var final_attack: int = 10
var final_defense: int = 0
var max_hp: int = 100
var current_hp: int = 100
初始化时:
func _ready():
recalculate_stats()
current_hp = max_hp
health_changed.emit(current_hp, max_hp)
如果你原来 _ready() 里已经有很多代码,比如信号连接、AttackArea 初始化、InteractArea 初始化,不要新建第二个 _ready()。
要把这些内容合并进去。
9. EquipmentSlots 装备栏
玩家需要知道当前穿了什么。
可以先用 Dictionary:
var equipped_items: Dictionary = {
EquipmentData.SlotType.WEAPON: null,
EquipmentData.SlotType.ARMOR: null,
EquipmentData.SlotType.ACCESSORY: null,
}
含义:
WEAPON 当前装备了什么
ARMOR 当前装备了什么
ACCESSORY 当前装备了什么
更清晰也可以写变量:
var equipped_weapon: EquipmentData = null
var equipped_armor: EquipmentData = null
var equipped_accessory: EquipmentData = null
第一版我更建议用 Dictionary。
因为装备栏以后会变多,Dictionary 更方便遍历。
10. Player 添加装备信号
signal equipment_changed
每次装备改变:
equipment_changed.emit()
stats_changed.emit()
UI 可以监听这些信号来刷新装备面板。
Godot 的 signal 本来就是节点或对象在某件事发生时通知其他对象的一种机制,能减少对象之间直接硬引用,这在 UI 根据玩家状态变化刷新时特别好用。(Godot Engine documentation)
11. recalculate_stats()
这个函数根据基础属性和装备加成计算最终属性。
func recalculate_stats():
final_attack = base_attack
final_defense = base_defense
max_hp = base_max_hp
for item in equipped_items.values():
if item == null:
continue
final_attack += item.attack_bonus
final_defense += item.defense_bonus
max_hp += item.max_hp_bonus
current_hp = min(current_hp, max_hp)
stats_changed.emit()
health_changed.emit(current_hp, max_hp)
重点:
先从基础属性开始
遍历所有装备
把装备加成加上去
如果最大血量变低,当前血量不能超过新的 max_hp
通知 UI 刷新
比如你脱掉皮甲:
max_hp 从 120 变回 100
current_hp 如果原来是 115
就应该变成 100
所以要:
current_hp = min(current_hp, max_hp)
12. equip_item()
装备一个物品。
func equip_item(item: ItemData) -> bool:
if item == null:
return false
if item.item_type != ItemData.ItemType.EQUIPMENT:
return false
if not item is EquipmentData:
return false
var equipment := item as EquipmentData
equipped_items[equipment.slot_type] = equipment
recalculate_stats()
equipment_changed.emit()
print("装备:", equipment.display_name)
return true
这里的关键判断:
if not item is EquipmentData:
return false
因为有些普通 ItemData 也可能误设置成 EQUIPMENT,这个判断能防一手。
13. unequip_item()
卸下某个部位的装备。
func unequip_item(slot_type: EquipmentData.SlotType) -> bool:
if not equipped_items.has(slot_type):
return false
var old_item = equipped_items[slot_type]
if old_item == null:
return false
equipped_items[slot_type] = null
recalculate_stats()
equipment_changed.emit()
print("卸下:", old_item.display_name)
return true
第一版先不考虑:
卸下后是否回到背包
背包满了怎么办
因为我们现在的背包没有容量限制。
实际上,因为装备本来还在背包里,所以卸下只是清空装备栏,不需要额外加回背包。
这是第一版最简单的做法。
14. 装备是否应该从背包里移除
有两种设计。
设计 A:
装备后,物品仍然在背包里
装备栏只是引用它
优点:
简单
不用处理卸下放回背包
缺点:
背包里还能看到已经穿上的装备
可能会重复出售或重复装备
设计 B:
装备后,从背包移除
卸下后,放回背包
优点:
更符合很多 RPG 的逻辑
缺点:
需要处理背包满、替换装备、退回旧装备
第一版建议用设计 A,但 UI 上标记“已装备”。
等背包格子系统更完善后,再改设计 B。
15. get_equipped_item()
func get_equipped_item(slot_type: EquipmentData.SlotType) -> EquipmentData:
return equipped_items.get(slot_type)
UI 显示装备栏时会用。
16. 攻击伤害使用 final_attack
前面 Player 攻击怪物时可能写了:
@export var attack_damage: int = 10
现在改成:
func get_attack_damage() -> int:
return final_attack
命中时:
area.take_damage(get_attack_damage())
原来:
area.take_damage(attack_damage)
改成:
area.take_damage(get_attack_damage())
这样装备武器后,攻击伤害会提高。
17. 防御如何减伤
玩家受到怪物伤害时:
func take_damage(amount: int, attacker: Node = null):
可以改成:
var final_damage = calculate_received_damage(amount)
减伤函数:
func calculate_received_damage(amount: int) -> int:
return max(amount - final_defense, 1)
意思是:
最终伤害 = 原始伤害 - 防御力
但最低至少受到 1 点伤害
比如:
怪物伤害 10
玩家防御 3
实际伤害 7
如果防御 999:
实际伤害最低 1
这样避免防御高了完全无伤。
第一版用这个公式就够。
以后可以改成百分比减伤、护甲公式、元素抗性等。
18. take_damage 使用防御
func take_damage(amount: int, attacker: Node = null):
if current_state == PlayerState.DEAD:
return
if is_invincible:
return
var final_damage = calculate_received_damage(amount)
current_hp -= final_damage
current_hp = clamp(current_hp, 0, max_hp)
health_changed.emit(current_hp, max_hp)
print("玩家受到伤害:", final_damage)
if current_hp <= 0:
change_state(PlayerState.DEAD)
died.emit()
return
change_state(PlayerState.HURT)
start_invincibility()
现在穿防具后,实际受伤会降低。
19. max_hp_bonus 的问题
如果装备皮甲:
max_hp +20
那么当前血量要不要也加 20?
有两种设计。
设计 A:
只提高最大生命
当前生命不变
比如:
100/100
装备皮甲
100/120
设计 B:
提高最大生命时,也补上增加的部分
比如:
100/100
装备皮甲
120/120
第一版建议设计 A。
原因:
更简单
避免通过反复穿脱装备免费回血
如果你想装备时补血,也要小心玩家反复装备刷血的问题。
20. 背包 UI 中点击装备
前面 InventoryPanel 里点击物品是使用药水:
player.use_item(item_id)
现在如果点击的是装备,应该装备它。
Player.use_item() 可以这样改:
func use_item(item_id: String) -> bool:
if current_state == PlayerState.DEAD:
return false
var slot = inventory.find_slot_by_item_id(item_id)
if slot == null:
return false
var item = slot.item
if item.item_type == ItemData.ItemType.CONSUMABLE:
if item.heal_amount > 0:
if current_hp >= max_hp:
print("生命值已满")
return false
heal(item.heal_amount)
inventory.remove_item(item_id, 1)
print("使用:", item.display_name)
return true
if item.item_type == ItemData.ItemType.EQUIPMENT:
return equip_item(item)
return false
这样背包里点击药水是使用,点击装备是穿上。
21. InventoryPanel 显示装备信息
原来的按钮可能是:
木剑 x1
现在可以显示:
木剑 x1 [装备]
如果已经装备了:
木剑 x1 [已装备]
InventoryPanel.gd 里可以问 Player:
func is_equipped(item: ItemData) -> bool:
if player == null:
return false
if not player.has_method("is_item_equipped"):
return false
return player.is_item_equipped(item)
Player.gd:
func is_item_equipped(item: ItemData) -> bool:
if item == null:
return false
for equipped in equipped_items.values():
if equipped == item:
return true
return false
然后 InventoryPanel 按钮文本:
var text = "%s x%d" % [slot.item.display_name, slot.quantity]
if slot.item.item_type == ItemData.ItemType.EQUIPMENT:
if is_equipped(slot.item):
text += " [已装备]"
else:
text += " [装备]"
22. 装备栏 UI
除了背包里显示,还可以做一个装备面板。
结构:
EquipmentPanel Control
└── Panel
├── WeaponLabel Label
├── ArmorLabel Label
├── AccessoryLabel Label
├── AttackLabel Label
├── DefenseLabel Label
└── MaxHPLabel Label
显示:
武器:木剑
防具:皮甲
饰品:无
攻击:15
防御:2
生命:120
第一版用文字就够。
23. EquipmentPanel.gd
extends Control
@export var player: Node
@onready var weapon_label: Label = $Panel/WeaponLabel
@onready var armor_label: Label = $Panel/ArmorLabel
@onready var accessory_label: Label = $Panel/AccessoryLabel
@onready var attack_label: Label = $Panel/AttackLabel
@onready var defense_label: Label = $Panel/DefenseLabel
@onready var max_hp_label: Label = $Panel/MaxHPLabel
func _ready():
visible = false
if player != null:
if player.has_signal("equipment_changed"):
player.equipment_changed.connect(refresh)
if player.has_signal("stats_changed"):
player.stats_changed.connect(refresh)
refresh()
func toggle():
visible = not visible
refresh()
func refresh():
if player == null:
return
weapon_label.text = "武器:" + get_equipment_name(EquipmentData.SlotType.WEAPON)
armor_label.text = "防具:" + get_equipment_name(EquipmentData.SlotType.ARMOR)
accessory_label.text = "饰品:" + get_equipment_name(EquipmentData.SlotType.ACCESSORY)
attack_label.text = "攻击:%d" % player.final_attack
defense_label.text = "防御:%d" % player.final_defense
max_hp_label.text = "生命:%d / %d" % [player.current_hp, player.max_hp]
func get_equipment_name(slot_type: EquipmentData.SlotType) -> String:
if not player.has_method("get_equipped_item"):
return "无"
var item = player.get_equipped_item(slot_type)
if item == null:
return "无"
return item.display_name
24. 打开装备面板
InputMap 添加:
open_equipment
绑定:
C
UI.gd:
@onready var equipment_panel: Control = $EquipmentPanel
func _unhandled_input(event):
if event.is_action_pressed("open_equipment"):
equipment_panel.toggle()
get_viewport().set_input_as_handled()
也可以在 InventoryPanel 里加一个切换按钮。
第一版按 C 打开最简单。
25. 卸下装备按钮
装备面板可以先不做卸下按钮。
如果要做,可以加:
UnequipWeaponButton
UnequipArmorButton
UnequipAccessoryButton
EquipmentPanel.gd:
@onready var unequip_weapon_button: Button = $Panel/UnequipWeaponButton
@onready var unequip_armor_button: Button = $Panel/UnequipArmorButton
@onready var unequip_accessory_button: Button = $Panel/UnequipAccessoryButton
func _ready():
unequip_weapon_button.pressed.connect(func():
unequip(EquipmentData.SlotType.WEAPON)
)
unequip_armor_button.pressed.connect(func():
unequip(EquipmentData.SlotType.ARMOR)
)
unequip_accessory_button.pressed.connect(func():
unequip(EquipmentData.SlotType.ACCESSORY)
)
func unequip(slot_type: EquipmentData.SlotType):
if player != null and player.has_method("unequip_item"):
player.unequip_item(slot_type)
refresh()
第一版可以只做武器和防具。
26. 装备替换
如果你已经穿了木剑,又点击铁剑:
木剑自动被替换
铁剑成为当前武器
因为我们的 equip_item() 是:
equipped_items[equipment.slot_type] = equipment
所以同部位会自动覆盖。
旧装备仍然在背包里。
第一版没问题。
27. 商店售卖装备
装备也是 ItemData。
所以商店可以直接卖装备。
创建:
wooden_sword_shop_item.tres
设置:
item = wooden_sword.tres
price = 20
infinite_stock = false
stock = 1
加入:
weapon_shop.tres
玩家购买后:
wooden_sword 进入背包
打开背包点击木剑
装备成功
攻击力增加
这说明装备系统已经和商店打通了。
28. 怪物掉落装备
怪物掉落逻辑也可以掉装备。
Enemy 里:
drop_item = wooden_sword.tres
但装备通常掉率低。
第一版你可以测试用 100% 掉落。
以后做 DropTable 时再控制概率。
29. 任务奖励装备
QuestData 的 reward_items 也可以放装备。
比如:
完成任务奖励:木剑
因为 player.pick_up_item() 对装备和普通物品都能加背包。
装备不需要特殊处理。
这就是统一 ItemData 系统的好处。
30. 攻击力显示在 HUD 还是装备面板
第一版建议只放装备面板。
HUD 里显示:
血量
金币
装备面板显示:
攻击
防御
最大生命
当前装备
不要把 HUD 塞太满。
HUD 是开车时的仪表盘,不是公司年报。
31. 防御公式以后怎么优化
当前公式:
max(amount - final_defense, 1)
简单好懂。
但缺点:
防御太高会几乎无敌
小怪可能永远只打 1
数值成长容易失控
以后可以用百分比减伤:
var damage_multiplier = 100.0 / (100.0 + final_defense)
var final_damage = max(roundi(amount * damage_multiplier), 1)
比如:
防御 0:受到 100%
防御 50:受到约 66%
防御 100:受到 50%
但第一版不用。
先用减法公式最直观。
32. 装备属性还能有哪些
以后可以加:
critical_chance:暴击率
critical_damage:暴击伤害
move_speed_bonus:移动速度
attack_speed_bonus:攻击速度
magic_power:法术强度
element_type:元素类型
resistance_fire:火抗
resistance_ice:冰抗
但不要现在就加。
第一版只留:
attack_bonus
defense_bonus
max_hp_bonus
够用了。
33. 装备和卡牌系统的关系
你之前提过想加入卡牌元素。
装备系统以后可以和卡牌联动:
武器决定可用攻击卡
饰品增加抽牌数量
防具给防御类卡牌加成
装备提供被动卡牌
比如:
火焰法杖:
攻击 +3
解锁火球卡牌
骑士盾:
防御 +5
战斗开始获得格挡卡
但这属于后面“卡牌系统”。
现在装备先作为数值加成。
34. Player.gd 装备相关完整片段
signal equipment_changed
signal stats_changed
@export var base_attack: int = 10
@export var base_defense: int = 0
@export var base_max_hp: int = 100
var final_attack: int = 10
var final_defense: int = 0
var max_hp: int = 100
var current_hp: int = 100
var equipped_items: Dictionary = {
EquipmentData.SlotType.WEAPON: null,
EquipmentData.SlotType.ARMOR: null,
EquipmentData.SlotType.ACCESSORY: null,
}
func recalculate_stats():
final_attack = base_attack
final_defense = base_defense
max_hp = base_max_hp
for item in equipped_items.values():
if item == null:
continue
final_attack += item.attack_bonus
final_defense += item.defense_bonus
max_hp += item.max_hp_bonus
current_hp = min(current_hp, max_hp)
stats_changed.emit()
health_changed.emit(current_hp, max_hp)
func equip_item(item: ItemData) -> bool:
if item == null:
return false
if item.item_type != ItemData.ItemType.EQUIPMENT:
return false
if not item is EquipmentData:
return false
var equipment := item as EquipmentData
equipped_items[equipment.slot_type] = equipment
recalculate_stats()
equipment_changed.emit()
print("装备:", equipment.display_name)
return true
func unequip_item(slot_type: EquipmentData.SlotType) -> bool:
if not equipped_items.has(slot_type):
return false
var old_item = equipped_items[slot_type]
if old_item == null:
return false
equipped_items[slot_type] = null
recalculate_stats()
equipment_changed.emit()
print("卸下:", old_item.display_name)
return true
func get_equipped_item(slot_type: EquipmentData.SlotType) -> EquipmentData:
return equipped_items.get(slot_type)
func is_item_equipped(item: ItemData) -> bool:
if item == null:
return false
for equipped in equipped_items.values():
if equipped == item:
return true
return false
func get_attack_damage() -> int:
return final_attack
func calculate_received_damage(amount: int) -> int:
return max(amount - final_defense, 1)
35. use_item() 装备版
func use_item(item_id: String) -> bool:
if current_state == PlayerState.DEAD:
return false
var slot = inventory.find_slot_by_item_id(item_id)
if slot == null:
return false
var item = slot.item
if item.item_type == ItemData.ItemType.CONSUMABLE:
if item.heal_amount > 0:
if current_hp >= max_hp:
print("生命值已满")
return false
heal(item.heal_amount)
inventory.remove_item(item_id, 1)
print("使用:", item.display_name)
return true
if item.item_type == ItemData.ItemType.EQUIPMENT:
return equip_item(item)
return false
36. 攻击命中改成 final_attack
func _on_attack_area_entered(area):
if current_state != PlayerState.ATTACK:
return
if not area.is_in_group("hurtbox"):
return
if hit_targets.has(area):
return
hit_targets.append(area)
if area.has_method("take_damage"):
area.take_damage(get_attack_damage())
37. 受伤改成防御减伤
func take_damage(amount: int, attacker: Node = null):
if current_state == PlayerState.DEAD:
return
if is_invincible:
return
var final_damage = calculate_received_damage(amount)
current_hp -= final_damage
current_hp = clamp(current_hp, 0, max_hp)
health_changed.emit(current_hp, max_hp)
print("玩家受到伤害:", final_damage)
if current_hp <= 0:
change_state(PlayerState.DEAD)
died.emit()
return
change_state(PlayerState.HURT)
start_invincibility()
38. 常见错误 1:创建不了 EquipmentData 资源
检查:
EquipmentData.gd 是否有 class_name EquipmentData
是否 extends ItemData
ItemData.gd 是否有 class_name ItemData
脚本是否有语法错误
如果 ItemData.gd 有报错,EquipmentData 也会创建不了。
39. 常见错误 2:装备点击后没变化
排查:
装备资源 item_type 是否设置为 EQUIPMENT
装备资源是否是 EquipmentData,不是普通 ItemData
InventoryPanel 点击是否调用 player.use_item()
Player.use_item 是否处理 EQUIPMENT
Player.equip_item 是否成功
临时打印:
print("点击使用:", item.display_name, " 类型:", item.item_type)
40. 常见错误 3:攻击力没增加
排查:
EquipmentData.attack_bonus 是否大于 0
equip_item 后是否调用 recalculate_stats
攻击命中时是否调用 get_attack_damage()
是否还在用旧的 attack_damage 变量
如果你还写着:
area.take_damage(attack_damage)
那装备攻击力当然不会生效。
要改成:
area.take_damage(get_attack_damage())
41. 常见错误 4:防御没生效
排查:
take_damage 是否调用 calculate_received_damage
final_defense 是否正确计算
防具 defense_bonus 是否设置
装备是否真的穿上
临时打印:
print("原始伤害:", amount, " 防御:", final_defense, " 最终伤害:", final_damage)
42. 常见错误 5:装备最大血量后血条不变
排查:
max_hp_bonus 是否设置
recalculate_stats 是否更新 max_hp
recalculate_stats 是否 emit health_changed
PlayerHUD 是否监听 health_changed
血条 UI 不会自己知道 max_hp 变了。
必须 emit:
health_changed.emit(current_hp, max_hp)
43. 常见错误 6:装备后背包 UI 不显示已装备
检查:
Player 是否有 is_item_equipped 方法
InventoryPanel 是否能拿到 player
刷新时是否判断 item_type == EQUIPMENT
装备后是否 emit equipment_changed
InventoryPanel 是否监听 equipment_changed 或手动 refresh
可以在 UI 里连接:
player.equipment_changed.connect(refresh)
44. 常见错误 7:同一把装备卖掉后还穿着
这是设计 A 的缺点。
因为装备后仍然保留在背包里,如果商店出售该物品,装备栏可能还引用着这个 Resource。
解决方案:
第一版:暂时不允许出售已装备物品
出售时判断:
if current_player.has_method("is_item_equipped"):
if current_player.is_item_equipped(slot.item):
print("已装备物品不能出售")
return
ShopPanel 的出售按钮也可以禁用。
这是一个很重要的小补丁。
45. 禁止出售已装备物品
ShopPanel.gd 的 add_sell_item_button 里:
var is_equipped = false
if current_player != null and current_player.has_method("is_item_equipped"):
is_equipped = current_player.is_item_equipped(slot.item)
button.disabled = sell_price <= 0 or is_equipped
if is_equipped:
button.text += " [已装备]"
sell_item 里也防一次:
if current_player.has_method("is_item_equipped"):
if current_player.is_item_equipped(slot.item):
print("已装备物品不能出售")
return
UI 禁用是用户体验。
函数里判断是安全保护。
两层都要有,才稳。
46. 这一部分最重要的记忆点
装备本质上也是一种物品
EquipmentData 可以继承 ItemData
装备栏记录当前穿了什么
基础属性和最终属性要分开
recalculate_stats 根据装备重新计算属性
武器影响 final_attack
防具影响 final_defense 和 max_hp
攻击时用 get_attack_damage()
受伤时用 calculate_received_damage()
装备变化要发 equipment_changed / stats_changed
第一版装备后可以仍留在背包里,但要禁止出售已装备物品
47. 你现在的小练习
建议你按这个顺序做:
1. 创建 EquipmentData.gd
2. 创建 wooden_sword.tres
3. 创建 leather_armor.tres
4. Player 添加 base_attack / base_defense / base_max_hp
5. Player 添加 final_attack / final_defense / max_hp
6. Player 添加 equipped_items
7. Player 写 recalculate_stats()
8. Player 写 equip_item()
9. Player 写 unequip_item()
10. Player.use_item 支持 EQUIPMENT
11. 攻击命中改用 get_attack_damage()
12. 受伤改用 calculate_received_damage()
13. 背包里放一个 wooden_sword
14. 点击 wooden_sword 后攻击力增加
15. 打怪时伤害提高
16. 背包里放一个 leather_armor
17. 点击 leather_armor 后防御和最大生命增加
18. 怪物攻击玩家时伤害降低
19. 添加 EquipmentPanel
20. 按 C 打开装备面板
21. 显示武器、防具、攻击、防御、生命
22. 商店出售 wooden_sword
23. 玩家购买后装备
24. 禁止出售已装备物品
完成这部分之后,你的 RPG 就有了“获得装备 → 装备变强 → 战斗数值改变 → 商店买卖装备”的成长闭环。
如果您觉得这篇文章有帮助,请点个赞吧~
评论
请登录后发表评论
去登录