使用服务器优化¶
像godot这样的引擎由于其高级别的结构和特性而提供了更高的易用性。其中大部分通过 Scene System . 使用节点和资源简化了复杂游戏中的项目组织和资产管理。
当然,总有一些缺点:
有一层额外的复杂性
性能低于直接使用简单API
不能使用多个线程来控制它们
需要更多的内存。
在许多情况下,这并不是真正的问题(godot非常优化,大多数操作都是用信号处理的,因此不需要轮询)。尽管如此,有时候也可以。例如,处理需要处理的每个帧的数万个实例可能是一个瓶颈。
这种情况让程序员后悔他们使用了一个游戏引擎,并希望他们能回到一个更手工的、低级别的游戏代码实现。
不过,Godot是为解决这个问题而设计的。
服务器¶
Godot最有趣的设计决策之一是整个场景系统 可选择的 . 虽然目前无法编译它,但可以完全绕过它。
在核心,Godot使用服务器的概念。它们是非常低级的API,用于控制渲染、物理、声音等。场景系统构建在它们之上,并直接使用它们。最常见的服务器是:
VisualServer :处理与图形相关的所有内容。
PhysicsServer :处理与三维物理相关的所有内容。
Physics2DServer :处理与二维物理相关的所有内容。
AudioServer :处理与音频相关的所有内容。
只要研究一下他们的API,您就会发现所提供的所有函数都是Godot允许您做的所有事情的低级实现。
RIDs¶
使用服务器的关键是了解资源ID (RID )对象。这些是服务器实现的不透明句柄。它们是手动分配和释放的。几乎服务器中的每个功能都需要RID来访问实际的资源。
大多数godot节点和资源都包含来自服务器内部的这些RID,它们可以通过不同的功能获得。事实上,任何继承 Resource 可以直接强制转换为RID(并非所有资源都包含RID,但在这种情况下,RID将为空)。实际上,资源可以作为RID传递给服务器API。只需确保将对资源的引用保留在服务器外部,因为如果资源被擦除,内部RID也会被擦除。
对于节点,有许多可用功能:
对于CanvasItem CanvasItem.get_canvas_item() 方法将返回服务器中的画布项RID。
对于Canvaslier, CanvasLayer.get_canvas() 方法将返回服务器中的画布RID。
对于视区, Viewport.get_viewport_rid() 方法将返回服务器中的viewport rid。
对于3D, World 资源(可在 Viewport 和 Spatial 节点)包含用于获取 VisualServer方案 和 物理服务器空间 . 这允许直接使用服务器API创建3D对象并使用它们。
对于2d, World2D 资源(可在 Viewport 和 CanvasItem 节点)包含用于获取 VisualServer画布 和 物理服务器空间 . 这允许直接使用服务器API创建二维对象并使用它们。
这个 VisualInstance 类,允许获取方案 实例 和 实例库 通过 VisualInstance.get_instance() 和 VisualInstance.get_base() 分别。
只需浏览您熟悉的节点和资源,并找到获取服务器的函数 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物理服务器是相同的(使用 RigidBody 和 PhysicsServer 分别)。
从服务器获取数据¶
尝试 从未 请求任何信息 VisualServer
, PhysicsServer
或 Physics2DServer
调用函数,除非你知道你在做什么。这些服务器通常会异步运行以获得性能,调用任何返回值的函数都会使它们停止运行,并强制它们处理任何挂起的内容,直到实际调用该函数为止。如果您每帧都调用它们,这将严重降低性能(原因不明显)。
正因为如此,这些服务器中的大多数API都是这样设计的:在实际数据可以保存之前,甚至不可能请求返回信息。