Attention: Here be dragons

This is the latest (unstable) version of this documentation, which may document features not available in or compatible with released stable versions of Godot.

Оптимізація за допомогою серверів

Такі рушії, як Godot, забезпечують підвищену зручність використання завдяки своїм високорівневим конструкціям та функціям. Більшість із них доступні та використовуються через систему сцен. Використання вузлів та ресурсів спрощує організацію проекту та управління активами у складних іграх.

У цьому є кілька недоліків:

  • Це додатковий рівень складності.

  • Продуктивність нижча, ніж при безпосередньому використанні простих API.

  • Неможливо використовувати кілька потоків для керування ними.

  • Потрібно більше пам'яті.

У більшості випадків це насправді не є проблемою. Godot добре оптимізований, і більшість операцій обробляються за допомогою сигналів, що означає, що опитування не потрібне. Тим не менш, іноді ми хочемо отримати кращу продуктивність від апаратного забезпечення, коли інші шляхи оптимізації вичерпані. Наприклад, обробка десятків тисяч екземплярів чогось, що потрібно обробляти кожен кадр, може бути вузьким місцем.

Така ситуація змушує програмістів жалкувати про те, що вони використовують ігровий движок, і бажають повернутися до більш ручної, низькорівневої реалізації коду гри.

Тим не менш, Godot спроектований так, щоб вирішити цю проблему.

Дивись також

Ви можете побачити, як працює використання низькорівневих серверів у дії, використовуючи демонстраційний проект Bullet Shower.

Сервери

Одним із найцікавіших дизайнерських рішень для Godot є той факт, що вся система сцен є необов'язковою. Хоча її неможливо скомпілювати, її можна повністю обійти.

В основі Godot лежить концепція Серверів. Це низькорівневі API для керування рендерингом, фізикою, звуком тощо. Система сцен побудована на їх основі та використовує їх безпосередньо. Найпоширенішими серверами є:

  • class_RenderingServer: Вивчає все, що пов'язано з графікою.

  • class_PhysicsServer3D: Займається всім, що пов'язано з 3D-фізикою.

  • class_PhysicsServer2D: Займається всім, що пов'язано з двовимірною фізикою.

  • AudioServer: Вирішує все, що пов'язано зі звуком.

Дослідіть їхні API, і ви зрозумієте, що всі надані функції є низькорівневими реалізаціями всього, що Godot дозволяє робити за допомогою вузлів.

RID-и

Ключем до використання серверів є розуміння ідентифікатора ресурсу (RID) об'єктів. Це непрозорі дескриптори реалізації сервера. Вони виділяються та звільняються вручну. Майже кожна функція на серверах вимагає RID для доступу до фактичного ресурсу.

Більшість вузлів і ресурсів Godot містять ці RID-и із внутрішніх серверів, і їх можна отримати за допомогою різних функцій. Насправді все, що успадковує Ресурс, може бути безпосередньо передано до RID. Однак не всі ресурси містять RID: у таких випадках RID буде порожнім. Тоді ресурс можна передати API сервера, як RID.

Попередження

Ресурси підраховуються за посиланнями (див. RefCounted), а посилання на RID ресурсу не враховуються під час визначення того, чи ресурс все ще використовується. Переконайтеся, що ви зберігаєш посилання на ресурс поза сервером. В іншому випадку і ресурс, і його RID будуть видалені.

Для вузлів доступно багато функцій:

  • Для CanvasItem метод CanvasItem.get_canvas_item() поверне RID елемента полотна на сервері.

  • Для CanvasLayer метод CanvasLayer.get_canvas() поверне RID полотна на сервері.

  • Для Viewport метод Viewport.get_viewport_rid() поверне RID області перегляду на сервері.

  • Для 2D, ресурс class_World2D (доступний у вузлах class_Viewport та CanvasItem) містить функції для отримання RenderingServer Canvas та PhysicsServer2D Space. Це дозволяє створювати 2D-об'єкти безпосередньо за допомогою API сервера та використовувати їх.

  • Для 3D ресурс class_World3D (доступний у вузлах class_Viewport та class_Node3D) містить функції для отримання сценарію RenderingServer та простору PhysicsServer. Це дозволяє створювати 3D-об'єкти безпосередньо за допомогою API сервера та використовувати їх.

  • Клас class_VisualInstance3D дозволяє отримати екземпляр сценарію та базу екземплярів через VisualInstance3D.get_instance() та VisualInstance3D.get_base() відповідно.

