Godot的基于工具的应用程序的架构是什么



我目前正在开发一个应用程序,可以为我的DnD(和其他RPG)组创建和导出战斗地图。我使用Godot作为基础,因为它带来了许多支持此用例的功能,如精灵渲染、多边形、着色器、令人惊叹的UI框架等。

我有一个场景树,大致如下:

  • 窗口
    • 菜单
    • 工具窗口
      • 选择工具
      • 多边形工具
    • 画布
      • 工具特定节点
        • 精灵预览
      • MapRoot
        • 多边形1
        • 精灵1

每当选择另一个工具时;ToolSpecificNodes";并让选定的工具添加它自己的节点。每个工具都有一个select()和deselect()生命周期方法以及对画布的引用。每个工具都有一个自己管理的属性窗口。

我的问题是,我将如何以一种允许它们松散耦合和交换的方式集成不同的工具。计划的工具是";选择工具"多边形工具"线工具"精灵工具";。我在挣扎,尤其是";选择工具";因为它有点依赖于其他工具来处理诸如多边形变形、线变形和精灵缩放之类的东西。

是否有一个众所周知的体系结构模式涵盖了这个用例?或者可能是一个没有太多复杂性的示例项目?无Godot解决方案也很受欢迎,因为我应该能够将它们应用到我的环境中。

一般来说,

  • 如果直接引用其他类(在本例中为节点),则表示代码紧密耦合
  • 如果您使用依赖项注入(或代码相互查找的方法,如反射或服务定位器)和接口(或使用鸭子类型),那么您就拥有松散耦合的代码
  • 如果您使用基础结构在对象之间传递消息(即信号),那么您就获得了解耦的代码

因此,如果您希望工具解耦,请使用信号。

当一个工具可用时,它会连接到它需要的任何信号。当工具不再可用时,它会断开连接。

注意:释放对象会自动断开连接。

然后其他代码(例如,处理输入的位置)可以发出信号,而无需担心当前可用的一个或多个工具。

这一点,再加上一个对象可以发出其他对象的信号,也将允许工具之间的通信。然而,它不允许一个工具专门与另一个特定工具通信(从一个工具到另一个工具的这种引用会将它们耦合)。

附录:您可以使用信号总线来解耦以进一步编码,正如我在其他地方所描述的那样。


如果您希望您的工具松散耦合,但不解耦…

无论您在哪里需要,都要保留对当前工具的引用。所以你可以调用它。更改当前工具就是更改引用。

附录:也许您会从堆栈中受益。堆栈顶部是当前工具的位置。因此,当您选择工具时,可以将它们推送到堆栈上。并在取消选择时弹出它们。这会将用户返回到他们以前使用的工具。

当然,你不希望工具之间有引用,但你希望它们能够找到彼此。一个简单的机制是在场景树中查找,如果不存在所需的工具,则加载它。因此,让我们将该逻辑放入自动加载(sigleton)中。它将是我们的服务定位器。

若要执行此操作,请仅使用根节点创建一个场景。并将其添加为自动加载,并命名为";工具定位器";。给根节点一个脚本,我们需要添加一个方法,为我们提供一个工具的参考:

func get_tool(name:String) -> Node:
return null # TODO

它将检查是否已加载工具。我们将使用场景树。如果已加载,则返回:

func get_tool(name:String) -> Node:
var result := get_node_or_null(name)
if result != null:
return result
return null # TODO

否则,它将尝试加载它。为此,它将试图根据工具的名称构造资源路径。当然,它必须保留它(在场景树中)以备将来的请求。

const tool_resource_root := "res://tools/"
const extension := ".tscn"
func get_tool(name:String) -> Node:
var result := get_node_or_null(name)
if result != null:
return result
var packed_scene := load(tool_resource_root + name + extension) as PackedScene
if packed_scene != null:
result = packed_scene.instance()
result.name = name
add_child(result)
return result
return null

在这种情况下,我们正在寻找"res://tools/"和扩展名为".tscn"的工具场景

由于这是一个自动加载,你可以在任何场景中使用它,比如:

var polygon_tool := ToolLocator.get_tool("polygon")

是的,这将包括工具场景。也就是说,工具场景能够像那样找到彼此。因此,这是一种他们可以在没有硬编码引用或其类型的情况下进行通信的方式。换句话说,它们是松散耦合的。

注意:当工具不可用时(get_tool返回null),您需要处理该情况。在返回null之前,请考虑在get_tool的末尾添加push_error


您可能还想看看https://softwareengineering.stackexchange.com,这个问题将成为那里的主题

最新更新