Godot场景和脚本都是类

在Godot中,脚本和场景,两者都可以等同于面向对象编程语言中的类。主要区别在于:场景是 声明性代码,而脚本可以包含 命令性代码

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

本指南介绍了,脚本和场景在引擎核心中的工作方式,以帮助您了解Godot在引擎盖下的工作方式,并帮助您更好地理解本系列中某些最佳实践的来历。

Godot中类的意义

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

Godot的内部类具有使用一个 ClassDB 注册一个类数据的方法。该数据库提供对类信息的运行时访问。ClassDB 包含有关类的信息,例如:

  • 属性
  • 方法
  • 常量
  • 信号

当执行诸如访问属性或调用方法之类的操作时,对象会对此 ClassClass 进行检查。ClassDB 检查数据库的记录和对象的基本类型的记录,以查看对象是否支持该操作。

在引擎方面,每个类都定义一个静态的 _bind_methods() 函数,该函数描述其向数据库注册的 C++ 内容以及如何注册。使用引擎时,可以通过将 Script 附加到节点,来扩展 ClassDB 中可用的方法、属性、和信号。

对象在数据库之前检查其附加脚本。这就是脚本可以覆盖内置方法的原因。如果脚本定义了一个 _get_property_list() 方法,则Godot会将数据追加到,Object从 ClassDB 获取的属性列表中。其他声明性代码也是如此。

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

注解

结果,您可以实例化,代码中没有 extends 关键字的脚本,但是您不能将它们附加到一个 Node

脚本性能和 PackedScene

随着对象大小的增加,创建它们所需的脚本大小也变得越来越大。创建节点层次结构演示了这一点。每个节点的逻辑长度可能是几百行代码。

让我们看一个简单的示例,将单个 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,是使用序列化数据创建对象的资源。引擎可以在后端批量处理场景,并提供比脚本更好的性能。

场景和脚本是对象

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

场景的内容有助于定义:

  • 脚本可使用哪些节点
  • 它们是如何组织的
  • 它们是如何初始化的
  • 它们彼此之间有什么信号连接

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

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

本系列中介绍的大多数技巧和技术,都将以此为基础。