🔥码云GVP开源项目 12k star Uniapp+ElementUI 功能强大 支持多语言、二开方便! 广告
## 异步事件 ![物体编辑器异步事件](https://gamemakerchina.github.io/GMS2_manual_en2ch/source/_build/2_interface/1_editors/events/images/editor_objects_async.png)**异步事件**是当*GameMaker Studio 2*从某些外部源(可以来自 Web 或运行游戏的设备)收到 “回调” 时触发的事件。你告诉*GameMaker Studio 2*做一些事情,比如加载一个图像,然后它就会在加载图像时同时干其它事情。 然后,当请求(即加载图像)完成后,将向*GameMaker Studio 2*发送回调,并且将触发为该类型的回调定义的**异步事件**。请注意,异步事件将针对具有它们的*所有*实例触发,就像按键事件一样,因此你可以在一个实例中执行http\_get调用,但在另一个实例中使用异步 HTTP 事件来处理回调。 **注意**: 你应该知道,由于浏览器中的 XSS 保护,对来自跨域的请求和尝试加载资源的操作都被阻止,并且在使用以下任何事件时可能会返回空白结果 有与异步事件类别相关的各种类型的事件,它们都在以下部分中解释: **注意:**以下各节中提到的变量async\_load仅在这些事件中有效,因为指向的ds\_map是在**事件开始时创建的**,然后在结尾处再次删除,此变量在其他时间被重置为 -1。 录音回放 此事件只能由[音频队列播放函数](https://gamemakerchina.github.io/GMS2_manual_en2ch/source/_build/3_scripting/4_gml_reference/audio/audio%20buffers.html)触发,并将返回存储在变量async\_load中的[ds\_map](https://gamemakerchina.github.io/GMS2_manual_en2ch/source/_build/3_scripting/4_gml_reference/data_structures/ds%20maps/index.html),其中包含与触发事件的音频队列相关的不同键 / 值对。 选择要回放的音频队列并完成该音频队列中的缓冲区播放后,将触发该事件。 ds\_map 中将提供以下键: * "**queue\_id**" - 已完成播放的队列的索引,由函数[audio\_create\_play\_queue](https://gamemakerchina.github.io/GMS2_manual_en2ch/source/_build/3_scripting/4_gml_reference/audio/audio_create_play_queue.html)返回。 * "**buffer\_id**" - 不再播放的缓冲区的缓冲区 ID * "**queue\_shutdown**" - 在正常播放期间设置为 0,在接收到事件时设置为 1,因为已调用[audio\_free\_play\_queue](https://gamemakerchina.github.io/GMS2_manual_en2ch/source/_build/3_scripting/4_gml_reference/audio/audio_free_play_queue.html)。如果将其设置为 1,你不希望排队等待任何进一步的数据。 由于可以从多个缓冲区创建音频队列,因此当到达缓冲音频的每个部分的末尾时,可以针对一个队列多次触发该事件,因此使用了 “buffer\_id” 键。 **注意:**变量async\_load仅在异步事件中有效,因为指向的ds\_map是在事件开始时创建的,然后在结尾处再次删除,此变量将重置为值 -1。但是,**必须使用适当的函数清除**从事件创建的所有其他数据结构。 在下面给出的使用示例中,我们将创建一个音频队列,其中添加了 10 个缓冲的音频声音,然后播放队列: audio\_queue = audio\_create\_play\_queue(buffer\_s16, 11052, audio\_mono); for (var i = 0; i < 10; i++;)    {    audio\_queue\_sound(audio\_queue, audio\_buffer\[i\], 0, buffer\_get\_size(audio\_buffer\[i\]));    } audio\_play\_sound(audio\_queue, 0, true); 现在每个游戏步都会检测到录音将触发异步音频录制事件,你可以在此处处理录制的输入,如下所示: var queue = async\_load\[?"queue\_id"\]; var num = 0; if queue = audio\_queue    {    for (var i = 0; i < 10; i++;)       {       if async\_load\[?"buffer\_id"\] == audio\_buffer\[i\]          {          buffer\_seek(audio\_buffer\[i\], buffer\_seek\_start, 0);          num = i;          }       }    if num == 9       {       audio\_stop\_sound(audio\_queue);       audio\_free\_play\_queue(audio\_queue);       }    } 在这里我们检查队列 ID,如果它是我们想要的那个,我们就检查缓冲区 ID 以查看哪个缓冲已经完成播放。 然后,我们还将局部变量设置为该索引值以便稍后检查,并将缓冲区设置回该声音的开始。 一旦我们的局部变量达到 9,表明队列已经完成,我们停止播放声音并释放队列。 录音 此事件只能由[audio\_start\_recording()](https://gamemakerchina.github.io/GMS2_manual_en2ch/source/_build/3_scripting/4_gml_reference/audio/audio_start_recording.html)函数触发,并将返回存储在变量async\_load中的[ds\_map](https://gamemakerchina.github.io/GMS2_manual_en2ch/source/_build/3_scripting/4_gml_reference/data_structures/ds%20maps/index.html),其中包含不同的键 / 值对,具体取决于触发事件的函数的回调。 ds\_map 中将提供以下键: * "**buffer\_id**" - 可用于检索音频数据的临时缓冲区的 ID * "**channel\_index**" - 由此数据来自的调用函数返回的记录通道索引 * "**data\_len**" - 你收到的数据长度(以字节为单位) **注意:**如本页开头所述,变量async\_load仅在异步事件中有效,因为指向的ds\_map是在事件开始时创建的,然后在结束时再次删除。但是,请注意,所有音频与地图一起存储的临时缓冲区也将在事件结束时删除,因此如果要保留以供以后使用,则应将其复制到自定义缓冲区。 在下面的用法示例中,我们将创建一个自定义缓冲区来存储我们录制的音频,并将我们的游戏设置为从输入源 0 录制: channel\_index = audio\_start\_recording(0); audio\_buffer = buffer\_create(len, buffer\_fast, 1); 现在每个游戏步都会检测到录音将触发异步音频录制事件,你可以在此处处理录制的输入,如下所示: var channel = async\_load\[?"channel\_index"\]; if channel == channel\_index    {    len = async\_load\[?"data\_len"\];    buffer\_copy(async\_load\[?"buffer\_id"\], 0, len, audio\_buffer, 0);    } 在这里,我们只需检查 “channel\_index” 键以确保它与我们开始记录时返回的值匹配,如果是,我们将为此事件创建的临时缓冲区的内容复制到我们的自定义缓冲区中。 之后,你可以使用自定义缓冲区执行所需操作 - 你可以使用专用缓冲区音频函数播放它,你可以处理它并通过网络发送它,你可以将其保存到磁盘... 一旦你在缓冲区中有录制的音频,你可以做任何你想要的事情。 云 云事件是由其中一个[cloud\_函数](https://gamemakerchina.github.io/GMS2_manual_en2ch/source/_build/3_scripting/4_gml_reference/asynchronous%20functions/index.html)(如[cloud\_synchronise](https://gamemakerchina.github.io/GMS2_manual_en2ch/source/_build/3_scripting/4_gml_reference/asynchronous%20functions/cloud_synchronise.html))的回调触发的事件。它实际上生成了一个独特的ds\_map,并存储在特殊变量**async\_load**中(请参阅各个函数以获取更详细地解释此事件使用的代码示例)。 此 ds\_map 具有以下结构: * **"status":**这是状态码,其中负值表示错误,错误详情包含在 “errorstring” 中。 0 (或者大于 0 的值)代表成功(见下文)的返回, “resultString” 中包含返回的消息数据。 * * **"id":**返回被调用的函数的 id,如果你发送了一系列的cloud\_请求,你需要知道被回应的是哪一个,所以你要用这个值来和发送时存储的值进行比较来找到你需要的请求。 * * **"description":**上传的最后一个文件的描述。 * * **"resultString":**从云端返回的二进制数据。 * * **"errorString":**返回的错误消息。 返回值 "status" 的详细映射关系如下如下: | 状态值 | errorString / resultString | 描述 | | --- | --- | --- | | \-1 | errorString = "Not logged in to " | 未能成功登陆到给定的云服务。 | | 0 | resultString = recovered data | 从云端下载新的游戏数据(在 cloud\_synchronise 后调用)。 | | 1 | resultString = "AlreadySynchronized" | 自从上次调用以来 cloud\_synchronise 没有新的数据。 | | 2 | resultString = "ConflictDeferral" | 遇到冲突,但玩家选择忽略它。 | | 3 | resultString = "GameUploadSuccess" | 来自[cloud\_string\_save](https://gamemakerchina.github.io/GMS2_manual_en2ch/source/_build/3_scripting/4_gml_reference/asynchronous%20functions/cloud_string_save.html)或[cloud\_file\_save](https://gamemakerchina.github.io/GMS2_manual_en2ch/source/_build/3_scripting/4_gml_reference/asynchronous%20functions/cloud_file_save.html)的数据已成功上传到云端 | | \-n | errorString = Description of error | 负数意味着同步失败。 | 对话框 与上述事件一样,**对话框**事件仅在从其中一个特殊异步用户函数(如[get\_login\_async()](https://gamemakerchina.github.io/GMS2_manual_en2ch/source/_build/3_scripting/4_gml_reference/asynchronous%20functions/get_login_async.html))返回时触发(请参阅此函数以获取有关如何使用此事件的扩展代码示例)。 这些事件是要求某种类型的用户输入的事件,可以是名称、登录详细信息、数字或颜色等...... 由于大多数设备不喜欢在循环中等待回复,它们必须是异步的,*GameMaker Studio 2*将继续在后台运行,而这些函数打开对话框,直到它们获得触发此事件的所需用户输入。 同样,会返回[ds\_map](https://gamemakerchina.github.io/GMS2_manual_en2ch/source/_build/3_scripting/4_gml_reference/data_structures/ds%20maps/index.html),其 id 保存在特殊变量**async\_load**中。此映射中保存的值取决于所使用的功能,有关详细信息,请参阅本手册中每个功能的各个条目。 HTTP HTTP 事件是由一个[http\_函数](https://gamemakerchina.github.io/GMS2_manual_en2ch/source/_build/3_scripting/4_gml_reference/asynchronous%20functions/index.html)(如[http\_post\_string](https://gamemakerchina.github.io/GMS2_manual_en2ch/source/_build/3_scripting/4_gml_reference/asynchronous%20functions/http_post_string.html))的回调触发的事件。它实际上生成了一个ds\_map(有时称为“字典”),它独有于此事件并存储在特殊变量async\_load中(请参阅各个函数以获取更详细地解释此事件使用的代码示例)。 此ds\_map具有以下结构: * **"id":**从命令返回的id。如果你发出一系列http\_请求,那么你需要知道你得到了哪一个回复,因此你可以使用这个值来比较你最初发送请求时存储的值,以找到正确的请求。 * * **"status":**为错误返回小于 0 的值,为成功返回 0,如果正在下载内容则返回 1。 * * **"result":**收到的数据(仅限字符串),或者如果使用了[http\_get\_file()](https://gamemakerchina.github.io/GMS2_manual_en2ch/source/_build/3_scripting/4_gml_reference/asynchronous%20functions/http_get_file.html),则是下载的文件的路径。 * * **"url":**你请求的完整网址。 * * **"http\_status":**原始 http 状态代码(如果可用)。这将返回大多数浏览器的标准 Web 状态代码,例如:304 表示 “未修改” 或 204 表示 “无内容” 等... 这适用于使用http\_post\_string()函数时,但每个http\_函数可能会返回略有不同的映射,因此请参阅每个函数的手册条目,以找出为其返回的精确数据。 **注意:**当async\_load创建[ds\_map](https://gamemakerchina.github.io/GMS2_manual_en2ch/source/_build/3_scripting/4_gml_reference/data_structures/ds%20maps/index.html)时,这些函数在与[json\_encode](https://gamemakerchina.github.io/GMS2_manual_en2ch/source/_build/3_scripting/4_gml_reference/file%20handling/json_encode.html)和[json\_decode](https://gamemakerchina.github.io/GMS2_manual_en2ch/source/_build/3_scripting/4_gml_reference/file%20handling/json_decode.html)函数结合使用时特别有用。 如果你已请求下载文件,则此映射还可能提供其他数据。 在这种情况下,“status” 的值为 1,ds\_map 将保存这些额外的键: * **"contentLength":**这是 Web 服务器所说的你应该接收的文件大小(如果服务器不返回此数据,则可能为 -1)。 * * **"sizeDownloaded":**已下载的数据大小。 请注意,*不会*为接收到的每个数据包触发事件,而是在主游戏循环中的下载过程中随时更新事件。 另请注意,目前此功能仅适用于常规*Windows*目标平台。 应用内购 只有在为游戏激活[应用内购](https://gamemakerchina.github.io/GMS2_manual_en2ch/source/_build/3_scripting/4_gml_reference/in%20app%20purchases/index.html)时才能触发此事件。 如果添加了此功能,则在以下情况下将触发该事件: * 商店状态发生变化。 * 一种产品(或多种产品)已被激活。 * 产品和购买已经恢复。 * 已购买产品。 * 产品已被消费。 该事件将始终创建一个特殊的[ds\_map](https://gamemakerchina.github.io/GMS2_manual_en2ch/source/_build/3_scripting/4_gml_reference/data_structures/ds%20maps/index.html),其索引 ID 存储在内置变量**iap\_data**中。 此映射将*始终*包含键 “**type**”,它将包含以下常量值之一: | 常量 | 描述 | | --- | --- | | iap\_ev\_storeload | 在目标平台存储中检测到更改时会触发此操作。 | | iap\_ev\_product | 激活产品时会触发此事件,并包含有关产品的其他信息。 | | iap\_ev\_restore | 只有在使用[iap\_restore\_all](https://gamemakerchina.github.io/GMS2_manual_en2ch/source/_build/3_scripting/4_gml_reference/in%20app%20purchases/iap_restore_all.html)函数时才会触发此事件。 | | iap\_ev\_purchase | 购买完成后,将触发此事件。 | | iap\_ev\_consume | 当你使用函数[iap\_consume](https://gamemakerchina.github.io/GMS2_manual_en2ch/source/_build/3_scripting/4_gml_reference/in%20app%20purchases/iap_consume.html)时,它会触发此事件。 | 这些可能的事件 “类型” 中的每一个都将为iap\_datads\_map 添加额外的密钥,然后可以对其进行解析以获取必要的购买或产品信息。 下面列出了每个事件 “类型” 的iap\_data映射的确切内容。 iap\_ev\_storeload 当你激活购买时,你的游戏将尝试联系目标商店,在此过程中触发此事件。 然后,iap\_data映射将具有一个附加键 “**status**”,它将具有以下常量之一作为其值: | 常量 | 描述 | | --- | --- | | iap\_storeload\_ok | 已连接商店,连接良好。 | | iap\_storeload\_failed | 存在连接错误或由于某种原因存储不可用。 | iap\_ev\_product 所有激活的购买都会触发此事件 “类型”,但每次购买只会触发一次,因此如果你已激活十个产品,则应该会触发此事件十次。 如果你收到此事件,则iap\_datads\_map 将保留附加键 “**index**”,其中包含正在激活的产品的唯一**产品 ID**字符串。 然后,你可以使用函数[iap\_product\_details](https://gamemakerchina.github.io/GMS2_manual_en2ch/source/_build/3_scripting/4_gml_reference/in%20app%20purchases/iap_product_details.html)以及此产品 ID 来获取更多信息。 **注意:**Google Play(Android)一次只能请求产品的 20 条详细信息,这会导致具有大量产品的应用程序的加载时间相当长。 iap\_ev\_restore 当你调用函数[iap\_restore\_all](https://gamemakerchina.github.io/GMS2_manual_en2ch/source/_build/3_scripting/4_gml_reference/in%20app%20purchases/iap_restore_all.html)时,它将触发此事件,向iap\_data ds\_map添加 “**result**” 键。 此键将包含true或false,以指示是否已从目标商店成功还原购买数据。 iap\_ev\_purchase 在使用函数[iap\_acquire](https://gamemakerchina.github.io/GMS2_manual_en2ch/source/_build/3_scripting/4_gml_reference/in%20app%20purchases/iap_acquire.html)请求购买时,将触发此事件类型。iap\_datads\_map 将具有附加键 “**index**”,其中包含所购买产品的唯一**购买 ID**值。 然后,你可以使用函数[iap\_purchase\_details](https://gamemakerchina.github.io/GMS2_manual_en2ch/source/_build/3_scripting/4_gml_reference/in%20app%20purchases/iap_purchase_details.html)以及此购买 ID 来获取更多信息。 **注意:**如果购买的产品是消耗品,则**必须先使用**iap\_consume进行购买,然后才能进一步购买。 iap\_ev\_consume 使用函数[iap\_consume](https://gamemakerchina.github.io/GMS2_manual_en2ch/source/_build/3_scripting/4_gml_reference/in%20app%20purchases/iap_consume.html)时,将触发此事件类型。iap\_datads\_map 将具有附加密钥 “**product**”,其中包含正在使用的产品的唯一**产品 ID**字符串。 加载图片 只要你使用了具有适用加载文件功能的有效 URL 或路径,就会在将图像加载到*GameMaker Studio 2*时触发此事件。例如,假设你要加载精灵图像,并且仅在加载时将实例的当前精灵更改为新精灵。 那么你会在创建事件或计时器事件中有这样的事情(例如): spr = sprite\_add("http://www.angusgames.com/game/background1.png", 0, false, false, 0, 0); 现在,这将开始将图像加载到设备或浏览器中,但在等待加载文件时*不会*阻止*GameMaker Studio 2*。 相反,*GameMaker Studio 2*将继续正常运行,直到加载图像并且回调触发**加载图片**事件,其中创建[ds\_map](https://gamemakerchina.github.io/GMS2_manual_en2ch/source/_build/3_scripting/4_gml_reference/data_structures/ds%20maps/index.html)并将其存储在特殊变量**async\_load**中。 该映射包含以下信息: * **"filename":**你请求的文件的完整路径。 * * **"id":**已加载资源的 ID。这与你为其分配资源的变量相同。 * * **"status":**为错误返回小于 0 的值。 然后,你将在此事件中将新加载的图像分配给背景。 对于精灵和声音也是如此,如上所示为每个资源生成 ds\_map,以下代码示例演示了如何在此事件中使用返回的信息: if ds\_map\_find\_value(async\_load, "id") == spr    {    if ds\_map\_find\_value(async\_load, "status") >= 0       {       sprite\_index = spr       }    } 上面的代码将首先检查已创建的 ds\_map 的 id,然后检查回调的状态。如果该值大于或等于 0(信号成功),则回调的结果将用于将背景索引设置为新加载的图像。 联网 网络事件是由任何传入网络流量触发的事件,并链接到[网络函数](https://gamemakerchina.github.io/GMS2_manual_en2ch/source/_build/3_scripting/4_gml_reference/networking/index.html)。此事件生成一个特殊的[ds\_map](https://gamemakerchina.github.io/GMS2_manual_en2ch/source/_build/3_scripting/4_gml_reference/data_structures/ds%20maps/index.html),该事件专用于此事件,并存储在特殊变量**async\_load**中。 此ds\_map将包含不同的信息,具体取决于生成它的网络事件的类型。 常见网络事件 以下键对所有接收的网络功能都是通用的,并且始终存在于async\_load映射中: * **"type":**这将使下面列出的常量之一作为其返回值,并指定网络事件类型。 * * **"id":**正在接收事件的套接字 id(由[network\_create\_server](https://gamemakerchina.github.io/GMS2_manual_en2ch/source/_build/3_scripting/4_gml_reference/networking/network_create_server.html)或[network\_create\_socket](https://gamemakerchina.github.io/GMS2_manual_en2ch/source/_build/3_scripting/4_gml_reference/networking/network_create_socket.html)函数返回的实数)。 在大多数情况下,返回的套接字 ID 是触发事件的 TCP 或 UDP 套接字的 ID,但是如果在*服务器*中触发事件并且它是数据事件(见下文),那么套接字 ID 是发送数据的*客户端*套接字 ID。 * * **"ip":**套接字的 IP 地址(作为字符串)。 * * **"port":**与 IP 地址关联的端口(在使用 UDP 时很有用)。 “type” 键的可能返回值可以是下面列出的四个常量中的任何一个: | 常量 | 描述 | | --- | --- | | network\_type\_connect | 事件由连接触发。 | | network\_type\_disconnect | 事件由断开连接触发。 | | network\_type\_data | 事件由传入数据触发。 | | network\_type\_non\_blocking\_connect | 事件由配置为非阻塞的连接触发(你可以使用函数[network\_set\_config()](https://gamemakerchina.github.io/GMS2_manual_en2ch/source/_build/3_scripting/4_gml_reference/networking/network_set_config.html))。 | 连接 / 断开连接 如果你有类型为network\_type\_connect、network\_type\_non\_blocking\_connect或network\_type\_disconnect的事件,则async\_load映射将具有以下附加键: * **"socket":**此键将保持连接 / 断开套接字 ID。 * * **"succeeded":**此键将为 0 或 1,其中 0 表示连接超时,1 表示成功,套接字可以使用。 值得注意的是,当*服务器*断开连接时,网络事件不会在*客户端*中触发,并且当连接的服务器断开连接时,即使连接基于 TCP,也不会在客户端中触发network\_type\_\*事件。 接收数据 当你有network\_type\_data类型事件(表示你的网络已收到数据)时,创建的映射将具有以下键: * **"buffer":**这是由事件生成的唯一 “缓冲区 ID”。 创建一个 “增长” 类型缓冲区,字节对齐为 1,以保存 id 应存储在变量中,并用于***此事件中***对缓冲区的所有进一步函数调用。 就像async\_load映射一样,创建的缓冲区会在此事件结束时自动从内存中删除。 有关缓冲区的更多信息,请参阅[参考 - 缓冲区](https://gamemakerchina.github.io/GMS2_manual_en2ch/source/_build/3_scripting/4_gml_reference/buffers/index.html) * * **"size":**这是正在接收的缓冲区数据的大小(以字节为单位)。 **注意:**创建的缓冲区**仅在这些事件中有效**,并在事件结束时释放。 推送通知 推送通知事件是由设备操作系统上的**推送通知**回调触发的事件,可以是使用函数[push\_local\_notification](https://gamemakerchina.github.io/GMS2_manual_en2ch/source/_build/3_scripting/4_gml_reference/asynchronous%20functions/push_local_notification.html)的本地源,也可以是远程源(即:你的服务器)。它生成一个独特于此事件的ds\_map,并存储在特殊变量**async\_load**中(请参阅触发异步事件的各个函数,以获取更详细地解释此事件使用的代码示例)。此ds\_map具有以下键: * **"type":**值可以是设备本地通知的 “local”,远程通知的 “reomte” 或远程通知注册的 “register”。 * **"status":**成功的值为 “1”,错误的值为 “0”。 根据返回的 “类型” 和 “状态” 值,可能存在其他键条目。对于 “status”,如果返回错误(“0”),那么你还将拥有以下键: * **"error":**包含收到的错误的详细信息。 如果 “status” 值为 1(即:无错误),则ds\_map将包含以下附加值,具体取决于 “type” 键的值: * **"reg\_id":**如果收到的 “type” 是 “register”,则该键将保存设备注册 ID 以进行远程通知。 * **"data":**如果收到的 “type” 是 “local” 或 “remote”,则此键将保存你在调用通知函数时定义的字符串有效内容。 为了更好地理解这一点,我们在下面创建了一个小示例代码段供你查看。在这个示例中,我们将使用下面的代码来发送一个本地推送通知: var fireTime = date\_inc\_day(date\_current\_datetime(), 1); var data = "daily\_reward"; push\_local\_notification(fireTime, "Ahoy!", "Catch The Haggis Has A Present", data); 设置一个计时器在一天后向设备 “推送” 一个通知。一天以后,无论你的程序是在后台还是未启动状态,玩家都将收到之前设置的通知(在 iOS 平台标题被游戏的名字替代),然后一个异步的推送通知事件(Push Notification Event)会被触发。需要注意的是,如果推送时程序运行在前台,通知将*不会*被展示,但是异步推送事件**仍会触发**。在事件本身你会这样处理回调: var type = ds\_map\_find\_value(async\_load, "type"); var status = ds\_map\_find\_value(async\_load, "status"); if status == 0    {    //error of some kind    var error = ds\_map\_find\_value(async\_load, "error");    show\_debug\_message("error=" + string(error));    } else    {    if type == "register"       {       global.reg\_id = ds\_map\_find\_value(async\_load, "reg\_id");       }    else       {       var data = ds\_map\_find\_value(async\_load, "data");          if data == "daily\_reward"          {          global.Gold += 1000;          }       }    } 保存 / 载入 此事件将由与加载和保存文件缓冲区以及从内存加载或卸载音频时相关的某些函数触发。 事件本身将包含内置的async\_loadDS 映射,该映射将由特定函数所需的密钥填充。 这些列在下面的部分中。 缓冲区 使用函数[buffer\_save\_async()](https://gamemakerchina.github.io/GMS2_manual_en2ch/source/_build/3_scripting/4_gml_reference/buffers/buffer_save_async.html)或[buffer\_load\_async()](https://gamemakerchina.github.io/GMS2_manual_en2ch/source/_build/3_scripting/4_gml_reference/buffers/buffer_load_async.html)时,将在数据传输完成时触发异步事件。 此事件将使用以下键 / 值对填充async\_load映射 * **"id":**由所使用的函数返回的异步函数的 ID。 * **"status":**如果正确保存/加载数据,则返回true,否则返回false。 这允许你轮询保存 / 加载进度并显示消息或更改房间等...当过程完成时。 音频组 使用[音频组](https://gamemakerchina.github.io/GMS2_manual_en2ch/source/_build/3_scripting/4_gml_reference/audio/audio%20groups.html)时,你可以使用[audio\_group\_load()](https://gamemakerchina.github.io/GMS2_manual_en2ch/source/_build/3_scripting/4_gml_reference/audio/audio_group_load.html)和[audio\_group\_unload()](https://gamemakerchina.github.io/GMS2_manual_en2ch/source/_build/3_scripting/4_gml_reference/audio/audio_group_unload.html)函数加载它们并从内存中卸载它们。使用加载功能时,当为该组设置的完整音频文件集已加载到内存中时,它将触发此事件,并使用以下键 / 值对填充映射: * **"type":**这告诉我们被调用的事件的类型,并且将是 “audiogroup\_load” 来加载音频。 * **"group\_id":**将返回已加载的音频组的 ID(如[音频组编辑器](c:/source/yoyostudio/documentation/english/2_interface/3_settings/audio.html)中所定义)。 当为一个组加载所有音频时,此事件将触发,然后可用于更改房间,或播放音乐曲目等... 社交 此事件只能由各种特定的[社交函数](https://gamemakerchina.github.io/GMS2_manual_en2ch/source/_build/3_scripting/4_gml_reference/asynchronous%20functions/index.html)触发,并将返回存储在变量[async\_load](https://gamemakerchina.github.io/GMS2_manual_en2ch/source/_build/3_scripting/4_gml_reference/data_structures/ds%20maps/index.html)中的ds\_map,其中包含不同的键 / 值对,具体取决于触发事件的函数的回调。 ds\_map 将包含许多键,其中最重要的是 “*id*” 键。 这将返回一个**常量**,然后可以在代码中检查该常量以确定事件已收到的许多回调中的哪一个。 解析此密钥并将返回的值与可用常量进行比较后,你可以继续从映射中提取其余信息。 成就和排行榜 函数页面[社交游戏 - 成就和排行榜](https://gamemakerchina.github.io/GMS2_manual_en2ch/source/_build/3_scripting/4_gml_reference/asynchronous%20functions/achievements%20and%20leaderboards/index.html)参考部分中详细介绍了函数和回调以及用于触发此事件的常量,但是在某些情况下,如果没有来自游戏的函数调用,它将在运行时被触发: * 从操作系统常用工具栏启动游戏时 * 当你完成挑战时 * 当另一位玩家完成挑战时 * 从操作系统常用工具栏启动游戏时选择挑战 任何上述回调都将触发**社交事件(Social Event)**,并且将生成带有以下详细信息的async\_load映射(请注意,有一个不同的 “id” 键值来定义调用该事件的每个不同原因,但所有其他内容映射是一样的): * "**id**" - 此键的值取决于触发事件的回调类型。 它可以是以下常量之一: * **achievement\_challenge\_received**\- 已收到挑战 * **achievement\_challenge\_completed**\- 挑战已经完成。 * **achievement\_challenge\_completed\_by\_remote**\- 其他玩家已完成挑战。 * **achievement\_challenge\_launched**\- 游戏是从具有给定挑战的操作常用工具栏启动的。 * "**playerid**" - 挑战的玩家 id。 * "**issuerid**" - 发出挑战的人的 id。 * "**state**" - 挑战的状态,对于*无效*、*待定*、*已完成*或*已拒绝*,其值为 0 - 3(作为字符串)。 * "**message**" - 挑战的消息。 * "**issueddate**" - 挑战的发布日期 * "**completeddate**" - 挑战的完成日期。 * "**type**" - 给出的挑战类型。 可以是两个常量之一: * **achievement\_type\_score\_challenge**\- 基于分数值的挑战。 * **achievement\_type\_achievement\_challenge**\- 基于成就的挑战。 * "**identifier**" - 挑战的识别字符串。 * "**score**" - 分数与挑战是一致的。 你可以在下面找到一小段代码作为使用示例: var ident = ds\_map\_find\_value(async\_load, "id" ); if ident == achievement\_challenge\_completed;    {    player\_id = ds\_map\_find\_value(async\_load, "playerid");    issuer\_id = ds\_map\_find\_value(async\_load, "issuerid");    state = ds\_map\_find\_value(async\_load, "state");    date\_completed = ds\_map\_find\_value(async\_load, "completeddate");    date\_issued = ds\_map\_find\_value(async\_load, "issueddate");    ach\_type = ds\_map\_find\_value(async\_load, "type");    ach\_ident = ds\_map\_find\_value(async\_load, "identifier");    ach\_score = ds\_map\_find\_value(async\_load, "score");    } Facebook 一些[Facebook 函数](https://gamemakerchina.github.io/GMS2_manual_en2ch/source/_build/3_scripting/4_gml_reference/asynchronous%20functions/facebook/index.html)将触发社交异步事件并创建带有一些特定信息的ds\_map。 返回的信息将取决于已调用事件的 “类型”。 你可以通过读取 ds\_map 中的 “type” 键来检查这一点,这将是以下 Facebook 事件的字符串: * "**facebook\_permission\_request**" 当事件被触发并且属于这种类型时,还会有其他可以检查的键: * "**requestId**" - 触发事件的facebook\_request\_xxx\_permission()函数返回的请求 ID 值 * "**result**" - 这将是以下字符串之一: * "**granted**" – 用户接受了许可 * "**denied**" – 用户不接受一个或多个权限 * "**error**" – 发生错误 * **"error**" - 这将包含错误的描述,但*仅*在 “**result**” = “**error**”时出现。 Steam 此事件只能由[Steam API](https://gamemakerchina.github.io/GMS2_manual_en2ch/source/_build/3_scripting/4_gml_reference/steam%20api/index.html)函数触发,并将返回存储在变量async\_load中的[ds\_map](https://gamemakerchina.github.io/GMS2_manual_en2ch/source/_build/3_scripting/4_gml_reference/data_structures/ds%20maps/index.html),其中包含不同的键 / 值对,具体取决于触发事件的函数的回调。映射将始终包含键 “**event\_type**”,然后可以对其进行解析以查找触发事件的函数类型并更改所需的代码。 当调用任何触发此事件的函数时,它将生成一个唯一的异步 ID 值,该值应存储在变量中并进行检查,因为async\_load映射将始终包含一个 ID 密钥,然后你可以解析该密钥并确保你正在响应正确的事件。实际上,无论用于生成异步响应的 Steam 函数如何,映射都将*始终*包含以下键: * "**id**" - 触发事件的函数返回的异步 ID * * "**result**" - 操作的结果(实际值)。这将是 GML 常量ugc\_result\_success或其他一些实数。因此,你应检查此常量以确保调用成功,否则某些内容无法正常工作。返回的其余可能值显示为 Steam “EResult” 值的结果,你应该在 SDK 头文件steamclientpublic.h中看到所有 89 个可能值。 * * "**event\_type**" - 表示事件类型的字符串(详见下文) 上传数据 使用 Steam 函数上传排行榜([steam\_upload\_score()](https://gamemakerchina.github.io/GMS2_manual_en2ch/source/_build/3_scripting/4_gml_reference/steam%20api/steam_upload_score.html)或[steam\_upload\_score\_buffer()](https://gamemakerchina.github.io/GMS2_manual_en2ch/source/_build/3_scripting/4_gml_reference/steam%20api/steam_upload_score_buffer.html))时,返回的ds\_map将具有以下键 / 值对: * "**event\_type**" - 此键将保存 “**leaderboard\_upload**” 值 * * "**post\_id**" - 此键应与上传调用函数返回的 ID 值匹配 * * "**lb\_name**" - 此键包含已发布到的排行榜的名称 * * "**success**" - 如果传输成功,则为 1,0 为失败 * * "**updated**" - 如果排行榜得分实际更新(即:新得分更高),则为 1,否则为 0 * * "**score**" - 此键保留已发布的分数 你可以在分数上传功能的页面上查看此示例。 下载排行榜 使用 Steam 函数下载排行榜([steam\_download\_scores()](https://gamemakerchina.github.io/GMS2_manual_en2ch/source/_build/3_scripting/4_gml_reference/steam%20api/steam_download_scores.html)、[steam\_download\_scores\_around\_user()](https://gamemakerchina.github.io/GMS2_manual_en2ch/source/_build/3_scripting/4_gml_reference/steam%20api/steam_download_scores_around_user.html)或[steam\_download\_friends\_scores()](https://gamemakerchina.github.io/GMS2_manual_en2ch/source/_build/3_scripting/4_gml_reference/steam%20api/steam_download_friends_scores.html))时,返回的ds\_map将具有以下键 / 值对: * "**event\_type**" - 此键将保存 “**leaderboard\_download**” 值 * * "**id**" - 此键应与下载调用函数返回的 ID 值匹配 * * "**status**" - 回调的状态,其中 -1 等于失败或没有返回结果,0 等于成功。 * "**lb\_name**" - 此键包含已发布到的排行榜的名称 * * "**numEntries**" - 要返回的排行榜数据的 “行” 数。 * * "**entries**" - 包含另一个 ds\_map 的 JSON 对象字符串,它将包含键 “default”(表示其中未包含任何结果)或键 “entries”(你可以获取其值)。 “entries” 的返回值将是包含排行榜中每个排名的[ds\_list](https://gamemakerchina.github.io/GMS2_manual_en2ch/source/_build/3_scripting/4_gml_reference/data_structures/ds%20lists/index.html),其中列表中的每个条目本身将引用另一个ds\_map,其中包含键 “name”、“score” 和 “rank”,以及它也可能包含 “数据” 键,具体取决于用于上载的功能。 我们将通过请求给定排行榜的前十名排名并在**Steam 异步事件**中解析其结果来显示下载得分数据如何工作的示例(有关上传示例,请参阅相应的功能页面)。首先,我们需要使用以下代码请求分数: score\_get = steam\_download\_scores("Game Scores", 1, 10); 这将向 Steam 服务器发送请求,以获取排行榜 “Game Scores” 中的分数,并将请求的**异步 ID**存储在变量 “score\_get” 中。 然后,将通过以下方式在**Steam 异步事件**中处理: var async\_id = ds\_map\_find\_value(async\_load, "id"); if async\_id == score\_get    {    var entries = ds\_map\_find\_value(async\_load, "entries");    var map = json\_decode(entries);    if ds\_map\_exists(map, "default")       {       ds\_map\_destroy(map);       exit;       }    else       {       var list = ds\_map\_find\_value(map, "entries");       var len = ds\_list\_size(list);       var entry;       for(var i = 0; i < len; i++;)          {          entry = ds\_list\_find\_value(list, i );          steam\_name\[i\] = ds\_map\_find\_value(entry, "name");          steam\_score\[i\] = ds\_map\_find\_value(entry, "score");          steam\_rank\[i\] = ds\_map\_find\_value(entry, "rank");          if (ds\_map\_exists(entry, "data"))             {             var data = ds\_map\_find\_value(entry, "data");             var buffId = buffer\_base64\_decode(data);             var message = buffer\_read(buffId, buffer\_string);             show\_debug\_message( " -- attached message: " + string(message));             buffer\_delete(buffId);             }          ds\_map\_destroy(entry);          }       ds\_list\_destroy(list)       }    ds\_map\_destroy(map)    } 我们在这里做的是首先检查特殊async\_load映射的 “id” 键。如果此值与原始回调函数的值(存储在 “score\_get” 变量中)相同,则我们将继续处理数据。我们要做的第一件事就是为关键 “entries” 解析async\_loadds\_map,它将包含一个包含排行榜数据的 JSON 对象。然后将此 JSON 对象解码(请参阅[json\_decode](https://gamemakerchina.github.io/GMS2_manual_en2ch/source/_build/3_scripting/4_gml_reference/file%20handling/json_decode.html))作为另一个 ds\_map,并将此新映射 ID 存储在变量 “map” 中。 检查该映射是否有为 “default” 的键,如果有的话,则销毁该映射并退出该事件。如果未找到 “default” 键,则代码将解析映射以提取有关排行榜的必要信息,方法是首先从 ds\_map 的 “entries” 键中提取 ds\_list,然后循环遍历列表的每个条目以获取*另一个*ds\_map,其中包含每个条目的名称,分数和排名。然后将这些值存储在数组中,然后检查是否有其他 “数据” 键。如果有(即:分数是使用附加数据包上传的),那么我们也会解析它并将其发送到编译器控制台进行调试,然后再销毁缓冲区然后继续销毁映射。请注意,如果包含 “data” 键,则需要先使用[buffer\_base64\_decode()](https://gamemakerchina.github.io/GMS2_manual_en2ch/source/_build/3_scripting/4_gml_reference/buffers/buffer_base64_decode.html)对其进行解码,然后才能正确读取。 一旦循环完成,条目列表就会像从其获取的映射一样被销毁。无需销毁async\_loadds\_map,因为这是由*GameMaker Studio 2*为你处理的。 下载 UGC(用户生成的内容) 使用 Steam 功能下载[用户生成内容(UGC)](https://gamemakerchina.github.io/GMS2_manual_en2ch/source/_build/3_scripting/4_gml_reference/steam%20api/user%20generated%20content/index.html)时,其中一些将触发此事件。但是,每个函数都会生成一个具有不同键 / 值对的async\_loadds\_map(尽管它们通常将始终包含 “**id**”、“**result**” 和 “**event\_type**” 键),因此,请参阅特定于所使用的函数的页面,以获得详细信息和示例。 独特的 UGC 事件 当用户订阅游戏外的项目时,也可以触发 Steam 异步事件 - 例如,他们选择跳转到浏览器并订阅新项目然后选项回到游戏。在这些情况下,async\_load映射将**仅**包含以下详细信息(并且没有列出本页顶部的默认值): * "**event\_type**" - 此键将保存 “**ugc\_item\_installed**” 值 * * "**published\_file\_id**" - 新安装的 UGC 项的 ID(你可以使用函数[steam\_ugc\_get\_item\_install\_info()](https://gamemakerchina.github.io/GMS2_manual_en2ch/source/_build/3_scripting/4_gml_reference/steam%20api/user%20generated%20content/steam_ugc_get_item_install_info.html)来获取已安装项的路径) * 订阅创意工坊项目时也可以触发 Steam 异步事件 - 从应用程序内部或从创意工坊浏览器外部 - 并且在这些情况下,async\_load映射将包含以下键 / 值对: * "**event\_type**" - 此键将保存 “**ugc\_item\_subscribed**” 值 * * "**published\_file\_id**" : 此密钥具有新订阅项的已发布文件ID * 如果任何项目也未被订阅,则会触发该事件,数据结构映射包含以下内容: * "**event\_type**" - 此键将保存 “**ugc\_item\_unsubscribed**” 值 * * "**published\_file\_id**" : 此密钥具有未订阅项的已发布文件 ID * 系统 此事件只能由系统级事件触发(例如检测到游戏手柄或自动登录到 XBox Live),它将返回存储在变量async\_load中的[ds\_map](https://gamemakerchina.github.io/GMS2_manual_en2ch/source/_build/3_scripting/4_gml_reference/data_structures/ds%20maps/index.html),其中包含不同的键 / 值对,具体取决于系统触发回调的事件级别。 游戏手柄 当连接或断开游戏手柄触发此事件时,它将返回async\_load映射中的以下键 / 值对之一: * "**event\_type**" - 收到的系统事件的类型,它将是以下字符串之一: * "**gamepad discovered**" - 当系统报告已连接新游戏手柄时发生 * "**gamepad lost**" - 当系统失去与游戏手柄的连接时发生 * "**pad\_index**" - 已添加或删除的游戏手柄的索引 此事件现在允许你将所有游戏手柄检查逻辑从步骤事件或警报事件移动到系统事件中,并仅在实际需要时运行它。 虚拟键盘 当为打开或关闭的虚拟键盘触发此事件时,它将在async\_load映射中返回以下键 / 值对: * "**event\_type**" - 收到的系统事件的类型,它将是虚拟键盘的 “**虚拟键盘状态**”。 * * "**虚拟键盘高度**" - 虚拟键盘的高度(以像素为单位)。 如果键盘不可见,则该值为 0。 * * "**键盘状态**" - 键盘的当前状态,作为以下字符串之一返回: * "正在执行隐藏动作" * "隐藏" * "正在执行显示动作" * "可见" 有关虚拟键盘的更多信息,请参阅[此处](https://gamemakerchina.github.io/GMS2_manual_en2ch/source/_build/3_scripting/4_gml_reference/controls/virtual%20keys/index.html)。 XBox Live 在以**XBox One**为目标平台并使用**UWP**导出时,如果勾选[UWP 游戏选项](https://gamemakerchina.github.io/GMS2_manual_en2ch/source/_build/2_interface/3_settings/game_options/options_windowsuwp.html)中的**启用 XBox Live**选项,可以触发异步系统事件。当你启动启用了 Xbox Live 的*GameMaker Studio 2*UWP 项目时,项目将自动尝试以静默方式登录 Xbox Live。 此登录尝试的结果将作为async\_load映射中的以下键 / 值对之一返回: * "**event\_type**" - 收到的系统事件的类型,它将是以下字符串之一: * "**user signed in**" - 静默用户登录已成功完成 * "**user sign in failed**" - 静默用户登录失败(当发生这种情况时,你可以使用函数[xboxlive\_show\_account\_picker()](https://gamemakerchina.github.io/GMS2_manual_en2ch/source/_build/3_scripting/4_gml_reference/xbox_live/xboxlive_show_account_picker.html)让用户选择要登录的帐户) * "**user signed out**" - 用户已退出 有关 XBox Live 可用特定函数的更多信息,请参阅[此处](https://gamemakerchina.github.io/GMS2_manual_en2ch/source/_build/3_scripting/4_gml_reference/xbox_live/index.html)。 Audio System Status This event type is specifically for use when working with the HTML5 target, as it will be triggered every time the Web Audio context status changes. This means that if you have, for example, a piece of looping background music, then you can pause it or stop and restart it, based on this event triggering. This can be checked by looking for the following key/value pair in theasync\_loadmap: * "**event\_type**" - the type of system event received, which will be the following string for an audio event: * "**audio\_system\_status**" - The audio system has initialised or the context has changed. If this event type is triggered, then there will be an additional key in theasync\_loadmap: * "**status**" - The status of the audio system, which will be one of the following two strings * "**available**" - The audio system is initialised and available to play sounds * "**unavailable**" - The audio system is not initialised, or the context is not currently running, and so can't play sounds (all sound playing functions will return -1) For more information on the specific functions available for Audio, please see[here](https://gamemakerchina.github.io/GMS2_manual_en2ch/source/_build/3_scripting/4_gml_reference/audio/index.html). **NOTE**: While this event is designed for use with the HTML5 target, it will also be triggered on*all*other platforms, but on everything (except HTML5) it will only be triggered once on Game Start when the audio engine is first initialised.