Спробуйте вивчити вузли та ресурси, з якими ви знайомі, і знайдіть функції для отримання RID сервера .

Не рекомендується керувати RID від об’єктів, які вже мають пов’язаний вузол. Натомість функції сервера завжди повинні використовуватися для створення та керування новими та взаємодії з наявними.

Створення спрайта

Це приклад того, як створити спрайт з коду та перемістити його за допомогою низькорівневого API CanvasItem.

Примітка

Під час створення елементів полотна за допомогою RenderingServer слід скинути інтерполяцію фізики на першому кадрі за допомогою RenderingServer.canvas_item_reset_physics_interpolation(). Це забезпечує належну синхронізацію між системами рендерингу та фізики.

Якщо цього не зробити, елемент полотна може телепортуватися всередину під час завантаження сцени, а не з'являтися безпосередньо у своєму призначеному місці.

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 to 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)
    # Reset physics interpolation for this item.
    RenderingServer.canvas_item_reset_physics_interpolation(ci_rid)

API елементів Canvas на сервері дозволяє додавати до них примітиви малювання. Після додавання їх не можна змінювати. Елемент потрібно очистити, а примітиви додати повторно. Це не стосується налаштування перетворення, яке можна робити скільки завгодно разів.

Примітиви очищаються таким чином:

RenderingServer.canvas_item_clear(ci_rid)

Створення екземпляра Mesh у 3D просторі

3D API відрізняються від 2D, тому необхідно використовувати 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 to keep this reference.
    mesh = load("res://my_mesh.obj")
    RenderingServer.instance_set_base(instance, mesh)
    # Move the mesh around.
    var xform = Transform3D(Basis(), Vector3(2, 3, 0))
    RenderingServer.instance_set_transform(instance, xform)

Створення 2D RigidBody і переміщення спрайта за допомогою нього

Це створює class_RigidBody2D за допомогою class_PhysicsServer2D API та переміщує CanvasItem, коли тіло рухається.

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


func _body_moved(state, index):
    # Created your own canvas item; use it here.
    # `ci_rid` from the sprite example above needs to be moved to a
    # member variable (instead of within `_ready()`) so it can be referenced here.
    RenderingServer.canvas_item_set_transform(ci_rid, state.transform)


func _ready():
    # Create the body.
    body = PhysicsServer2D.body_create()
    PhysicsServer2D.body_set_mode(body, PhysicsServer2D.BODY_MODE_RIGID)
    # Add a shape.
    shape = PhysicsServer2D.rectangle_shape_create()
    # Set rectangle extents.
    PhysicsServer2D.shape_set_data(shape, Vector2(10, 10))
    # Make sure to keep the shape reference!
    PhysicsServer2D.body_add_shape(body, shape)
    # Set space, so it collides in the same space as current scene.
    PhysicsServer2D.body_set_space(body, get_world_2d().space)
    # Move initial position.
    PhysicsServer2D.body_set_state(body, PhysicsServer2D.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.
    PhysicsServer2D.body_set_force_integration_callback(body, self, "_body_moved", 0)

    # Also create a sprite using RenderingServer here.
    # See the section above on creating a sprite.
    # ...

3D-версія має бути дуже схожою, оскільки 2D- та 3D-сервери фізики ідентичні (використовуючи class_RigidBody3D та class_PhysicsServer3D відповідно).

Отримання даних із серверів

Намагайтеся ніколи не запитувати будь-яку інформацію з class_RenderingServer, class_PhysicsServer2D або class_PhysicsServer3D, викликаючи функції, якщо ви не знаєте, що робите. Ці сервери часто працюють асинхронно для підвищення продуктивності, і виклик будь-якої функції, яка повертає значення, зупинить їх і змусить обробляти все, що очікує обробки, доки функція фактично не буде викликана. Це значно знизить продуктивність, якщо ви викликатимете їх кожного кадру (і не буде очевидно, чому).

Через це більшість API на таких серверах розроблені таким чином, що навіть неможливо надіслати запит на повернення інформації, поки це не будуть фактичні дані, які можна зберегти.