利用服务器进行优化

像Godot这样的引擎由于其更高层次的构建和功能,提供了更多的易用性.它们中的大多数都是通过 Scene System 来访问和使用的.使用节点和资源可以简化复杂游戏中的项目组织和资产管理.

当然,总是有缺点的:

  • 那有一个额外复杂层

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

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

  • 需要更多的内存.

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

这种情况会让程序员后悔自己使用的是游戏引擎,希望能回到更加手动、更加低层的游戏代码实现中去.

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

Servers

对于Godot来说,最有趣的一个设计决定,就是整个场景系统是 可选的 .虽然目前还不能编译出来,但完全可以绕过它.

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

你只需研究它们的API,就会意识到,它们所提供的全部函数都是Godot允许你操作的低级实现.

RIDs (Resource ID)

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

大多数Godot节点和资源都包含这些来自服务内部的RID,它们可以通过不同的函数获得.事实上,任何继承 Resource 的东西都可以直接指向RID(不过,并不是所有资源都包含RID,在这种情况下,RID会是空的).事实上,资源可以作为RID传递给服务API.只要确保将资源的引用保留在服务之外,因为如果资源被删除,内部的RID也会被删除.

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

  • 对于CanvasItem, :ref:`CanvasItem.get_canvas_item()<class_CanvasItem_method_get_canvas_item>`方法将在服务器中返回画布项目RID.

  • 对于CanvasLayer来说, CanvasLayer.get_canvas() 方法将返回服务器中的canvas RID.

  • 对于视口, Viewport.get_viewport_rid() 方法将返回服务器中的视口RID.

  • 对于3D, World 资源(可在 :ref:`Viewport <class_Viewport>`和 :ref:`Spatial <class_Spatial>`节点中获得)包含获取 VisualServer ScenarioPhysicsServer Space 的函数.这样就可以直接用服务API创建3D对象并使用它们.

  • 对于2D, World2D 资源(可在 ViewportCanvasItem 节点中获取)包含获取 VisualServer CanvasPhysics2DServer Space 的函数.这样就可以直接用服务API创建2D对象并使用它们.

  • VisualInstance 和 VisualInstance.get_base() 来获取场景 instanceinstance base .

只需探索你所熟悉的节点和资源,找到获得服务器 RID 的功能.

不建议从已经有节点关联的对象中控制RID.相反,服务函数应始终用于创建和控制新的以及与现有的交互.

创建精灵

这是一个简单的例子,说明如何从代码中创建一个精灵,并使用低级的 CanvasItem API移动它.

GDScript

  1. extends Node2D
  2. # VisualServer expects references to be kept around.
  3. var texture
  4. func _ready():
  5. # Create a canvas item, child of this node.
  6. var ci_rid = VisualServer.canvas_item_create()
  7. # Make this node the parent.
  8. VisualServer.canvas_item_set_parent(ci_rid, get_canvas_item())
  9. # Draw a texture on it.
  10. # Remember, keep this reference.
  11. texture = load("res://my_texture.png")
  12. # Add it, centered.
  13. VisualServer.canvas_item_add_texture_rect(ci_rid, Rect2(texture.get_size() / 2, texture.get_size()), texture)
  14. # Add the item, rotated 45 degrees and translated.
  15. var xform = Transform2D().rotated(deg2rad(45)).translated(Vector2(20, 30))
  16. VisualServer.canvas_item_set_transform(ci_rid, xform)

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

基本单元是这种方式被清除:

GDScript

  1. VisualServer.canvas_item_clear(ci_rid)

将网格实例化到3D空间

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

GDScript

  1. extends Spatial
  2. # VisualServer expects references to be kept around.
  3. var mesh
  4. func _ready():
  5. # Create a visual instance (for 3D).
  6. var instance = VisualServer.instance_create()
  7. # Set the scenario from the world, this ensures it
  8. # appears with the same objects as the scene.
  9. var scenario = get_world().scenario
  10. VisualServer.instance_set_scenario(instance, scenario)
  11. # Add a mesh to it.
  12. # Remember, keep the reference.
  13. mesh = load("res://mymesh.obj")
  14. VisualServer.instance_set_base(instance, mesh)
  15. # Move the mesh around.
  16. var xform = Transform(Basis(), Vector3(20, 100, 0))
  17. VisualServer.instance_set_transform(instance, xform)

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

这将使用 Physics2DServer API创建一个 RigidBody2D,并在物体移动时移动一个 CanvasItem.

GDScript

  1. # Physics2DServer expects references to be kept around.
  2. var body
  3. var shape
  4. func _body_moved(state, index):
  5. # Created your own canvas item, use it here.
  6. VisualServer.canvas_item_set_transform(canvas_item, state.transform)
  7. func _ready():
  8. # Create the body.
  9. body = Physics2DServer.body_create()
  10. Physics2DServer.body_set_mode(body, Physics2DServer.BODY_MODE_RIGID)
  11. # Add a shape.
  12. shape = RectangleShape2D.new()
  13. shape.extents = Vector2(10, 10)
  14. # Make sure to keep the shape reference!
  15. Physics2DServer.body_add_shape(body, shape)
  16. # Set space, so it collides in the same space as current scene.
  17. Physics2DServer.body_set_space(body, get_world_2d().space)
  18. # Move initial position.
  19. Physics2DServer.body_set_state(body, Physics2DServer.BODY_STATE_TRANSFORM, Transform2D(0, Vector2(10, 20)))
  20. # Add the transform callback, when body moves
  21. # The last parameter is optional, can be used as index
  22. # if you have many bodies and a single callback.
  23. Physics2DServer.body_set_force_integration_callback(body, self, "_body_moved", 0)

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

从服务器获取数据

除非你知道自己在做什么,否则尽量不要**通过调用函数向 VisualServerPhysicsServerPhysics2DServer 请求任何信息.这些服务器通常会为了性能而异步运行,调用任何返回值的函数都会使它们停滞,并迫使它们处理任何待处理的东西,直到函数被实际调用.如果你每一帧都调用它们,这将严重降低性能(而且原因不会很明显).

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