核心类型

Godot有一组丰富的类和模板组成了它的核心,所有的东西都建立在它们之上。

这篇参考文献将试图列出它们,以便更好地理解它们。

定义

godot使用标准的c98数据类型,例如 uint8_tuint32_tint64_t 等等,现在每个编译器都支持。重新设计轮子并不是很有趣,因为它使代码更难阅读。

一般来说,除非使用大型结构或数组,否则不要为给定任务使用最有效的数据类型。 int 在大多数代码中使用,除非必要。这是因为现在每个设备都至少有一个32位总线,并且可以在一个周期内完成这些操作。它也使代码更加可读。

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

对于Unicode字符,使用chartype而不是wchar_t,因为许多体系结构有4个字节长的wchar_t,其中可能需要2个字节。但是,在默认情况下,这不是强制的,chartype直接映射到wchar_t。

参考文献:

记忆模型

PC是一个很好的体系结构。计算机通常有千兆字节的RAM、千兆字节的存储空间和千兆赫兹的CPU,当应用程序需要更多的资源时,操作系统将替换掉不活动的资源。其他的架构(如移动或控制台)通常比较有限。

最常见的内存模型是堆,在堆中,应用程序将请求一个内存区域,底层操作系统将尝试将其放在某个位置并返回。这往往是最好的工作,是灵活的,但随着时间的推移和滥用,这可以导致分割。

分段会缓慢地产生对于大多数常见分配来说太小的孔,从而浪费内存。有很多关于堆和分段的文献,所以这个主题在这里不会得到进一步的发展。现代操作系统使用分页内存,这有助于减轻分割问题,但不能解决它。

然而,在许多研究和测试中,都表明,如果有足够的内存,那么如果最大分配大小低于给定的阈值(与最大堆大小和打算不使用的内存比例成比例),那么随着时间的推移,分段将不会是一个问题,因为它将保持不变。换句话说,让10-20%的内存空闲,然后执行所有小的分配,这样就可以了。

Godot确保可以动态分配的所有对象都很小(最多不到几个KB)。但是,如果分配太大(如图像、网格几何或大型数组),会发生什么情况?在这种情况下,godot可以选择使用动态内存池。需要锁定此内存才能访问,如果分配耗尽内存,则池将根据需要重新排列和压缩。根据游戏的需要,程序员可以配置动态内存池大小。

分配内存

Godot有很多工具可以跟踪游戏中的内存使用情况,特别是在调试期间。因此,不应该使用常规的C和C++库调用。相反,还提供了其他一些。

对于C样式的分配,godot提供了一些宏:

memalloc()
memrealloc()
memfree()

这相当于通常的malloc,realloc,不受标准C库的限制。

对于C++风格的分配,提供了特殊的宏:

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

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

相当于新建、删除、新建 []删除[] .

MeMeNe/MeMeDelk也使用了一点C++魔术,并在创建对象后立即通知对象,并在删除前立即通知它们。

对于动态内存,提供poolvector<>模板。POOLVector是一个标准的向量类,与C++标准库中的向量非常类似。要创建池向量缓冲区,请使用以下命令:

PoolVector<int> data;

可以使用[]运算符访问poolvector,并为此存在一些助手:

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

这些操作允许从池向量快速读/写,并将其保持锁定,直到它们超出范围。但是,poolvector应该用于小型的动态内存操作,因为read()和write()对于大量访问来说速度太慢。

容器

Godot还提供一套通用容器:

  • 矢量

  • 集合

  • 地图

它们是简单的,目的是尽可能的小,因为C++中的模板经常被内联,并且在调试符号和代码中都使二进制大小变得更胖。列表、集合和映射可以使用指针进行迭代,如下所示:

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

Vector<>类还有一些很好的特性:

  • 它是在写的时候复制的,所以只要不修改,复制它是很便宜的。

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

Godot还提供了一个字符串类。这个类有大量的特性,在所有函数(如case操作)和utf8解析/提取中都完全支持Unicode,以及用于转换和可视化的帮助器。

参考文献:

StringName

字符串名称类似于字符串,但它们是唯一的。从字符串创建StringName会为所有相等的字符串生成唯一的内部指针。字符串名称对于使用字符串作为标识符很有用,因为比较字符串基本上就是比较指针。

创建一个StringName(尤其是一个新的)很慢,但是比较很快。

参考文献:

数学类型

在core/math目录中有几种线性数学类型。

参考文献:

NodePath

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

参考文献:

RID

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

参考文献: