使用服务器优化

像godot这样的引擎由于其高级别的结构和特性而提供了更高的易用性。其中大部分通过 Scene System . 使用节点和资源简化了复杂游戏中的项目组织和资产管理。

当然,总有一些缺点:

  • 有一层额外的复杂性

  • 性能低于直接使用简单API

  • 不能使用多个线程来控制它们

  • 需要更多的内存。

在许多情况下,这并不是真正的问题(godot非常优化,大多数操作都是用信号处理的,因此不需要轮询)。尽管如此,有时候也可以。例如,处理需要处理的每个帧的数万个实例可能是一个瓶颈。

这种情况让程序员后悔他们使用了一个游戏引擎,并希望他们能回到一个更手工的、低级别的游戏代码实现。

不过,Godot是为解决这个问题而设计的。

服务器

Godot最有趣的设计决策之一是整个场景系统 可选择的 . 虽然目前无法编译它,但可以完全绕过它。

在核心,Godot使用服务器的概念。它们是非常低级的API,用于控制渲染、物理、声音等。场景系统构建在它们之上,并直接使用它们。最常见的服务器是:

只要研究一下他们的API,您就会发现所提供的所有函数都是Godot允许您做的所有事情的低级实现。

RIDs

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

大多数godot节点和资源都包含来自服务器内部的这些RID,它们可以通过不同的功能获得。事实上,任何继承 Resource 可以直接强制转换为RID(并非所有资源都包含RID,但在这种情况下,RID将为空)。实际上,资源可以作为RID传递给服务器API。只需确保将对资源的引用保留在服务器外部,因为如果资源被擦除,内部RID也会被擦除。

对于节点,有许多可用功能:

只需浏览您熟悉的节点和资源,并找到获取服务器的函数 RIDs .

建议不要从已关联节点的对象控制RID。相反,服务器功能应该总是用于创建和控制新的功能,以及与现有的功能交互。

创建精灵

这是一个简单的例子,说明如何从代码中创建sprite并使用低级代码移动它 CanvasItem 应用程序编程接口。

extends Node2D

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

服务器中的画布项API允许您向其添加绘图原语。一旦添加,就不能修改它们。需要清除该项并重新添加基元(这不是设置转换的情况,可以根据需要执行多次)。

原语清除方式如下:

VisualServer.canvas_item_clear(ci_rid)

将网格实例化为三维空间

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

extends Spatial

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

创建二维刚体并用它移动sprite

这创造了一个 RigidBody2D 使用 Physics2DServer 然后移动 CanvasItem 当身体移动时。

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

func _ready():
    # Create the body.
    var body = Physics2DServer.body_create()
    Physics2DServer.body_set_mode(body, Physics2DServer.BODY_MODE_RIGID)
    # Add a shape.
    var shape = RectangleShape2D.new()
    shape.extents = 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物理服务器是相同的(使用 RigidBodyPhysicsServer 分别)。

从服务器获取数据

尝试 从未 请求任何信息 VisualServerPhysicsServerPhysics2DServer 调用函数,除非你知道你在做什么。这些服务器通常会异步运行以获得性能,调用任何返回值的函数都会使它们停止运行,并强制它们处理任何挂起的内容,直到实际调用该函数为止。如果您每帧都调用它们,这将严重降低性能(原因不明显)。

正因为如此,这些服务器中的大多数API都是这样设计的:在实际数据可以保存之前,甚至不可能请求返回信息。