如何为Godot制作Multimesh构建脚本?
how to make Multimesh construction script for Godot?
Multimesh 有 StaticBody 和 CollisionBody。
我的 Multimesh 上有这个脚本,它使一组对象连续排列。 (例如栅栏)
tool
extends MultiMeshInstance
export (float) var distance:float = 1.0 setget set_distance
export (int) var count:int = 1 setget set_count
export (Mesh) var mesh:Mesh setget set_mesh
export (Vector3) var rotMesh:Vector3 setget set_rotMesh
export (Vector3) var sclMesh:Vector3 setget set_sclMesh
export (Vector3) var colMesh:Vector3
onready var coll = get_node("static/collision")
func set_distance(new_distance):
distance = new_distance
update()
func set_count(new_count):
count = new_count
update()
func set_mesh(new_mesh):
mesh = new_mesh
update()
func set_rotMesh(new_rot):
rotMesh = new_rot
update()
func set_sclMesh(new_scl):
sclMesh = new_scl
update()
func update():
self.multimesh = MultiMesh.new()
self.multimesh.transform_format = MultiMesh.TRANSFORM_3D
self.multimesh.instance_count = count
self.multimesh.visible_instance_count = count
self.multimesh.mesh = mesh
var offset = Vector3(0,0,0)
var trfMesh:Basis = Basis(rotMesh)
var extents = Vector3(colMesh.x*distance*count,colMesh.y,colMesh.z)
var shape:Shape = BoxShape.new()
shape.extents = extents
coll.Shape = shape # or coll.shape the same error
trfMesh = trfMesh.scaled(sclMesh)
for i in range(count):
self.multimesh.set_instance_transform(i, Transform(trfMesh, offset))
offset.x += distance
我想自动设置 CollisionShape,或者至少通过我的脚本设置一些参数。
当我尝试设置 CollisionShape 时,我得到:
res://scene/test.gd:40 - Invalid set index 'Shape' (on base: 'Nil') with value of type 'BoxShape'.
在场景加载期间,Godot 将遵循以下执行顺序(new version):
- 分配新节点,
所有变量都归零。常规变量(没有onready
)在这里初始化为它们的默认值(如果它们是export
,该值在第 3 步被覆盖,自定义 setter 或不),如果未指定默认值,则归零。
- 调用
_init
就可以了。
- 设置其属性(初始化
export
variables, and any custom setters运行)。
- 如果有应该是子节点但尚未执行步骤 1 到 3 的节点,请对它们执行相同的步骤。
- IDE 建立信号连接(这发生在所有节点都完成步骤 1 到 3 之后,是的,这包括未来子节点的连接)。
- 将节点添加到其父节点。
NOTIFICATION_PARENTED
(18).
NOTIFICATION_ENTER_TREE
(10).
- 发送
tree_entered
信号。
- 如果有节点,应该是子节点,但还没有执行步骤 6 到 9,请对它们执行相同的步骤。
NOTIFICATION_POST_ENTER_TREE
(27).
- 初始化任何
onready
variables。
- 调用
_ready
就可以了。
NOTIFICATION_READY
(13).
- 发送
ready
信号。
一个重要的警告是 NOTIFICATION_ENTER_TREE
和 tree_entered
如果父级在 SceneTree
中(例如,如果节点是从脚本创建但尚未添加),也会发生它们不会出现在编辑器中(对于 tool
脚本)。说到 tool
脚本,从 11 开始的步骤不会在编辑器中发生。 基本上 ready
和 enter_tree
在编辑器上不起作用。
另请参阅:
- 节点constants, and signals.
- Godot notifications.
_notification
(exampe).
因此,当您的 setter 调用 update
(上面的第 3 步)时,此行尚未 运行(它会 运行 在第 12 步):
onready var coll = get_node("static/collision")
因此,coll
是 null Nil
在:
coll.Shape = shape # or coll.shape the same error
当然,Godot 无法访问 Nil
的 Shape
。
一个常见的解决方案是遵循这种模式:
func set_distance(new_distance):
distance = new_distance
if not is_inside_tree():
yield(self, "ready")
update()
这意味着当 setter 被调用时,但节点还不在场景树中,Godot 将停止执行,直到它获得 ready
信号(发生在节点之后在场景树中,onready
个变量被初始化)。
但是,还有一个问题!这是一个tool
脚本!
在编辑器中运行ning时,onready
和ready
不起作用,加上is_inside_tree
会returntrue
。因此,在编辑器中,它将调用 update
但 coll
是 Nil
.
您可以使用 Engine.editor_hint
来阻止 setter 在编辑器中调用 update
,如下所示:
func set_distance(new_distance):
distance = new_distance
if Engine.editor_hint:
return
if not is_inside_tree():
yield(self, "ready")
update()
请记住,对于 tool
脚本,您不能依赖 onready
。我建议使用 get_node_or_null
并检查 null
。所以你可以这样做:
func set_distance(new_distance):
distance = new_distance
coll = get_node_or_null("static/collision")
if coll == null:
return
update()
这样,Godot在初始化时调用setter时会找不到节点,不会调用update
。 当然你也可以把那张支票放在update
里面。
我们可以做得更好。在 运行 时间 Godot 将调用 setter... 所以我们将 yield
在 ready
之后继续执行,此时它应该找到节点,除非有什么出错了。
func set_distance(new_distance):
distance = new_distance
if not Engine.editor_hint and not is_inside_tree():
yield(self, "ready")
coll = get_node_or_null("static/collision")
if coll == null:
return
update()
如果出现问题,我们可能会通知:
func set_distance(new_distance):
distance = new_distance
if not Engine.editor_hint and not is_inside_tree():
yield(self, "ready")
coll = get_node_or_null("static/collision")
if coll == null:
if not Engine.editor_hint:
push_error("static/collision not found")
return
update()
提取到另一个函数:
func set_distance(new_distance):
distance = new_distance
if not Engine.editor_hint and not is_inside_tree():
yield(self, "ready")
if can_update():
update()
func can_update() -> bool:
coll = get_node_or_null("static/collision")
if coll == null:
if not Engine.editor_hint:
push_error("static/collision not found")
return false
return true
您可以在所有 setter 中以相同的模式重用提取的函数。使其适应手头脚本的任何有意义的内容。这里它只关心你在 update
中使用的节点,但你可以根据需要制作任何逻辑。而且,是的,您也可以在 update
内部进行。
有些事情已经成为一种常见的做法:导出一个 bool 变量作为更新按钮。
我们可以用我上面描述的模式来做到这一点:
export (bool) var refresh:bool setget set_refresh
func set_refresh(new_value):
refresh = new_value
if not Engine.editor_hint and not is_inside_tree():
yield(self, "ready")
if can_update():
update()
refresh = false
这将在检查器面板上添加一个 Refresh
属性,带有一个您可以单击的复选框。如果可能,会调用 update
。另外,该复选框保持未选中状态。这个 refresh
总是错误的。
想法是您可以在编辑器中单击它以在需要时调用 update
。
此外,这个setter也会在运行时间内执行,并在初始化后不久调用update
。你可以让它只在编辑器上做一些事情:
export (bool) var refresh:bool setget set_refresh
func set_refresh(new_value):
refresh = new_value
if Engine.editor_hint and can_update():
update()
refresh = false
您可以添加 _get_configuration_warning
以提供将出现在场景面板中的警告(类似于 PhysicsBody
告诉您它需要 CollisionShape
或 CollisionPolygon
的方式):
func _get_configuration_warning():
if can_update():
return ""
return "static/collision not found"
Multimesh 有 StaticBody 和 CollisionBody。 我的 Multimesh 上有这个脚本,它使一组对象连续排列。 (例如栅栏)
tool
extends MultiMeshInstance
export (float) var distance:float = 1.0 setget set_distance
export (int) var count:int = 1 setget set_count
export (Mesh) var mesh:Mesh setget set_mesh
export (Vector3) var rotMesh:Vector3 setget set_rotMesh
export (Vector3) var sclMesh:Vector3 setget set_sclMesh
export (Vector3) var colMesh:Vector3
onready var coll = get_node("static/collision")
func set_distance(new_distance):
distance = new_distance
update()
func set_count(new_count):
count = new_count
update()
func set_mesh(new_mesh):
mesh = new_mesh
update()
func set_rotMesh(new_rot):
rotMesh = new_rot
update()
func set_sclMesh(new_scl):
sclMesh = new_scl
update()
func update():
self.multimesh = MultiMesh.new()
self.multimesh.transform_format = MultiMesh.TRANSFORM_3D
self.multimesh.instance_count = count
self.multimesh.visible_instance_count = count
self.multimesh.mesh = mesh
var offset = Vector3(0,0,0)
var trfMesh:Basis = Basis(rotMesh)
var extents = Vector3(colMesh.x*distance*count,colMesh.y,colMesh.z)
var shape:Shape = BoxShape.new()
shape.extents = extents
coll.Shape = shape # or coll.shape the same error
trfMesh = trfMesh.scaled(sclMesh)
for i in range(count):
self.multimesh.set_instance_transform(i, Transform(trfMesh, offset))
offset.x += distance
我想自动设置 CollisionShape,或者至少通过我的脚本设置一些参数。
当我尝试设置 CollisionShape 时,我得到:
res://scene/test.gd:40 - Invalid set index 'Shape' (on base: 'Nil') with value of type 'BoxShape'.
在场景加载期间,Godot 将遵循以下执行顺序(new version):
- 分配新节点,
所有变量都归零。常规变量(没有onready
)在这里初始化为它们的默认值(如果它们是export
,该值在第 3 步被覆盖,自定义 setter 或不),如果未指定默认值,则归零。 - 调用
_init
就可以了。 - 设置其属性(初始化
export
variables, and any custom setters运行)。 - 如果有应该是子节点但尚未执行步骤 1 到 3 的节点,请对它们执行相同的步骤。
- IDE 建立信号连接(这发生在所有节点都完成步骤 1 到 3 之后,是的,这包括未来子节点的连接)。
- 将节点添加到其父节点。
NOTIFICATION_PARENTED
(18).NOTIFICATION_ENTER_TREE
(10).- 发送
tree_entered
信号。 - 如果有节点,应该是子节点,但还没有执行步骤 6 到 9,请对它们执行相同的步骤。
NOTIFICATION_POST_ENTER_TREE
(27).- 初始化任何
onready
variables。 - 调用
_ready
就可以了。 NOTIFICATION_READY
(13).- 发送
ready
信号。
一个重要的警告是 NOTIFICATION_ENTER_TREE
和 tree_entered
如果父级在 SceneTree
中(例如,如果节点是从脚本创建但尚未添加),也会发生它们不会出现在编辑器中(对于 tool
脚本)。说到 tool
脚本,从 11 开始的步骤不会在编辑器中发生。 基本上 ready
和 enter_tree
在编辑器上不起作用。
另请参阅:
- 节点constants, and signals.
- Godot notifications.
_notification
(exampe).
因此,当您的 setter 调用 update
(上面的第 3 步)时,此行尚未 运行(它会 运行 在第 12 步):
onready var coll = get_node("static/collision")
因此,coll
是 null Nil
在:
coll.Shape = shape # or coll.shape the same error
当然,Godot 无法访问 Nil
的 Shape
。
一个常见的解决方案是遵循这种模式:
func set_distance(new_distance):
distance = new_distance
if not is_inside_tree():
yield(self, "ready")
update()
这意味着当 setter 被调用时,但节点还不在场景树中,Godot 将停止执行,直到它获得 ready
信号(发生在节点之后在场景树中,onready
个变量被初始化)。
但是,还有一个问题!这是一个tool
脚本!
在编辑器中运行ning时,onready
和ready
不起作用,加上is_inside_tree
会returntrue
。因此,在编辑器中,它将调用 update
但 coll
是 Nil
.
您可以使用 Engine.editor_hint
来阻止 setter 在编辑器中调用 update
,如下所示:
func set_distance(new_distance):
distance = new_distance
if Engine.editor_hint:
return
if not is_inside_tree():
yield(self, "ready")
update()
请记住,对于 tool
脚本,您不能依赖 onready
。我建议使用 get_node_or_null
并检查 null
。所以你可以这样做:
func set_distance(new_distance):
distance = new_distance
coll = get_node_or_null("static/collision")
if coll == null:
return
update()
这样,Godot在初始化时调用setter时会找不到节点,不会调用update
。 当然你也可以把那张支票放在update
里面。
我们可以做得更好。在 运行 时间 Godot 将调用 setter... 所以我们将 yield
在 ready
之后继续执行,此时它应该找到节点,除非有什么出错了。
func set_distance(new_distance):
distance = new_distance
if not Engine.editor_hint and not is_inside_tree():
yield(self, "ready")
coll = get_node_or_null("static/collision")
if coll == null:
return
update()
如果出现问题,我们可能会通知:
func set_distance(new_distance):
distance = new_distance
if not Engine.editor_hint and not is_inside_tree():
yield(self, "ready")
coll = get_node_or_null("static/collision")
if coll == null:
if not Engine.editor_hint:
push_error("static/collision not found")
return
update()
提取到另一个函数:
func set_distance(new_distance):
distance = new_distance
if not Engine.editor_hint and not is_inside_tree():
yield(self, "ready")
if can_update():
update()
func can_update() -> bool:
coll = get_node_or_null("static/collision")
if coll == null:
if not Engine.editor_hint:
push_error("static/collision not found")
return false
return true
您可以在所有 setter 中以相同的模式重用提取的函数。使其适应手头脚本的任何有意义的内容。这里它只关心你在 update
中使用的节点,但你可以根据需要制作任何逻辑。而且,是的,您也可以在 update
内部进行。
有些事情已经成为一种常见的做法:导出一个 bool 变量作为更新按钮。
我们可以用我上面描述的模式来做到这一点:
export (bool) var refresh:bool setget set_refresh
func set_refresh(new_value):
refresh = new_value
if not Engine.editor_hint and not is_inside_tree():
yield(self, "ready")
if can_update():
update()
refresh = false
这将在检查器面板上添加一个 Refresh
属性,带有一个您可以单击的复选框。如果可能,会调用 update
。另外,该复选框保持未选中状态。这个 refresh
总是错误的。
想法是您可以在编辑器中单击它以在需要时调用 update
。
此外,这个setter也会在运行时间内执行,并在初始化后不久调用update
。你可以让它只在编辑器上做一些事情:
export (bool) var refresh:bool setget set_refresh
func set_refresh(new_value):
refresh = new_value
if Engine.editor_hint and can_update():
update()
refresh = false
您可以添加 _get_configuration_warning
以提供将出现在场景面板中的警告(类似于 PhysicsBody
告诉您它需要 CollisionShape
或 CollisionPolygon
的方式):
func _get_configuration_warning():
if can_update():
return ""
return "static/collision not found"