嗨!大家好,我是小蚂蚁。
在之前的两篇文章中,我们首先打开了自己的“创作者之眼”,看到了消除游戏背后那张看不见的网格,想象一下你所玩过的消除游戏,是不是其中的每一个图标都遵循着网格布局在排布?接着我们继续做了进一步的修炼,将“创作者之眼”继续升级,除了那张网格,我们也看到了一些数字,这些数字就是每一个图标的标识。
今天,我们继续修炼一些基本功。是的,在正式开始制作具体类型的消除游戏之前,我们需要把基本功练扎实。这样在你真正制作游戏的时候,才能够知其所以然。
在网格的那一节中,我们讲了一些基础的数学知识,再次回顾一下这张图片。
根据网格的中点,行列数以及边长,我们首先计算出了左下角的起点位置。有了起点位置,就可以根据当前的行号和列号计算出每个格子的位置。
依靠着这些简单的数学知识,我们描绘出了一个网格,并且让每一个元素出现在该出现的位置。这是一个消除游戏的基础,有了这样的基础布局,才能够进行下一步的操作。
在消除游戏中,通常你需要先选中某一个图标,然后再进行后续的操作。对于玩家来讲,只需要用手指点住想要选择的那个图标即可,但是对于游戏制作者来讲,我们就需要做一些处理来确保玩家点住的就是他想点的那个图标。
两个坐标系
想要了解其中的原理,需要先了解两个不同的坐标系。
- 屏幕坐标系
如图,中间的蓝色网格区域是消除游戏的操作区域,外围的绿色边框表示的屏幕区域,可以理解为你的手机屏幕,在屏幕区域中存在着一个坐标系,即屏幕坐标系,以屏幕的左下角为原点,假设当前的设备屏幕是 400 x 800 的尺寸,那么屏幕坐标系就如图中红色箭头标注的一样,屏幕的最右侧为 400,屏幕的最上侧为 800。
不同的游戏引擎中屏幕坐标系的原点是不一样的,有的以左下方为原点,向右向上为正方向,有的以左上方为圆点,向右向下为正方向,还有的以屏幕中心为原点,不论原点在哪里,道理其实都一样,无非是计算会稍有差异。本文以左下方为原点举例,在不同的游戏引擎中实现时需要根据具体情况做出相应的调整。
- 网格坐标系
网格坐标系指的是针对于整个消除布局网格的坐标系,它以整个消除网格中左下角的第一个元素位置为原点,如图中紫色箭头标注的坐标系。还记得我们是如何计算出左下角第一个元素的位置的吗?忘记了的话,请回顾上方带有计算公式的图片或者回顾之前的文章。
很显然对于网格坐标系来讲,它的原点位置并不是(0,0),没有硬性的规定,坐标系的原点必须是(0,0),这一点需要清楚。
坐标系的转换
请注意,接下来我们又要涉及一点儿数学知识了,不用担心,并不难理解。
如图,网格布局左下角起点(绿色圆点)的位置我们之前已经算出来了,是(x0,y0),现在假设玩家点击了红色点的位置 (x1,y1),这个点的位置是可以知道的(不同的游戏开发工具都有提供获取屏幕点击位置的功能),这个点的位置其实是针对于屏幕坐标系的位置。
现在我们有了这样两个已知条件:
当前点击的屏幕坐标的位置(x1,y1) 网格坐标系左下角起点的位置(x0,y0)
根据当前的已知条件,可以计算出当前点击位置在网格坐标系中的位置,也就是图中橙色线所标注的(x2,y2)。
x2 = x1 - x0 y2 = y1 - y0
到这里为止其实我们完成了这样的一件事:将当前点击位置的坐标由屏幕坐标系转换到了网格坐标系。
为什么要去做这样的坐标转换呢?因为只有在网格坐标系中,我们才能够将位置坐标转换成对应的行号和列号,进而找到玩家到底点击的是哪个图标。
行列号的计算
接着再加入一个已知的条件,即元素的边长 n,我们就可以通过下方的计算得到当前元素在网格中的行号和列号了。
行号 = (y1 - y0)/n + 1 列号 = (x1 - x0)/n + 1
是不是又迷糊了?我们看看下图中举出的例子,然后再对照着看这个公式。
如图,现在我们假设左下角起点的位置为(50,200),然后玩家当前所点击的屏幕的位置为(100,250),当前每个元素的边长是 50。将这些已知的数值带入计算行号和列号的公式:
行号 = (250-200)/50 + 1 = 2 列号 = (100-50)/50 + 1 = 2
由此可知,当前点击的是第 2 行第 2 列的图标。
根据这个例子,再回顾一个上方的计算行列号的公式,很不是就比较容易理解了呢!
容错处理
这里举的一些数值例子很方便计算,因为主要目的是帮助理解计算公式。但是在真实的游戏中,不可能总是恰到好处的得到能够整除的数字,但是很明显我们行列号最后又都必须是整数,此时该如何处理呢?
如图,任何一个红点的位置都是玩家可能点击的位置,有些红点的位置甚至都没有位于图标之上,对于玩家来讲这些点击应该都是有效的,因为我就是想要点第 2 行第 2 列的那个图标,我的手指可能不是很精准,但是我不管,我点的就是那个,如果图标没有被选中,不是我的问题,一定是游戏出了问题。
作为游戏创作者的我们又需要做一些事情了,来容许玩家的操作有一定的误差,我们要尽可能的保证玩家当前点击的位置就是他想要选择的图标。听起来是不是有些玄妙?怎样才能确保一定的容错率呢?怎样才能确定玩家想要选的到底是哪个图标呢?
答案就是利用“四舍五入”。又是一个在你知道真相之前感觉无比玄妙的东西,在捅破了一层窗户纸之后变得不过如此的例子。
我们利用四舍五入把计算行列数的公式改造一下,就变成了这样:
行号 = 四舍五入((y1 - y0)/n + 1) 列号 = 四舍五入((x1 - x0)/n + 1)
这里又举出了一些位置坐标,拿出计算器带入上方的计算公式,算算吧!看看是不是通过“四舍五入”之后,得到的行列号都是 2 。
是不是没想到你学过的四舍五入在游戏中竟然可以有这样的应用?竟然能够用于改善玩家的操作体验,增加游戏的操作容错率。
通过这样一个小小的改善,我们的游戏就能更具“人性化”一点,仿佛在告诉玩家:不论你的眼神儿好不好,手指点击的精不精准,都没关系,你尽管点,我知道你想点的是哪里。
元素的标识
到此为止,我们已经能够通过玩家点击屏幕的位置,经过一系列的计算,知道玩家当前要点击的元素的“行号”和“列号”了,通过行列号我们就能够在布局网格中找到那个对应的元素。我们可以把这个行列号叫做元素的标识,标识有什么作用呢?当你有很多的元素时,如果想要在其中进行精确查找的话,最好的方式就是为每一个元素分配一个唯一的标识(就像是我们的身份证号),当想要找到某一个元素时,只需要知道它的标识,就能够把它精确的找出来。
如图,我们使用行号和列号作为元素的标识时,其实就是这个样子,每一个元素都有一个唯一的[行列号],我们也可以通过这个[行列号]找到那个确定的元素。
使用行列号作为标识同时需要两个数字(行号和列号),通常我们还会使用另一种方式,通过行列号计算出一个数字索引来作为标识,这样使用一个数字就可以标识出一个元素了。
数字索引的计算公式是这样的:
索引 = (行号-1) * 总列数 + 列号
假设有 3 行 3 列元素,当前元素位于第 2 行第 1 列,那么
索引 = (2-1) * 3 + 1 = 4
反过来根据这个数字索引,我们也能够倒推出行列号:
行号 = 向上取整(索引/总列数) 列号 = 索引 - (行号-1) * 总列数
假设有 3 行 3 列元素,当前元素的数字索引是 8 ,那么:
行号 = 向上取整(8/3) = 3 列号 = 8 - (3-1) * 3 = 2
得到它位于第 3 行第 2 列的位置。
有了上方的这两个行列号和数字索引的互推公式,其实我们就可以做这样的一件事,就是把二维的数组或者表格转换成一维的数组或者列表。
只要愿意的话,你完全可以使用一个一维的数组或列表实现一个消除游戏,因为它其实就是一个“压扁”了的表格。当然你没有必要这么做,除非你所使用的游戏开发工具或者编程语言对于二维数组或者表格缺乏支持。否则,你应该优先选用更直观的表格或者数组。举这个例子是想说明,即使是在一个处处受限的开发条件下,你仍然能够找到其它的解决方案,只要善于利用已有的资源,学会变通,其实有很多的选择。