Work in progress

The content of this page was not yet updated for Godot 4.4 and may be outdated. If you know how to improve this page or you can confirm that it's up to date, feel free to open a pull request.

利用服务器进行优化

Engines like Godot provide increased ease of use thanks to their high-level constructs and features. Most of them are accessed and used via the Scene System. Using nodes and resources simplifies project organization and asset management in complex games.

当然, 总是有缺点的:

  • 那有一个额外复杂层。

  • 性能比直接使用简单API要低。

  • 无法使用多个线程来控制它们。

  • 需要更多的内存.

在很多情况下, 这并不是一个真正的问题(Godot是非常优化的, 大多数操作都是用信号处理的, 所以不需要轮询). 不过, 有时候还是会有这样的情况. 例如, 对于每一帧都需要处理的东西来说, 处理数以万计的实例可能是一个瓶颈.

This type of situation makes programmers regret they are using a game engine and wish they could go back to a more handcrafted, low-level implementation of game code.

当然,Godot的设计工作中还是可以解决这个问题.

参见

你可以使用`Bullet Shower 演示项目 <https://github.com/godotengine/godot-demo-projects/tree/master/2d/bullet_shower>`__ 来了解如何使用低阶服务器

服务器

Godot 有许多非常有趣的设计决定,其中之一就是整个场景系统都是可选的。虽然目前还不能在编译时去除,但你完全可以绕过它。

Godot 在核心中使用了“服务器”的概念。它们是非常底层的 API,用来控制渲染、物理、声音等。场景系统建立在它们之上,直接使用它们。最常见的服务器有:

你只需研究它们的 API 就会意识到,它们所提供的函数全部都是 Godot 允许你所进行的操作的底层实现。

RID

使用服务器的关键是理解资源 ID(Resource ID,即 RID)对象。这些对象是服务器实现的非公开的句柄。它们是手动分配和释放的。几乎服务器中的每个功能都需要 RID 来访问实际的资源。

大多数 Godot 节点和资源都包含这些来自服务内部的 RID,它们可以通过不同的函数获得。事实上,任何继承 Resource 的东西都可以直接转换成 RID。不过并不是所有资源都包含 RID:在这种情况下,RID 为空。可以用 RID 的形式将资源传递给服务器 API。

警告

资源是引用计数的(见 RefCounted),对资源 RID 的引用在确定资源仍在使用时不进行计数。请确保在服务器外部保持对资源的引用,否则将删除资源及其 RID。

对于节点来说, 有很多函数功能可以使用:

请尝试探索你所熟悉的节点和资源,找到获得服务器 RID 的功能。

不建议控制已经有节点关联的对象的 RID。服务器函数应始终用于创建和控制新的对象、与现有对象进行交互。

创建精灵

这是一个如何从代码创建精灵并使用低级 CanvasItem API 移动它的示例。

extends Node2D


# RenderingServer expects references to be kept around.
var texture


func _ready():
    # Create a canvas item, child of this node.
    var ci_rid = RenderingServer.canvas_item_create()
    # Make this node the parent.
    RenderingServer.canvas_item_set_parent(ci_rid, get_canvas_item())
    # Draw a texture on it.
    # Remember, keep this reference.
    texture = load("res://my_texture.png")
    # Add it, centered.
    RenderingServer.canvas_item_add_texture_rect(ci_rid, Rect2(-texture.get_size() / 2, texture.get_size()), texture)
    # Add the item, rotated 45 degrees and translated.
    var xform = Transform2D().rotated(deg_to_rad(45)).translated(Vector2(20, 30))
    RenderingServer.canvas_item_set_transform(ci_rid, xform)

服务器中的 Canvas Item API 允许你向其添加绘制图元。一旦添加,它们就不能被修改。需要清除 Item,并重新添加图元(设置变换时则不然,变换可根据需要多次进行)。

图元的清除方式为:

RenderingServer.canvas_item_clear(ci_rid)

将网格实例化到 3D 空间

3D API 与 2D API 不同,所以必须使用实例化 API。

extends Node3D


# RenderingServer expects references to be kept around.
var mesh


func _ready():
    # Create a visual instance (for 3D).
    var instance = RenderingServer.instance_create()
    # Set the scenario from the world, this ensures it
    # appears with the same objects as the scene.
    var scenario = get_world_3d().scenario
    RenderingServer.instance_set_scenario(instance, scenario)
    # Add a mesh to it.
    # Remember, keep the reference.
    mesh = load("res://mymesh.obj")
    RenderingServer.instance_set_base(instance, mesh)
    # Move the mesh around.
    var xform = Transform3D(Basis(), Vector3(20, 100, 0))
    RenderingServer.instance_set_transform(instance, xform)

创建 2D 刚体并使用它移动精灵

这段代码使用 PhysicsServer2D API 创建了一个 RigidBody2D,并在该物体移动时移动 CanvasItem

# Physics2DServer expects references to be kept around.
var body
var shape


func _body_moved(state, index):
    # Created your own canvas item, use it here.
    RenderingServer.canvas_item_set_transform(canvas_item, state.transform)


func _ready():
    # Create the body.
    body = Physics2DServer.body_create()
    Physics2DServer.body_set_mode(body, Physics2DServer.BODY_MODE_RIGID)
    # Add a shape.
    shape = Physics2DServer.rectangle_shape_create()
    # Set rectangle extents.
    Physics2DServer.shape_set_data(shape, Vector2(10, 10))
    # Make sure to keep the shape reference!
    Physics2DServer.body_add_shape(body, shape)
    # Set space, so it collides in the same space as current scene.
    Physics2DServer.body_set_space(body, get_world_2d().space)
    # Move initial position.
    Physics2DServer.body_set_state(body, Physics2DServer.BODY_STATE_TRANSFORM, Transform2D(0, Vector2(10, 20)))
    # Add the transform callback, when body moves
    # The last parameter is optional, can be used as index
    # if you have many bodies and a single callback.
    Physics2DServer.body_set_force_integration_callback(body, self, "_body_moved", 0)

3D 版本应该非常相似,因为 2D 和 3D 物理服务器是相同的(分别使用 RigidBody3DPhysicsServer3D)。

从服务器获取数据

除非你知道自己在做什么,否则请尽量永远不要通过调用函数从 RenderingServerPhysicsServer2DPhysicsServer3D 请求任何信息。这些服务器通常会异步运行以提高性能,调用任何返回值的函数都会使它们停滞,并迫使它们处理任何待处理的内容,直到实际调用该函数。如果你每帧都调用它们,这将严重降低性能(而且原因并不明显)。

正因为如此, 这类服务器中的大部分API都被设计成连信息都无法请求回来, 直到这是可以保存的实际数据.