Godot场景和脚本是类

在Godot中,脚本和场景都可以等效于面向对象编程语言中的类。主要区别在于场景 declarative code ,而脚本可以包含 imperative code .

因此,Godot中的许多最佳实践归结为将面向对象的设计原则应用于构成游戏的场景、节点或脚本。

本指南解释了脚本和场景如何在引擎核心中工作,以帮助您了解Godot如何在引擎盖下工作,并帮助您更好地了解本系列最佳实践的来源。

理解Godot的阶级

Godot引擎提供了内置类,比如 Node . 用户创建的类型在技术上不是类。相反,它们是资源,告诉引擎在引擎的一个内置类上执行一系列初始化。

Godot的内部类具有用 ClassDB . 此数据库提供对类信息的运行时访问。 ClassDB 包含有关类的信息,如:

  • 性质

  • 方法

  • 常量

  • 信号

这个 ClassDB 在执行诸如访问属性或调用方法之类的操作时,对象检查的对象是什么。 ClassDB 检查数据库的记录和对象基类型的记录,以查看对象是否支持该操作。

在引擎方面,每个类都定义了一个静态的 _bind_methods() 函数,它描述注册到数据库的C++内容以及如何注册。使用引擎时,可以扩展 ClassDB 通过附加 Script 到您的节点。

对象在数据库前检查其附加的脚本。这就是为什么脚本可以重写内置方法的原因。如果脚本定义了 _get_property_list() 方法,godot将该数据附加到对象从ClassDB获取的属性列表中。其他声明性代码也是如此。

即使是不从内置类型继承的脚本,也就是不以 extends 关键字,隐式继承自引擎的基 Reference 类。这允许对象在引擎逻辑认为合适的地方遵从脚本的内容。

注解

因此,您可以不使用 extends 关键字,但不能将它们附加到 Node

脚本性能和打包场景

随着对象大小的增加,脚本创建对象所需的大小也会增大很多。创建节点层次结构说明了这一点。每个节点的逻辑可以有几百行代码。

让我们来看一个创建单个 Node 作为一个孩子。下面的代码创建一个新的 Node ,更改其名称,为其分配脚本,将其未来的父级设置为其所有者,以便将其保存到磁盘上,最后将其添加为 Main 节点:

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

像这样的脚本代码要比引擎侧C++代码慢得多。每次更改都会单独调用脚本API,这会导致后端出现许多“查找”以查找要执行的逻辑。

场景有助于避免此性能问题。 PackedScene 场景继承的基本类型是使用序列化数据创建对象的资源。引擎可以在后端批量处理场景,并提供比脚本更好的性能。

场景和脚本是对象

为什么这对现场组织很重要?因为场景 are 物体。人们经常将场景与使用子节点的脚本化根节点配对。这意味着场景通常是脚本声明性代码的扩展。

场景的内容有助于定义:

  • 脚本可以使用哪些节点

  • 他们的组织方式

  • 它们是如何初始化的

  • 他们之间有什么信号连接

许多面向对象的原则适用于编写代码 also 应用于场景。

场景是 始终是附加到其根节点的脚本扩展 . 您可以将它包含的所有节点视为单个类的一部分。

本系列中介绍的大多数技巧和技术都将在此基础上构建。