godot-rpg-stats
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseRPG Stats
RPG属性系统
Resource-based stats, modifier stacks, and derived calculations define flexible character progression.
基于资源的属性、modifier堆叠以及衍生计算定义了灵活的角色成长机制。
Available Scripts
可用脚本
stat_resource.gd
stat_resource.gd
Robust Resource-based stat system with caching, dirty flags, and modifier stacks.
功能完善的基于资源的属性系统,支持缓存、脏标记和modifier堆叠。
modifier_stack_stats.gd
modifier_stack_stats.gd
Expert stat system with additive/multiplicative modifier stacks and priority ordering.
专家级属性系统,支持加法/乘法modifier堆叠和优先级排序。
NEVER Do in RPG Stats
RPG属性系统开发禁忌
- NEVER use int for percentages — for 50%? Integer division = truncation errors. Use
var critical_chance: int = 50(0.0-1.0 OR 0.0-100.0).float - NEVER modify stats without signals — UI showing health bar but doesn't update? MUST emit signals on stat changes.
stats.current_health -= 10 - NEVER use additive-only modifiers — Buff adds +10 strength on level 1 (10 base) = 100% increase. Same buff on level 50 (100 base) = 10% increase. Use multiplicative OR hybrid.
- NEVER skip modifier IDs — without ID? Can't remove specific buffs later. MUST use unique IDs (e.g., "sword_buff", "potion_123").
add_modifier("strength", 5) - NEVER use exponential XP formulas without cap — ? Level 100 = 100k XP, level 1000 = 1M. Use sqrt/log OR flat scaling.
xp_to_next = level * 1000 - NEVER forget to clamp derived stats — ? Negative vitality from debuff = negative health = crash. Use
max_health = vitality * 10.maxi(value, 1)
gdscript
undefined- 永远不要用整数存储百分比 — 用表示50%?整数除法会导致截断误差。请使用
var critical_chance: int = 50类型(取值范围0.0-1.0 或 0.0-100.0)。float - 永远不要不触发信号就修改属性 — 显示生命值条的UI在执行后没有更新?属性变更时必须触发信号。
stats.current_health -= 10 - 永远不要只使用加法modifier — 1级角色基础力量为10,buff加10点力量相当于提升100%;50级角色基础力量为100,同样的buff仅提升10%。请使用乘法或混合模式的modifier。
- 永远不要省略modifier ID — 调用时不带ID?后续就无法移除特定的buff效果。必须使用唯一ID(例如:"sword_buff"、"potion_123")。
add_modifier("strength", 5) - 永远不要使用无上限的指数级XP公式 — 比如?100级需要10万XP,1000级就需要100万XP。请使用平方根/对数或者固定梯度的成长公式。
xp_to_next = level * 1000 - 永远不要忘记对衍生属性做范围限制 — 比如?debuff导致活力为负的话,生命值也会变成负数,进而引发崩溃。请使用
max_health = vitality * 10做下限限制。maxi(value, 1)
gdscript
undefinedstats.gd
stats.gd
class_name Stats
extends Resource
signal stat_changed(stat_name: String, old_value: float, new_value: float)
signal level_up(new_level: int)
@export var level: int = 1
@export var experience: int = 0
@export var experience_to_next_level: int = 100
class_name Stats
extends Resource
signal stat_changed(stat_name: String, old_value: float, new_value: float)
signal level_up(new_level: int)
@export var level: int = 1
@export var experience: int = 0
@export var experience_to_next_level: int = 100
Base stats
Base stats
@export var strength: int = 10
@export var dexterity: int = 10
@export var intelligence: int = 10
@export var vitality: int = 10
@export var strength: int = 10
@export var dexterity: int = 10
@export var intelligence: int = 10
@export var vitality: int = 10
Derived stats (calculated from base)
Derived stats (calculated from base)
var max_health: int:
get: return vitality * 10
var attack_power: int:
get: return strength * 2
var defense: int:
get: return strength + (vitality / 2)
var magic_power: int:
get: return intelligence * 3
var critical_chance: float:
get: return dexterity * 0.01
var max_health: int:
get: return vitality * 10
var attack_power: int:
get: return strength * 2
var defense: int:
get: return strength + (vitality / 2)
var magic_power: int:
get: return intelligence * 3
var critical_chance: float:
get: return dexterity * 0.01
Modifiers
Modifiers
var modifiers: Dictionary = {}
func add_experience(amount: int) -> void:
experience += amount
while experience >= experience_to_next_level:
level_up_character()func level_up_character() -> void:
level += 1
experience -= experience_to_next_level
experience_to_next_level = int(experience_to_next_level * 1.5)
# Increase base stats
strength += 2
dexterity += 2
intelligence += 2
vitality += 2
level_up.emit(level)func get_stat(stat_name: String) -> float:
var base_value: float = get(stat_name)
var modifier_bonus := get_modifier_total(stat_name)
return base_value + modifier_bonus
func add_modifier(stat_name: String, modifier_id: String, value: float) -> void:
if not modifiers.has(stat_name):
modifiers[stat_name] = {}
modifiers[stat_name][modifier_id] = valuefunc remove_modifier(stat_name: String, modifier_id: String) -> void:
if modifiers.has(stat_name):
modifiers[stat_name].erase(modifier_id)
func get_modifier_total(stat_name: String) -> float:
if not modifiers.has(stat_name):
return 0.0
var total := 0.0
for value in modifiers[stat_name].values():
total += value
return totalundefinedvar modifiers: Dictionary = {}
func add_experience(amount: int) -> void:
experience += amount
while experience >= experience_to_next_level:
level_up_character()func level_up_character() -> void:
level += 1
experience -= experience_to_next_level
experience_to_next_level = int(experience_to_next_level * 1.5)
# Increase base stats
strength += 2
dexterity += 2
intelligence += 2
vitality += 2
level_up.emit(level)func get_stat(stat_name: String) -> float:
var base_value: float = get(stat_name)
var modifier_bonus := get_modifier_total(stat_name)
return base_value + modifier_bonus
func add_modifier(stat_name: String, modifier_id: String, value: float) -> void:
if not modifiers.has(stat_name):
modifiers[stat_name] = {}
modifiers[stat_name][modifier_id] = valuefunc remove_modifier(stat_name: String, modifier_id: String) -> void:
if modifiers.has(stat_name):
modifiers[stat_name].erase(modifier_id)
func get_modifier_total(stat_name: String) -> float:
if not modifiers.has(stat_name):
return 0.0
var total := 0.0
for value in modifiers[stat_name].values():
total += value
return totalundefinedEquipment Stats
装备属性
gdscript
undefinedgdscript
undefinedequipment_item.gd
equipment_item.gd
extends Item
class_name EquipmentItem
@export var stat_bonuses: Dictionary = {
"strength": 5,
"dexterity": 3
}
func on_equip(stats: Stats) -> void:
for stat_name in stat_bonuses:
stats.add_modifier(stat_name, "equipment_" + id, stat_bonuses[stat_name])
func on_unequip(stats: Stats) -> void:
for stat_name in stat_bonuses:
stats.remove_modifier(stat_name, "equipment_" + id)
undefinedextends Item
class_name EquipmentItem
@export var stat_bonuses: Dictionary = {
"strength": 5,
"dexterity": 3
}
func on_equip(stats: Stats) -> void:
for stat_name in stat_bonuses:
stats.add_modifier(stat_name, "equipment_" + id, stat_bonuses[stat_name])
func on_unequip(stats: Stats) -> void:
for stat_name in stat_bonuses:
stats.remove_modifier(stat_name, "equipment_" + id)
undefinedStatus Effects
状态效果
gdscript
undefinedgdscript
undefinedstatus_effect.gd
status_effect.gd
class_name StatusEffect
extends Resource
@export var effect_id: String
@export var duration: float
@export var stat_modifiers: Dictionary = {}
func apply(stats: Stats) -> void:
for stat_name in stat_modifiers:
stats.add_modifier(stat_name, "status_" + effect_id, stat_modifiers[stat_name])
func remove(stats: Stats) -> void:
for stat_name in stat_modifiers:
stats.remove_modifier(stat_name, "status_" + effect_id)
undefinedclass_name StatusEffect
extends Resource
@export var effect_id: String
@export var duration: float
@export var stat_modifiers: Dictionary = {}
func apply(stats: Stats) -> void:
for stat_name in stat_modifiers:
stats.add_modifier(stat_name, "status_" + effect_id, stat_modifiers[stat_name])
func remove(stats: Stats) -> void:
for stat_name in stat_modifiers:
stats.remove_modifier(stat_name, "status_" + effect_id)
undefinedDamage Calculation
伤害计算
gdscript
func calculate_damage(attacker_stats: Stats, defender_stats: Stats) -> float:
var base_damage := float(attacker_stats.attack_power)
var defense := float(defender_stats.defense)
# Damage reduction formula
var damage := base_damage * (100.0 / (100.0 + defense))
# Critical hit
if randf() < attacker_stats.critical_chance:
damage *= 2.0
return maxf(damage, 1.0) # Minimum 1 damagegdscript
func calculate_damage(attacker_stats: Stats, defender_stats: Stats) -> float:
var base_damage := float(attacker_stats.attack_power)
var defense := float(defender_stats.defense)
# Damage reduction formula
var damage := base_damage * (100.0 / (100.0 + defense))
# Critical hit
if randf() < attacker_stats.critical_chance:
damage *= 2.0
return maxf(damage, 1.0) # Minimum 1 damageSkill Requirements
技能要求
gdscript
undefinedgdscript
undefinedskill.gd
skill.gd
class_name Skill
extends Resource
@export var required_level: int = 1
@export var required_stats: Dictionary = {
"strength": 15,
"intelligence": 10
}
func can_use(stats: Stats) -> bool:
if stats.level < required_level:
return false
for stat_name in required_stats:
if stats.get_stat(stat_name) < required_stats[stat_name]:
return false
return trueundefinedclass_name Skill
extends Resource
@export var required_level: int = 1
@export var required_stats: Dictionary = {
"strength": 15,
"intelligence": 10
}
func can_use(stats: Stats) -> bool:
if stats.level < required_level:
return false
for stat_name in required_stats:
if stats.get_stat(stat_name) < required_stats[stat_name]:
return false
return trueundefinedBest Practices
最佳实践
- Derived Stats - Calculate from base stats
- Modifiers - Temporary/permanent bonuses
- Formula Balance - Avoid exponential power creep
- 衍生属性 - 基于基础属性计算得出
- Modifier - 支持临时/永久加成
- 公式平衡性 - 避免指数级数值膨胀
Reference
参考
- Related: ,
godot-combat-systemgodot-inventory-system
- 相关项目:、
godot-combat-systemgodot-inventory-system
Related
相关内容
- Master Skill: godot-master
- 核心技能:godot-master