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 具有构成其核心的丰富的类和模板集,并且所有内容都基于它们构建。

这份参考将试着按顺序列出它们,使之更容易被理解。

定义

Godot 使用标准的 C99 数据类型,如 uint8_tuint32_tint64_t 等,现在每个编译器都支持。为这些东西重新发明轮子没什么意思,只会让代码更难以阅读。

通常,除非使用大型结构或数组,否则不必在意是否使用给定任务的最有效数据类型。除非必要,否则大多数代码都使用 int。这样做是因为如今每个设备都至少具有 32 位总线,并且可以在一个周期中执行这样的操作。它也使代码更具可读性。

对于文件或内存大小,使用 size_t,保证为 64 位。

对于 Unicode 字符,使用 CharType 而不是 wchar_t,因为许多架构中 wchar_t 的长度是 4 个字节,而我们需要的是 2 个字节。但是默认情况下,这不会被强制,CharType 会直接映射到 wchar_t。

参考:

内存模型

PC 是一个很棒的架构。计算机通常具有 GB 级的内存、TB 级的存储空间和 GHz 级的 CPU,当应用程序需要更多资源时,操作系统会交换出不活动的资源。其他架构(如移动设备或游戏主机)在这些方面通常有更多的限制。

最常见的内存模型是堆,应用程序会从中请求一块内存区域,底层操作系统会试着从某处找到一块适合这样的内存并返回它。这通常效果最好,并且很灵活,但是随着时间的流逝和滥用,这可能会导致分段。

分段缓慢地产生对于大多数常见分配而言太小的孔洞,从而浪费了内存。关于堆和分段的文献有很多,因此在此不再赘述。现代操作系统使用分页内存,这有助于减轻但不能解决分段问题。

然而,许多研究和测试显示,在给出足够内存的情况下,如果最大分配大小所占堆最大大小的比例低于一个给定的阈值,并且有一定比例的内存本来就是不使用的,那么分段就不会变化,不会随着时间的推移成为问题。换句话说,如果留出 10-20% 的可用内存空间,并且执行的都是小额内存分配,那么就不会出事。

Godot 确保所有的可动态分配的对象都很小(最多不到几 kb)。但是如果出现很大的分配(像图像、网格几何数据或大数组)会怎样呢?在这种情况下,Godot 可以选择使用动态内存池。内存会被锁定以进行访问操作;如果内存不足,则内存池会根据需要进行重新排列和压缩。根据游戏的需要,程序员可以配置动态内存池的大小。

内存分配

Godot有许多工具可用于跟踪游戏中的内存使用情况, 尤其是在调试期间. 因此, 不应使用常规的C和C++库调用. 相反,Godot提供了一些其它的供你使用.

Godot提供了一些宏可以用来处理C风格的内存分配:

memalloc()
memrealloc()
memfree()

这些等效于标准C库中通常的malloc, realloc, free.

这些宏可以用来处理C++风格的内存分配:

memnew( Class / Class(args) )
memdelete( instance )

memnew_arr( Class , amount )
memdelete_arr( pointer to array )

这些等效于new, delete, new[]和delete[].

memnew/memdelete也使用一点C++的小技巧, 在对象创建之后和删除之前, 通知对象.

对于动态内存, 提供了PoolVector<>模板.PoolVector是一个标准的Vector类, 与C++标准库中的vector非常相似. 要创建PoolVector缓冲区, 请使用以下命令:

PoolVector<int> data;

可以使用[]运算符访问PoolVector, 并且存在一些帮助:

PoolVector<int>::Read r = data.read()
int someint = r[4]
PoolVector<int>::Write w = data.write()
w[4] = 22;

这些操作允许从PoolVectors快速读/写并保持锁定直到它们超出作用域. 但是,PoolVectors应该用于小型动态内存操作, 因为read()和write()对于大量访问来说太慢了.

参考:

容器

Godot 还提供了一系列通用的容器:

  • 向量

  • List

  • 设置

  • Map

They aim to be as minimal as possible, as templates in C++ are often inlined and make the binary size much fatter, both in debug symbols and code. List, Set and Map can be iterated using pointers, like this:

for(List<int>::Element *E=somelist.front();E;E=E->next()) {
    print_line(E->get()); // print the element
}

Vector<>类还具有一些不错的功能:

  • 它会在写入时进行复制, 因此只要不对其进行修改, 制作拷贝就很容易.

  • 通过在引用计数器上使用原子操作, 来支持多线程.

参考:

字符串

Godot 还提供了一个字符串类 String.Godot还提供了String类. 此类具有大量功能, 在所有函数(如大小写操作)和utf8解析/提取中均提供全面的Unicode支持, 以及提供用于转换和可视化的帮助函数.

参考:

StringName

StringName(字符串名称)和 String(字符串)类似,但它们是唯一的。从字符串创建 StringName 会为所有相等的字符串产生唯一的内部指针。StringNames 对于将字符串用作标识符非常有用,因为比较它们基本上是在比较指针。

创建一个字符串名称(尤其是一个新的) 是慢的, 但比较它们是快的.

参考:

数学类型

在core/math目录中, 有几种线性代数常用的类型.

参考:

节点路径

这是一种特殊的数据类型, 用于在场景树中存储路径并快速引用它们.

参考:

RID

RID是资源ID. 服务使用这些来引用存储在其中的数据.RID是不透明的, 这意味着它们引用的数据不能直接访问.RID是唯一的, 甚至对于不同类型的引用数据也是如此.

参考: