聊聊 2D 游戏的像素化中的问题

2019-05-06 10:10

引言

既然我是在做像素游戏,这次就聊一聊在对 2D 游戏像素化的过程中都会碰到的问题吧。

1. 照相机的投影

对于引擎来说,其实不管是 2D 游戏还是 3D 游戏它们的运行场景都是在一个 3D 空间下的(所以很多 2D 游戏其实干脆就是用 3D 场景的做法来做的,这样 3D 的渲染技术也可以直接运用到 2D 上)。而照相机的投影方式可以决定最终呈现的效果是 2D 的还是 3D 的。通常在游戏开发中较常使用到的投影方式有两种:

正交投影多用于做 2D 游戏;而透视投影多用于做 3D 游戏。当然啦,事实上透视投影也能做 2D 游戏。至于为啥很少有人用透视投影来做 2D 游戏呢?因为透视投影多了一个 Z 值会对画面的显示尺寸产生影响(透视存在近大远小么),导致操作起来比较麻烦。具体我就不展开了,有兴趣的可以查阅一下相关内容。总之,没啥特殊要求的话,选择正交投影就可以了。

2. 让像素变得有锯齿感

锯齿感是区别像素和非像素游戏的非常重要的感受。不同于其它风格的游戏,它们需要对画面进行平衡,边缘进行模糊来保证画面的圆润和谐。不过像素游戏就需要保留那种马赛克的感觉。除去美术在绘制时控制图片的风格以外,程序要也有相应工作要完成:

1. 关闭与“画面抗锯齿”有关的功能:

我们使用的引擎大多数默认不希望画面出现这种“锯齿感”的,所以它们默认会打开比如叫“反走样”啦,“抗锯齿”啦,“多重采样(俗称 MSAA,全称 Multi-Sample-Anti-Aliasing。以后如果看到某个图形技术的缩写带 AA 后缀的,一般就是处理抗锯齿的。顺便鄙视一下那些技术文档里面随手丢个英文缩写还不给全称和注释的做法,这种做法真的是优越感穷得只剩下英文缩写了)”啦等这些诸如此名的功能。对,没错,名字是很多,干的事情都是一件。大多数像素游戏自然不希望它被打开,所以一般情况下可以选择关掉它们。

2. 使用非平滑的贴图采样方式——最近邻采样方式:

上面的功能是对整个画面进行“抗锯齿”的;而这个功能决定了电脑如何获取图片中的颜色的。除去最近邻采样的方式以外,其它方式都会同时从图片上采样多个颜色,然后在通过某种方式(依据采样模式不同而不同)混合出一个新颜色作为最终从图片上获取到的颜色。这个可以说是对获取图片的颜色进行了“抗锯齿”。对于像素游戏而言,自然大部分情况下我们也不需要它,直接将它设置为最近邻采样吧;

多说一句,并不是所有的像素游戏都会选择关闭它们的,最终还是要依据需要的最终呈现效果而定。

3. 让像素看起来更大

为了复现的像素颗粒感。一般情况下像素游戏都需要显示比实际像素更大的像素。而这个更大,又包含了两个方面:

听起来似乎是一回事,确实这两个方面也确实有些交集。而我们平时做像素游戏一般关心的也就是“让像素变得更大”而已。我们会使用两倍甚至多倍的屏幕像素作为游戏里一个像素(为了区分,后面就把经过放大的像素叫它粗像素好了)。在这种方式下,自然粗像素看起来就变大了。

不过,影响“看起来更大”的不只是只有粗像素的尺寸而已。要知道,像素是衡量屏幕分辩率的东西,它的计量单位可不是厘米毫秒之类的直观尺寸单位。举一个极端点的例子,一个 10 英寸但最大分辩率为 100×100 的屏幕和一个 20 英寸但最大分辩率只有 50×50 的屏幕它们的屏幕像素尺寸就差了整整四倍。也就是我们的游戏如果运行在这个 10 英寸的屏幕上,需要将粗像素扩大到四倍的屏幕像素才能和那个 20 英寸屏幕像素看起来一样大。

我们在做游戏的时候多少会碰到这个问题(尤其是做 UI 的,估计对于这种问题很敏感)。庆幸的是,硬件的行业标准和完善的游戏引擎会让这个问题不会特别麻烦。前者让同类型设备之间的像素实际尺寸相等或是差距不大;而后者则会有对这一块提供了适配功能。而实际过程中,在差距较小或是对像素的实际显示尺寸要求不高的情况下,我们可以只关心如何“让像素变得更大”。但是如果开发者对这方面要求比较高或是存在跨平台的情况,也确实需要注意这个问题。

