Сцени та скрипти Godot - це класи

У Godot скрипти та сцени можуть бути еквівалентами класів в об’єктно-орієнтованій мові програмування. Основна відмінність полягає в тому, що сцени є декларативним кодом, тоді як скрипти можуть містити `імперативний код<https://en.wikipedia.org/wiki/Imperative_programming>`_.

Як результат, багато кращих практик роботи в Godot зводяться до застосування принципів об’єктно-орієнтованого підходу до сцен, вузлів, або скриптів, які складають вашу гру.

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

Сенс класів у Godot

Godot Engine забезпечує вбудовані класи, такі як Node. Створені користувачем типи технічно не є класами. Натомість це ресурси, які повідомляють движку послідовність ініціалізацій, які потрібно виконати на одному з вбудованих класів движка.

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

  • властивості
  • методи
  • константи
  • сигнали

This ClassDB is what Objects check against when performing an operation like accessing a property or calling a method. ClassDB checks the database’s records and the records of the Object’s base types to see if the Object supports the operation.

On the engine’s side, every class defines a static _bind_methods() function that describes what C++ content it registers to the database and how. When you use the engine, you can extend the methods, properties, and signals available from the ClassDB by attaching a Script to your node.

Objects check their attached script before the database. This is why scripts can override built-in methods. If a script defines a _get_property_list() method, Godot appends that data to the list of properties the Object fetches from the ClassDB. The same is true for other declarative code.

Even scripts that don’t inherit from a built-in type, i.e. scripts that don’t start with the extends keyword, implicitly inherit from the engine’s base Reference class. This allows the Object to defer to the script’s content where the engine logic deems appropriate.

Примітка

As a result, you can instance scripts without the extends keyword from code, but you cannot attach them to a Node

Scripting performances and PackedScene

As the size of Objects increases, the scripts“ necessary size to create them grows much, much larger. Creating node hierarchies demonstrates this. Each individual Node’s logic could be several hundred lines of code in length.

Let’s see a simple example of creating a single Node as a child. The code below creates a new Node, changes its name, assigns a script to it, sets its future parent as its owner so it gets saved to disk along with it, and finally adds it as a child of the Main node:

# Main.gd
extends Node

func _init():
    var child = Node.new()
    child.name = "Child"
    child.script = preload("Child.gd")
    child.owner = self
    add_child(child)
using System;
using Godot;

namespace ExampleProject
{
    public class Main : Resource
    {
        public Node Child { get; set; }

        public Main()
        {
            Child = new Node();
            Child.Name = "Child";
            Child.Script = (Script)ResourceLoader.Load("child.gd");
            Child.Owner = this;
            AddChild(Child);
        }
    }
}

Script code like this is much slower than engine-side C++ code. Each change makes a separate call to the scripting API which leads to many «look-ups» on the back-end to find the logic to execute.

Scenes help to avoid this performance issue. PackedScene, the base type that scenes inherit from, are resources that use serialized data to create objects. The engine can process scenes in batches on the back-end and provide much better performance than scripts.

Scenes and scripts are objects

Why is any of this important to scene organization? Because scenes are objects. One often pairs a scene with a scripted root node that makes use of the sub-nodes. This means that the scene is often an extension of the script’s declarative code.

The content of a scene helps to define:

  • What nodes are available to the script
  • How they are organized
  • How are they initialized
  • What signal connections they have with each other

Many Object-Oriented principles which apply to written code also apply to scenes.

The scene is always an extension of the script attached to its root node. You can see all the nodes it contains as part of a single class.

Most of the tips and techniques explained in this series will build on this.