用Surface实现转场特效(一)

大家好!下面这篇超赞的教程作者是 David Strachan,如果你觉得本文有用可以去看看他还有其它很多关于 GameMaker 的教程,下面附有完整的源码可以下载,同时也可以让你了解之后的第二部分都会有些什么内容—— Shaun Splading
下载地址
译者:这是 1.4 的工程文件,但导入 GMS2 可以完美运行,有一些实例创建需要兼容处理但针对转场部分是可以直接参考的。
原文地址:How to Make 3 Screen Transitions in GameMaker using Surfaces

当 Shaun Splading 邀请我来写一篇客座 blog 文章时我就想,我要面向那些掌握了基础知识希望能有进一步提高的人写一点东西以帮助他们更了解 GML 是如何工作的。
因此,本篇文章我将给各位展示 GameMaker 里的表面(Surface)这一功能,我觉得这是我曾经没有足够重视的众多功能之一。而在过去的几个月里,我一直沉迷于如何运用表面来实现遮罩、照明、描边、阴影以及其它诸多强大的图形功能。
我们将学习如何实现以下三种不同的场景(Room)切换效果,从最简单的开始,难度逐渐增加,本文我们仅介绍第一部分内容。现在你大概在想“屏幕切换已经有很多教程了,你又能说出什么特别的东西呢?”跟很多其它教程不同,我的教程可以同时展示两个场景的画面进行过渡。
让我们先来预览一下这三种转换机制的效果

以上这些场景过渡不仅同时展示出两个场景的画面,在最后一个效果中第二个场景的画面看上去还被拆成了一个个小块儿。但这只是一种障眼法,其实只是用很多图像进行组合以实现这种效果。
如果你以前没有使用过表面,你可以把它想象成一张空的画布,你可以像绘制精灵一样在上面绘制各种画面,并且你在表面上绘制的内容都会被保存下来,然后你可以把这些表面本身再作为一个单独的元素绘制到你的屏幕上。
在本文中,我将展示如何捕获屏幕当前状态的图像以及我们将用于触发转换过程的基本方法,我们以下面这个滑动过度作为例子进行说明:

项目概览

这个项目包含了 2 个场景和 8 个对象(Object)

  • 3 个对象是执行切换的按钮
  • 1 个是按钮的父对象
  • 1 个是返回按钮

我们暂时忘掉上面的那几个,来看看我们最关心的:

  • obj_transitionslide
  • obj_transitionquarters
  • obj_transitionbars

这些对象将用于创建转换效果

第一部分我们会来看一下第一个—— obj_transiiton !

OBJ_TRANSATION

这种简单又常见的切换过渡适合移动设备上使用,手机玩家比较习惯于用手指拖动来切换画面,并且移动的速度应该与手指滑动速度保持 1:1。
CREATE 事件

currentframe = 0
maxframes = 45

persistent = true; // 设为持久化以保证 room 切换时不被销毁

//把原 room 的画面保存下来这样在进入新的 room 以后也能使用这个画面
sur_oldroom = surface_create(room_width,room_height);
surface_copy(sur_oldroom,0,0,application_surface)

//前面已经存好原 room 的画面了所以现在就跳去新的 room 吧
room_goto(room_second) 

currentframe and maxframes——用来记录切换的时长以及控制最大值,如果你想让切换时间变长只需要把 maxframes 调大即可。

persistent——用来标记为“持久化”,这样在切换场景时这个对象就不会被销毁

surface_create() – 用这个方法创建一个新的表面用来保存画面并存入一个变量

surface_copy() – 用来复制表面,这里我们直接复制了 application_surface ,这其中已经包含了整个游戏的画面

同时使用 surface_create() 和 surface_copy() 获得 application_surface 的画面基本上就是一张完整的游戏截图。

附注:根据你的游戏,也可以考虑使用maxframes = room_speed * 1.5 的机制,这样你就可以根据游戏的 FPS 速度关联过渡效果时长,并使用更准确的计时系统。此外,您必须记住在完全转换完成之前,不要移动任何物体或播放任何动画,直到下个场景完全加载完成再进行这些操作。

STEP 事件

currentframe++

if (currentframe > maxframes) {
    instance_destroy() // 转场结束销毁
}

// 进入了新的 room,获取新 room 的画面
if (currentframe == 2) { 
    sur_newroom = surface_create(room_width,room_height);
    surface_copy(sur_newroom,0,0,application_surface)
}

在步事件(Step)中,当我们完成了场景转换就自动销毁转场对象,另外当处于第二祯时会保存新的场景的画面,就跟之前保存原场景的画面一样,这样我们就在 sur_oldroom 中保存了原场景的画面,并在sur_newroom 中保存了新场景的画面。
DRAW GUI 事件

if (currentframe > 1) {

    // 根据帧数计算 room 切换的像素值
    var slideamount = EaseOutQuad(currentframe,0,room_width,maxframes)

    if (surface_exists(sur_oldroom)) {
        draw_surface(sur_oldroom,-slideamount,0)
    }

    if (surface_exists(sur_newroom)) {
        draw_surface(sur_newroom,room_width-slideamount,0)
    }
}


///在进入新 room 第一帧闪现一帧原 room 的画面
if (currentframe == 1) { 
    if (surface_exists(sur_oldroom)) {
        draw_surface(sur_oldroom,0,0)
    }
}

这里我们使用了 EaseQuad()(具体见下文,并不是非用不可)来创建一个介于 0-room_width 之间的数字,这个数字会随着帧数的增长而增长,然后我们把两个表面相邻而绘,这样看起来这两个 room 就是同步滑动的了。

这些代码都非常直观,除了最后的那一小块儿,我们之所以在进入新场景以后闪现一下原场景的画面是为了避免切换 room 时画面的切换闪烁,因为我们通常需要在进入新 room 以后再通过application_surface 来获取游戏画面,但 application_surface 的画面不包括 draw_gui 中绘制的 gui 层画面,因此我们可以通过这种方法同时获取下方的场景画面并且避免玩家直接看到新场景的画面。(并不清楚 yoyo 为什么这么设计,但就是能这么操作)
DESTROY 事件

surface_free(sur_newroom)
surface_free(sur_oldroom)

最后,在切换结束以后我们需要做的就是清理一下不用的表面。

脚本说明

我想我最好提一下用过的 3 个脚本,这里面包含的都是非常基本的数学方法,但可以帮助你实现一些很不错的效果。基本上这些脚本可帮你绘制曲线,它通过提供输入和输出范围来实现这一功能。事实上会比这更进一步,因为这会是一个线性转换,这会使得转换更流畅没有停顿感,你不是非用不可,但这么做会使得图像滑动和切换更自然顺滑。
这就是我们的第一个切换效果了!
在这里你应该学会了如何记录屏幕画面并将这些内容传递到下一个场景中。

你可能会注意到,每次我要使用表面来绘制平面画面时,都会先确保这个表面是否存在。这非常重要,因为表面是易变的,甚至可能在不作任何通知的情况下自动从内存中清除。

如果你想要获得其它场景过度的特效灵感,我收集了一些,你可以在下面这个地址浏览:

http://www.davetech.co.uk/screentransitions

在我的网站上还有其他 GameMaker 的指南,另外还在 Twitter 上发布我的游戏开发信息:@DavesInHisPants

感谢阅读,敬请期待下一篇!

" Go forth and code " ——David Strachan

2018-12-11 10:25
Comments
Write a Comment