Gamemaker Studio 2 GML与buffer与C++

Author Avatar
くきふゆ 2月16日
  • 在其它设备中阅读本文章

终于弄明白了 GMS2 怎么用 C++ 的东西做扩展了。所以来记录一下GML中的string、buffer和DLL扩展相关内容。

字符串 - string

GML中的string十分傻逼。具体来说:

  • string类型不能当成array来用,比如用下标访问内容。要访问某个字符的内容需要用函数。
  • string序列从1开始编号。
  • GML没有char类型变量。利用chrord以进行unicode到字符(单字符字符串)的转换。
  • 单字符也是用双引号括起来的。单引号在GMS2中被抛弃了。(在GM8中是可以交换使用的)

但string支持一些友好的运算:

  • 字符串可以相加。例如:"51_"+"513"
  • 可以将常用类型强制类型转化为string,例如real(实数)和int64(整数)。例如:string(12.0)
  • 可以对实数转化用函数做更精确的调整,即string_format函数。

通过 这里 来访问string在官方文档上的内容。

内存处理 - buffer

GML中唯一可以按字节对内存进行任意处理的数据结构。类似 unsigned char 数组。

buffer内置一个指针,用于处理目前读写的位置。修改指针位置需要用函数 buffer_seek,访问具体位置的函数是 buffer_peek

创建buffer使用函数buffer_create(size, type, alignment)。参数分别为buffer的大小(字节数)、类型和对齐(字节数)。

常用的类型是两种:buffer_fixedbuffer_grow。前者规定buffer为固定大小,后者规定buffer在写入更多的内容时大小会增长。

对齐这个东西比较神奇。它说的是你每次用内置函数处理buffer内容时,指针移动的字节数,即多少字节分成一格。但buffer_tell这个用于返回你目前处理到第几字节的函数只会返回你最后一次写入的字节数位置+1,这与你即将要写入的下一个位置是不同的。挺意义不明的,实际上大部分时候你只需要将对齐设置为1就行,设置错误的话可能会出奇怪的错误,这依赖于进一步的实验。

关于buffer_grow,你可能会需要在buffer后写入buffer,但这玩意是必须要用buffer_copy来实现的。在使用buffer_copy时,buffer_grow大小不会增长。

buffer的数据处理主要依赖于buffer_readbuffer_write。指定写入的数据类型和值即可对内存进行操作。常用类型为buffer_u8(无符号8位整形,即8位二进制数),buffer_stringbuffer_text等。buffer_text是不以0值结尾的字符串,但谁也不知道GML的string相关函数会怎么处理含0值的字符串。通常使用base64_encode等不受0值影响的函数来处理字符串使得字符串不包含0值。

buffer是一种数据结构,在GML中理所当然的也通过一个独立ID进行访问。这意味着你可以在独立函数里对buffer进行处理,类似指针。GML本身并不支持指针或者引用等特性,甚至都不是很支持递归。

通过 这里 来访问buffer在官方文档上的内容。

GML 与 C++ - DLL 扩展的编写

新的编译器YYC支持将GML工程翻译成C++代码交给编译器编译成程序,虽然bug不少。故这给我们提供了一个新的将GML与C++共同运作的方式,即直接修改翻译过后的C++源代码再进行编译。GM社区已经有老哥做了这项工作,你可以自行google搜索相关信息。

当然还有一种更方便和友好的方式就是使用编译好的DLL(动态链接库)来与GML下的游戏进行直接沟通,而无需使用什么命令行和文件IO等绕圈子的方式。

动态链接库(英语:Dynamic-link library,缩写为DLL)是微软公司在微软视窗操作系统中实现共享函数库概念的一种实现方式。这些库函数的扩展名是.DLL、.OCX(包含ActiveX控制的库)或者.DRV(旧式的系统驱动程序)。

所谓动态链接,就是把一些经常会共享的代码(静态链接的OBJ程序库)制作成DLL档,当可执行文件调用到DLL档内的函数时,Windows操作系统才会把DLL档加载存储器内,DLL档本身的结构就是可执行档,当程序有需求时函数才进行链接。通过动态链接方式,存储器浪费的情形将可大幅降低。静态链接库则是直接链接到可执行文件。

DLL的文件格式与视窗EXE文件一样——也就是说,等同于32位视窗的可移植执行文件(PE)和16位视窗的New Executable(NE)。作为EXE格式,DLL可以包括源代码、数据和资源的多种组合。

在更广泛的意义上说,任何同样文件格式的电脑文件都可以称作资源DLL。这样的DLL的例子有扩展名为ICL的图标库、扩展名为FON和FOT的字体文件。

你可以在 这里 访问YYG官方提供的CPP扩展例子。总的概括下来实际上模板如下:

#include<cstring>

#if !defined( _MSC_VER)
#define EXPORTED_FN __attribute__((visibility("default")))
#else
#define EXPORTED_FN __declspec(dllexport)
#endif

extern "C" {
    EXPORTED_FN double fun(char *str) {
        // ...
    }
    // ...
    // your program
    // ...
}

GML实际上只有两种类型,一种是double,一种是字符串。因此在C++的DLL扩展中,参数和返回值的类型只能为char*(void*)或者double。看上去似乎十分受限制,但GML同样也支持传递buffer,即使用buffer_get_address(buff)函数,它会返回一个buffer的内存地址供DLL使用。但这个内存地址不能被释放和改变大小,也不能越界写入或访问数据。

编译和使用十分简单。在VS2019中新建控制台项目,然后写好你的程序,访问项目属性,配置类型改为.dll,F5编译即可。

在GMS中新建Extension,在Resources一栏中选择 Add File,进入下一级后在Functions一栏中选择 Add Function, 在Name中填写GML下的函数命名(你在GML中通过这个名字调用该函数),在Extern Name中填写C++中的函数名。选择返回值类型和添加 arguments,然后在Help中写下函数描述,例如fun(buffer, size)

每次重新编译之后都需要重新添加该DLL。

Something else

C++真的比GML快多了。

本文链接:https://acxblog.site/archives/gms2-gml-buffer-cpp.html
博客以 CC BY-NC-SA 4.0 协议进行许可。