Work in progress

The content of this page was not yet updated for Godot 4.5 and may be outdated. If you know how to improve this page or you can confirm that it's up to date, feel free to open a pull request.

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

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

Звичайно, завжди є недоліки:

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

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

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

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

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

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

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

Дивись також

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

Сервери

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

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

  • RenderingServer: обробляє все, що стосується графіки.

  • PhysicsServer3D: обробляє все, що стосується 3D фізики.

  • PhysicsServer2D: обробляє все, що стосується 2D фізики.

  • 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 області перегляду на сервері.

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

  • Для 2D ресурс World2D (отриманий у вузлах Viewport і CanvasItem) містить функції для отримання RenderingServer Canvas і Простір Physics2DServer. Це дозволяє створювати 2D-об’єкти безпосередньо за допомогою API сервера та використовувати їх.

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

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

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

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

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

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)

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

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

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, 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)

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

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

# 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)

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

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

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

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