对 Godot 中节点设计的思考
单个节点的功能设计的想法,体会 Godot 的设计思想
低耦合
设计单个节点可复用的节点时,调用方法尽量只对当前节点可获取到的变量或方法进行使用,比如我写一个可以控制 KinematicBody2D 节点的“控制器节点”,这时我们需要设计一个获取要控制对应节点的方法,以 get_host()
作为获取要控制的节点的方法举例。
比如我想获取当前场景的根节点作为控制的对象:
## 返回操控的宿主
func get_host() -> KinematicBody2D:
return get_owner() as KinematicBody2D
或者是以 NodePath
作为获取要控制的节点:
export var host = @""
func get_host() -> KinematicBody2D:
return get_node(host) as KinematicBody2D
也可以通过变量进行获取,我以前喜欢这样写,但是现在不喜欢这样,因为有时候我可能计划有变,需要在获取 host 时对 host 作出一些判断或修改,这样写又该重新再写个方法。
onready var host = get_owner() as KinematicBody2D
不要直接通过指定固定的值来获取外部的节点的方式!
func get_host() -> KinematicBody2D:
return get_node("../Player") as KinematicBody2D # 不要这样写
因为这样写在后来修改节点结构时,或者用在其他名字不为 Player
的节点下时,需要进行频繁的修改,这样做会非常容易出现 bug。
如果想要对节点进行"不属于当前节点功能的操作"时,需要发射信号进行处理
比如在节点向前冲刺时想要将碰撞到的节点进行“眩晕操作”,那么将其分开处理,一个冲刺节点,一个眩晕节点。
将冲刺时碰撞到的节点发出信号
signal collision_to_unit(collider_list) # 碰撞到单位
然后连接到眩晕节点的对一组节点“进行眩晕”的操作的方法。
func vertigo(collider_list: Array):
for node in collider_list:
# 下面写对接点的眩晕操作
pass
如果这时想扩展冲刺方法,想在升级时再造成伤害,那么就可以创建一个伤害节点,或者直接连接到主场景,在里边写如何造成伤害的方法
# 连接sprint(冲刺)节点的 collision_to_unit 信号
func _on_Sprint_collision_to_unit(collider_list):
for node in collider_list:
# 下面写对节点造成伤害操作
pass
这样写完,你的 冲刺节点 可以重新拿来在其他新的游戏中再次被重新利用,不需再重新写代码,节约时间。降低耦合度。
看一个示例:
下面的 技能1 是一个复合型的技能,它包含 冲刺、眩晕、伤害 的能力,如下
这样一看一目了然。
调用 技能1 的方法时,比如 cast_skill
进行调用技能,会在 cast_skill
下调用 冲刺 节点的调用技能的方法。然后连接冲刺节点的 collision_to_unit
信号到 技能1
的场景中,对碰撞到的单位进行一个循环遍历,然后调用 眩晕 伤害 节点的技能,逐个造成伤害和眩晕。提高了 技能1 节点的可扩展性,也降低了耦合,节点结构也清晰。
高内聚
一个节点的功能会根据不同的节点的变化而变化,比如 CollisionObject2D
类型的节点
下面可以添加 规则碰撞图形 和 不规则碰撞图形
CollisionObject2D
类型的节点,只对碰撞的结果进行处理,而碰撞形状不属于 碰撞的对象,而将功能分离,单独做成碰撞形状节点,和 CollisionObject2D
类型的节点一起使用。
如果一个功能是在需要时才用到的,那么将它设计成 Resource
属性或者独立的方法,比如 RigidBody2D
中的 Physics Material
属性。
如 CollisionShape2D
的 shape
属性