4. 让像素变得更大

我们在做像素游戏时最常考虑的问题。开发过程中,大致有如下的方式可以让像素变得更大:

1. 让美术资源放大:

这种方式很直接啦。举个例子就是我们把原本 50×50 的资源放大成 100×100。那么显示出来就是两倍于屏幕像素的效果。

2. 对渲染单位进行放大:

渲染单位,就是比如精灵(3D 渲染 2D 可能就是方体或是一个 3D 空间下的四边形)。我们也可以不对美术资源进行放大。转而在使用的过程中,对使用到的渲染单位进行放大来实现像素变大的效果。这种方式基本类同于第一种方式,无非把放大的工作由美术交给了程序。

3. 缩小照相机的视口区域:

这是一种逆向思维的产物。我们可以直接通过放大资源来实现最终呈现效果的放大;也可以通过缩小观察区域,并将其投影到屏幕来实现放大。这个过程简单来说就是两步:1.渲染缩小后的观察区域内的内容;2.将渲染结果投屏。为了完成这个工作,我们需要借助一下渲染到纹理(render-to-target)了:

举个简单的例子,假如我们的屏幕尺寸为 100×100;而一般情况下,照相机的视口大小也是 100×100。这个方式通过减小照相机的视口,比如将照相机的视口减小到 50×50,并将这个 50×50 区域内的内容先绘制到一张尺寸同样是 50×50 贴图上;最后再将此贴图再绘制到屏幕上,这样我们就等于实现了放大两倍的效果。

这种方式下,我们对资源以及资源相关的对象没有任何要求,完全一比一地来即可。同时速度和占用资源都会减少(后者自然不必说,前者的话因为第三种方式导致需要绘制的游戏场景区域变小,使得渲染过程更快);还有一点好处,对于严格一点的像素化需求来说,这种做法使得我们在坐标对齐的问题上可以少考虑些(后面会聊到)。

当然啦,这个方式虽然听起来不错,不过需要程序有额外工作量。这个额外工作也不仅仅是一个渲染到纹理的算法,毕竟使用的渲染区域已经不同于原有的区域了,对其他的效果渲染都会产生一定的影响。不过要是各位开发者使用 unity 的话,应该可以比较幸运地找到用这种方式实现像素化的插件。

5. 像素化后的坐标和照相机

其实完成上面的工作以后,我们基本上可以产出一个有像素感的画面了。对于一些非严格要求完全复古像素化的游戏来说,上面的工作已经满足需求。但是对于某一些更严格的像素游戏来说,还不够完善。问题出在我们的像素游戏因为最小单位由原来的一个像素变为现在的一个粗像素。那么展开来说是两个方面:

1. 坐标和尺寸的对齐:

既然我们现在的像素已经被放大,那么我们现在场景的最小单位也转变为粗像素的大小而不是原始的像素大小。我们自然要将场景内所有的游戏单位的坐标也好尺寸也好按照粗像素的大小对齐。没有哪个严格的像素游戏中,一个游戏单位的起点位置是:x 方向 1.5 个粗像素,y 方向 1.5 个粗像素;或者尺寸是:10.5 个粗像素宽;10.5 个粗像素高。

上述三种实现放大的方式中,方式一和二需要考虑坐标的对齐,而尺寸必定是对齐的;方式三无论坐标还是尺寸全部自然对齐,完全不需要考虑此问题(因为是投影等比放大的方式进行放大,所以不管尺寸还是坐标都是天然对齐的)。

2. 照相机的坐标和尺寸的对齐:

照相机自然也和游戏单位一样需要进行对齐:上述三种实现放大的方式中,方式一和二需要同时考虑坐标和尺寸的对齐;方式三仅需要考虑尺寸对齐即可。

我们主要说一说照相机尺寸的对齐:

这个问题会稍微麻烦一点,因为照相机的尺寸和屏幕尺寸有关系,但是屏幕尺寸是固定的。如果需要完美显示场景,我们要保证屏幕像素尺寸能够整除于粗像素的尺寸(这样屏幕上才不会出现类似于显示了半个粗像素的情况)。通常,对于两倍放大的像素游戏而言,基本不存在这个问题(因为几乎所有的显示设备的能够支持的尺寸都能整除2)。不过对于奇数倍或是其它不能够整除的情况,就需要其它方式来处理了。最为常见的方式就是加黑边来填充,通过对称填充黑边的方式来使得显示区域能够整除粗像素的尺寸。

结尾

好啦,这个就是对实现 2D 像素游戏的相关细节啦。希望能对打算开发像素游戏的开发者们提供一些参考。

分享到:
收藏