狐狸的小小窝

我是小狐狸~欢迎来我的小窝!
Gamemaker

FoxWinShave 2 中文使用手册

FoxWinShave 2 现已上架 GameMaker: Marketplace,支持 GM8, GMS 1.4 和 GMS 2。点此从Marketplace下载最新版本


0. 前置条件

  • 仅支持 Windows 平台。
  • 支持 Windows XP, Windows Vista, Windows 7, Windows 8, Windows 8.1, Windows 10。
  • 支持 GameMaker 7, GameMaker 8, GameMaker 8.1, GameMaker Studio 1.4.x, GameMaker Studio 2 Desktop。

1. 安装插件

1.0 GM 7, 8, 8.1

菜单栏 File-> Import Resources… 选择 FoxWinShave-GM8.gmres 导入插件及 Demo。

  • 在弹出的 Importing Resources 对话框中,请务必勾选 Scripts 和 Include Files 以导入插件运行所必须的文件和脚本。
  • 如需同时导入 Demo 代码及素材,请将 Sprites,Objects 和 Rooms 一并勾选上。

1.1 GMS 1.4.x 及 GMS 2 Desktop

Import resources: FoxWinShave 对话框
  1. 在 GameMaker: Marketplace 上购买本插件。(点此购买)
  2. 在 GMS 中菜单栏选择 Marketplace -> MyLibrary,找到 FoxWinShave,点击 Download。
  3. 下载完成后,点击 Import。
  4. 在弹出的 Import resources: FoxWinShave 对话框中,选中 Scripts 和 Included Files,然后点击中间的 Add 按钮以导入插件运行所必须的文件和脚本。
  5. 如需同时导入 Demo 代码及素材,那就直接点击中间的 Add All 按钮。
  6. 点击左下角 Import 按钮完成插件的导入。

2. 插件入门 101

2.0 基础准备

请务必在游戏设置中将窗口设置为无边框(borderless window):

GM 8 设置无边框
GMS 2 设置无边框

稍后将会解释需要设置为无边框的原因。

2.1 插件初始化

使用本插件的任何脚本前需要先进行插件的初始化。建议在首个 Room 的 Creation Code 内或者某个控制用的 Object 的 Game Start 事件里执行:

NF_WS_Init();

插件初始化后请勿再次调用该函数。

2.2 插件释放

当不再需要更改窗口形状后,可以执行以下函数将插件卸载以释放资源:

NF_WS_ResetWindow(); // 重置窗口形状为默认,非必要
NF_WS_Free(); // 释放插件

在释放插件后如果需要再次使用本插件,需要重新进行插件的初始化。

2.3 基础原理解释

2.3.0 窗口坐标系

将游戏窗口想象成一个画布,按照窗口的边框开始算左上角坐标为 (0, 0),右下角边框最外处坐标为 (viewport_width + 左边框宽 + 右边框宽, viewport_height + 上边框高 + 下边框高),如下图所示:


窗口坐标系统

然而问题在于窗口的边框宽度是由 Windows 系统定义的,不同 Windows 版本或者不同的主题都有可能带有不同尺寸的边框,给在定义窗口的形状时计算坐标带来了麻烦。因此如上文所述,请务必在游戏全局设置中关闭窗口边框的显示,这样窗口的坐标系统就和游戏 View Port 的坐标系统的原点能够统一,便于将窗口形状与游戏绘制内容对齐。

下文在描述坐标时将默认使用无边框窗口。

2.3.1 游戏绘制与 View Port

GM 支持在一个窗口中同时显示多个 View Port。View Port 的作用是将 Room 的一部分内容按一定比例、缩放和位移映射在窗口中显示出来。用户实际看到的内容是经过 View Port 变换后的结果,并不(一定)是游戏中调用绘制代码时使用的坐标。

由于我们只关心用户实际看到了什么,因此下文中提到的“游戏绘制内容”的尺寸、坐标,皆为经过 View Port 变换后映射到窗口坐标系中的尺寸和坐标。

更多关于 View Port 的内容,请参考 GM 官方文档,下文将不再赘述。

2.3.2 窗口形状遮罩

本插件采用遮罩形式设置窗口形状,支持根据 Sprite 和 Surface(仅限 GMS)设置窗口遮罩。以下核心函数

NF_WS_ShaveWindow(sprite, subimage, x, y);
NF_WS_ShaveWindowAlpha(sprite, subimage, x, y, alpha);
NF_WS_ShaveWindowExt(sprite, subimage, x, y, xscale, yscale, alpha);
NF_WS_ShaveWindowSurfaceExt(surface, x, y, xscale, yscale, alpha); // 仅限 GMS

与 draw_sprite_* 系列绘制函数有着十分类似的形式,除了不支持 rot 和
colour参数,同时 alpha 参数有不同的意义。可以理解为将给定的 Sprite / Surface 用提供的参数放置于窗口坐标系内作为遮罩(不经过 View Port 等任何变换),同时将窗口没有被遮罩覆盖的区域给“消去”变为透明:

