血量

  • 最大值
  • 最小值0,=0后触发死亡信号
  • 受到伤害后减少血量
  • 受到治疗或使用血瓶时增加血量
  • 改变最大值(比如升级后)或者受到物品的影响

在游戏中我们使用Health来表示血量,而不是Blood,以前的游戏会用HP表示血量。在游戏中,HP是Health Point的缩写,意思是生命值。

血量

  1. class_name Health
  2. extends Node
  3. var max_health = 200 # 血量最大值
  4. var min_health = 0 # 血量最小值
  5. var _health # 当前血量
  6. signal dead() # 死亡信号
  7. # 增加血量
  8. func add_helth(val:int):
  9. if _health + val <= max_health:
  10. _health += val
  11. else:
  12. _health = max_health
  13. # 减少血量
  14. func sub_helth(val:int):
  15. if _health - val >= min_health:
  16. _health -= val
  17. else: # 血量小于0或最小限定值
  18. _health = min_health
  19. emit_signal("dead") # 触发“死亡”信号
  20. # 修改最大血量值
  21. func change_max_health(new_val:int):
  22. max_health = new_val

在上面的基础之上,你可以设计当血量少于一定值时,玩家处于疲惫或心慌的状态,从而降低玩家的移动和攻击速度,乃至攻击的伤害值。而当血量高于一个值时,又可以恢复正常。
另外在某些游戏中血量和能量被划分出来,玩家连续工作或不休息都会使得玩家进入疲惫状态,甚至出现过劳死。(比如《星露谷物语》)。

代码改进

根据@毒游匠的提醒,可以使用clamp,也就是“钳制”来简化血量赋值代码。
image.png
image.png
修改后的代码:

  1. class_name Health
  2. extends Node
  3. export var max_health = 200 # 血量最大值
  4. var _health # 当前血量
  5. signal dead() # 死亡信号
  6. # 增加血量
  7. func add_helth(val:int):
  8. _health = clamp(_health + val,0,max_health)
  9. # 减少血量
  10. func sub_helth(val:int):
  11. _health = clamp(_health - val,0,max_health)
  12. if _health == 0:emit_signal("dead") # 触发“死亡”信号
  13. # 修改最大血量值
  14. func change_max_health(new_val:int):
  15. max_health = new_val

经验和等级

EXP 在游戏里是经验值,experience的缩写。
LV 是level的简写,英文中,level也是等级的意思。
每一级升到下一级所需要的经验值是不一样的。所有等级所需要的经验值绘制成一个曲线,可以展现为J型曲线、S型曲线和直线等。不同的曲线代表了不同的升级难度变化。
经验值一般与游戏游玩时间成正比,在很多游戏中与杀敌数挂钩,像《魔兽争霸》中,还可以利用打怪,以及“经验之书”这样的随机掉落物品来实现增加。

  1. class_name LV_EXP
  2. extends Node
  3. # 所有可用的经验值
  4. var lvs = {
  5. lv1=500,
  6. lv2=2000,
  7. lv3=4000,
  8. lv4=8000,
  9. lv5=16000
  10. }
  11. signal level_changed() # 等级发生变化
  12. signal exp_changed() # 经验值发生变化
  13. signal max_exp_changed() # 最大经验值发生变化
  14. var _level = 1 # 当前等级
  15. var _max_exp = lvs["lv" +str(_level)] # 当前等级升级所需要的最大经验值
  16. var _exp = 0 # 当前经验值
  17. # 升级
  18. func level_up():
  19. _level = clamp(_level + 1,1,lvs.size())
  20. _max_exp = lvs["lv" +str(_level)]
  21. emit_signal("max_exp_changed")
  22. emit_signal("level_changed",_level)
  23. # 增加经验值
  24. func add_exp(val:int):
  25. _exp = clamp(_exp + val,0,_max_exp)
  26. if _exp + val >= _max_exp:
  27. _exp = _exp + val - _max_exp # 多出来的值被累积到下一级
  28. level_up()
  29. emit_signal("exp_changed",_exp)
  30. # 修改经验值
  31. func change_exp(new_val:int):
  32. _exp = clamp(new_val,0,_max_exp)
  33. if new_val >= _max_exp:
  34. _exp = new_val - _max_exp # 多出来的值被累积到下一级
  35. level_up()
  36. emit_signal("exp_changed",_exp)

实现一个简单的组件模式

假设Node2D的我们的玩家节点,则我们可以直接以节点形式添加我们的组件类。
image.png
我们可以在添加节点对话框中以名称搜索到我们自定义的类型并快速添加。
image.png
但是这时附加的代码是原来的类脚本,这就是Godot设计不合理的地方了。你帮我extends一下原来的类型不行吗??
所以我们要做的多余的一步就是,删除我们的组件节点的附加脚本,重新添加一个之后,extends一下我们定义的类型。

  1. extends Node2D
  2. onready var health = $Health
  3. onready var lv_exp = $LV_EXP
  4. func _ready():
  5. print(health._health)
  6. func _on_Health_dead():
  7. # 播放死亡动画和音效
  8. pass
  9. func _on_LV_EXP_exp_changed():
  10. # 更新经验值的显示或什么都不做
  11. pass
  12. func _on_LV_EXP_level_changed():
  13. # 更新等级的显示或什么都不做
  14. pass
  15. func _on_LV_EXP_max_exp_changed():
  16. # 更新最大经验值的显示或什么都不做
  17. pass