利用服务器进行优化
像 Godot 这样的引擎,由于其更高层次的构建和功能,提供了更多的易用性。它们中的大多数都是通过场景系统来访问和使用的。使用节点和资源可以简化复杂游戏中的项目组织和资产管理。
当然, 总是有缺点的:
那有一个额外复杂层。
性能比直接使用简单API要低。
无法使用多个线程来控制它们。
需要更多的内存.
在很多情况下, 这并不是一个真正的问题(Godot是非常优化的, 大多数操作都是用信号处理的, 所以不需要轮询). 不过, 有时候还是会有这样的情况. 例如, 对于每一帧都需要处理的东西来说, 处理数以万计的实例可能是一个瓶颈.
这种情况会让程序员后悔自己使用的是游戏引擎, 希望能回到更加手动, 更加低层的游戏代码实现中去.
当然,Godot的设计工作中还是可以解决这个问题.
参见
你可以使用`Bullet Shower 演示项目 <https://github.com/godotengine/godot-demo-projects/tree/master/2d/bullet_shower>`__ 来了解如何使用低阶服务器
服务器
Godot 有许多非常有趣的设计决定,其中之一就是整个场景系统都是可选的。虽然目前还不能在编译时去除,但你完全可以绕过它。
Godot 在核心中使用了“服务器”的概念。它们是非常底层的 API,用来控制渲染、物理、声音等。场景系统建立在它们之上,直接使用它们。最常见的服务器有:
RenderingServer: 处理所有与图形相关的内容。
PhysicsServer3D: 处理所有与 3D 物理相关的内容。
PhysicsServer2D: 处理所有与 2D 物理相关的内容。
AudioServer: 处理与音频相关的一切.
你只需研究它们的 API 就会意识到,它们所提供的函数全部都是 Godot 允许你所进行的操作的底层实现。
RID
使用服务器的关键是理解资源 ID(Resource ID,即 RID)对象。这些对象是服务器实现的非公开的句柄。它们是手动分配和释放的。几乎服务器中的每个功能都需要 RID 来访问实际的资源。
大多数 Godot 节点和资源都包含这些来自服务内部的 RID,它们可以通过不同的函数获得。事实上,任何继承 Resource 的东西都可以直接转换成 RID。不过并不是所有资源都包含 RID:在这种情况下,RID 为空。可以用 RID 的形式将资源传递给服务器 API。
警告
资源是引用计数的(参阅 RefCounted),并且对资源 RID 的引用在确定资源仍在使用时 不 进行计数。请确保在服务外部保持对资源的引用,否则将删除资源及其 RID 。
对于节点来说, 有很多函数功能可以使用:
对于 CanvasItem,CanvasItem.get_canvas_item() 方法将在服务器中返回该画布项目的 RID。
对于CanvasLayer来说, CanvasLayer.get_canvas() 方法将返回服务器中的canvas RID.
对于视口, Viewport.get_viewport_rid() 方法将返回服务器中的视口RID.
For 3D, the World3D resource (obtainable in the Viewport and Node3D nodes) contains functions to get the RenderingServer Scenario, and the PhysicsServer Space. This allows creating 3D objects directly with the server API and using them.
For 2D, the World2D resource (obtainable in the Viewport and CanvasItem nodes) contains functions to get the RenderingServer Canvas, and the Physics2DServer Space. This allows creating 2D objects directly with the server API and using them.
The VisualInstance3D class, allows getting the scenario instance and instance base via the VisualInstance3D.get_instance() and VisualInstance3D.get_base() respectively.
请尝试探索你所熟悉的节点和资源,找到获得服务器 RID 的功能。
不建议从已经有节点关联的对象中控制RID. 相反, 服务函数应始终用于创建和控制新的以及与现有的交互.
创建精灵
This is an example of how to create a sprite from code and move it using the low-level CanvasItem API.
GDScriptC#
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)
public partial class MyNode2D : Node2D
{
// RenderingServer expects references to be kept around.
private Texture2D _texture;
public override void _Ready()
{
// Create a canvas item, child of this node.
Rid ciRid = RenderingServer.CanvasItemCreate();
// Make this node the parent.
RenderingServer.CanvasItemSetParent(ciRid, GetCanvasItem());
// Draw a texture on it.
// Remember, keep this reference.
_texture = ResourceLoader.Load<Texture2D>("res://MyTexture.png");
// Add it, centered.
RenderingServer.CanvasItemAddTextureRect(ciRid, new Rect2(_texture.GetSize() / 2, _texture.GetSize()), _texture.GetRid());
// Add the item, rotated 45 degrees and translated.
Transform2D xform = Transform2D.Identity.Rotated(Mathf.DegToRad(45)).Translated(new Vector2(20, 30));
RenderingServer.CanvasItemSetTransform(ciRid, xform);
}
}
服务器中的 Canvas Item API 允许你向其添加绘制图元。一旦添加,它们就不能被修改。需要清除 Item,并重新添加图元(设置变换时则不然,变换可根据需要多次进行)。
图元的清除方式为:
GDScriptC#
RenderingServer.canvas_item_clear(ci_rid)
RenderingServer.CanvasItemClear(ciRid);
将网格实例化到 3D 空间
3D API 与 2D API 不同,所以必须使用实例化 API。
GDScriptC#
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)
public partial class MyNode3D : Node3D
{
// RenderingServer expects references to be kept around.
private Mesh _mesh;
public override void _Ready()
{
// Create a visual instance (for 3D).
Rid instance = RenderingServer.InstanceCreate();
// Set the scenario from the world, this ensures it
// appears with the same objects as the scene.
Rid scenario = GetWorld3D().Scenario;
RenderingServer.InstanceSetScenario(instance, scenario);
// Add a mesh to it.
// Remember, keep the reference.
_mesh = ResourceLoader.Load<Mesh>("res://MyMesh.obj");
RenderingServer.InstanceSetBase(instance, _mesh.GetRid());
// Move the mesh around.
Transform3D xform = new Transform3D(Basis.Identity, new Vector3(20, 100, 0));
RenderingServer.InstanceSetTransform(instance, xform);
}
}
创建 2D 刚体并使用它移动精灵
This creates a RigidBody2D using the PhysicsServer2D API, and moves a CanvasItem when the body moves.
GDScript
# 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)
The 3D version should be very similar, as 2D and 3D physics servers are identical (using RigidBody3D and PhysicsServer3D respectively).
从服务器获取数据
Try to never request any information from RenderingServer
, PhysicsServer2D
or PhysicsServer3D
by calling functions unless you know what you are doing. These servers will often run asynchronously for performance and calling any function that returns a value will stall them and force them to process anything pending until the function is actually called. This will severely decrease performance if you call them every frame (and it won’t be obvious why).
正因为如此, 这类服务器中的大部分API都被设计成连信息都无法请求回来, 直到这是可以保存的实际数据.