“遮罩层”内容

如图所示,红色部分的窗口会被保留下来,其余部分将会被消去。需要注意的是上图所谓的“遮罩层”是一个与窗口同样大小的位于窗口之上的想象出的一层,并不是实际存在的可见图层,其内容仅为了作为遮罩所用,并不会显示出来。

遮罩层使用的图像为彩色图像带 Alpha 通道。在实际运行时色彩通道会被完全忽略,最终窗口形状仅受遮罩层的 Alpha 通道影响。默认情况下每个 Alpha 通道值为 0 的像素(即纯透明像素)被认为是窗口需要被消去的部分。这个值可以通过上文所述的几个函数中的 alpha 参数(范围 [0, 1.0])来改变。任何 Alpha 通道值 <= 该参数的像素将被认为是需要消去的。注意,这意味着如果你传入的 alpha 参数为 1,则任何像素都会被认定为透明像素。

另外,如果使用 Surface 来传入遮罩内容,请注意 Surface 的背景需要用透明色填充:

surface_set_target(mask_surface);
draw_clear_alpha(c_white, 0); // Alpha = 0, 使用的颜色无所谓.

2.3.3 遮罩对齐

使用本插件的目的通常是想将游戏窗口的形状,裁剪成游戏绘制的有意义的内容的形状:

游戏实际绘制内容

上图中,女牛仔的部分是有意义的、需要保留的部分,而黑色背景需要被裁剪掉,也就意味着我们在遮罩层“绘制”的遮罩需要与女牛仔的位置、形状相同。

这乍一听似乎很容易,只要使用相同的 x,y 参数将同样的女牛仔 Sprite 绘制到 Application Surface 和遮罩层即可。但是正如上文所说,用户实际看到的内容是经过 View Port 变换过的,因此其实际显示的坐标和尺寸可能与绘制时传入的参数不同;然而遮罩层是不受 View Port 影响的,因此开发者需要自行计算遮罩层图像的位置和尺寸,用以对齐经过 View Port 变换后的游戏内容。建议不要用过于复杂的 View Port 设置。

2.3.4 窗口形状更新时间与绘制事件顺序

由于 GM 运行原理所致,游戏绘制的内容并不会在绘制语句执行的瞬间显示出来,而是要等到当前 Step 所有绘制完成后才会刷新绘制缓存以显示绘制的内容。这意味着窗口外形的更新需要与绘制内容的刷新同步发生,否则就会发生窗口形状与所显示内容有所偏差的问题(比如游戏仍然显示的是上一帧的内容,但是窗口形状已经被裁剪成了下一帧所需的形状):


绘制内容与窗口形状没有同步更新

由于 Windows 窗口外形更新时机所限,本插件需要在 GM 的每个 Step 中,于每个 Step 开始 (Begin Step)至 Draw 系列事件开始之前对窗口形状进行更新,以保证窗口形状的更新与绘制内容的更新能够同步完成。如果在 Draw 系列事件中进行窗口形状的修改,则会发生窗口形状与绘制内容不同步的问题,请务必注意。建议在 End Step 事件之中更新窗口形状。

再次提示,请不要在 Draw 系列事件中更新窗口形状!除非你十分清楚可能发生的后果!

2.4 复杂遮罩

多次执行以下任意核心函数来改变窗口形状,则后一次的遮罩将会覆盖前一次的遮罩,并不会互相叠加:

NF_WS_ShaveWindow(sprite, subimage, x, y);
NF_WS_ShaveWindowAlpha(sprite, subimage, x, y, alpha);
NF_WS_ShaveWindowExt(sprite, subimage, x, y, xscale, yscale, alpha);
NF_WS_ShaveWindowSurfaceExt(surface, x, y, xscale, yscale, alpha); // 仅限 GMS

这意味着如果所需的遮罩是由多个元素组合而成的复杂形状,则需要开发者自行准备好已经事先组合好所有元素的单张 Sprite / Surface 用作实际的遮罩。

举例来说,如果你需要在屏幕上显示两位女牛仔,那么你需要准备如下的一张 Sprite / Surface 作为遮罩:

复杂遮罩

连续调用两次 NF_WS_ShaveWindow_* 函数并不能实现如上所述的需求。假设第一次传入了左侧牛仔的遮罩,第二次传入了右侧的遮罩,则只有右侧会正常显示出来,而第一次设置的遮罩会被覆盖而失效。


3. 常见问题

  1. 使用 Sprite 做遮罩时要注意所用 Sprite 的 xoffset 和 yoffset。

2 Comment

发表评论

电子邮件地址不会被公开。 必填项已用*标注

此站点使用Akismet来减少垃圾评论。了解我们如何处理您的评论数据