Unity 3D俄罗斯方块

来源:互联网 发布:我的奋斗 希特勒 知乎 编辑:程序博客网 时间:2024/05/06 02:20

学习unity也有一段时间了,从一开始的懵懵懂懂到现在的学有所小成,心里挺是高兴的,不过目前还是处于初学者阶段,很多东西还等着自己去发掘去学习。

最近做了一个3D俄罗斯方块小游戏作为练手,花了有两个多星期吧,感觉话费太多时间了,中间不断地修改实现的方法,但确实让自己学到很多东西,可能对有些人来说,俄罗斯方块根本算不上什么,不过对于初学者来说,练手还是能学到很多东西的,好了,废话不多说,接下来说说我如何完成的吧,先贴上最终的效果图


其实对于3D的俄罗斯方块,我觉得可以用多种方法实现的,一开始,我尝试用unity提供的碰撞机制去做这个游戏,结果问题一个个接踵而来,解决了一个又出现了另一个,最后只能上网找教程,网上是有网友实现了的,有一种unity实现方法是直接用GUI,把button当作方块,虽然也能做出很好的效果,但是我觉得这样就不算3D了吧。另外我还找到一份代码,它实现了真正意义上的3d俄罗斯方块,不过它是JS写的,我试着看了一下代码,然后自己用C#写了一个自己的,看代码里面的注释,应该是外国的,看完它的代码,着实让我学到很多东西,我之前想通过真正的碰撞去完成这个游戏,结果问题多多,但是网上的这份代码提出了一种很聪明的解决方法,用两个布尔类型的数组分别存放已经固定下来的方块和正在掉落的方块,通过判断下落方块下一个位置对应坐标的布尔值的真假,为真则停止下落,固定位置。另外可以将边界对应的值设为真,从而方块在碰到边界无法继续运动。在检测满一行方块消除的时候也相当的方便,可以直接对布尔数组进行操作。这种思维让我恍然大悟,才发现自己真的是有待提高啊。

好了,开始动手,首先你需要一个正方体模型,它的边界需要经过一些处理,在多个相连的时候才能显现出接痕,这个我自己用Maya做了一个,很简单的。然后导入模型的FBX文件到你的项目中。

接着创建一个新场景,在场景中创建边界,如效果图中所示,用unity自带的Cube就可以了,改一下一些参数就可以有图中的效果,当然你也可以自己制作边界物体。设置左边界x坐标为3,右边界为14,这不是硬性规定说得设置成这样的,这不过是方便我编写代码而已,哦,另外,由于方块的最大size是4,所以我两边都留出了4个位置,中间10个位置是方块可以放置的地方,底边界放置在0位置。创建一个空物体对象命名为Manager,并为其创建一个绑定脚本Manager.cs,接着一一创建对应方块的空对象,对其各自命名,注意,之前我导入的正方体只是方块的组成单体,不同形状的方块需要四个正方体拼接在一起,正常俄罗斯方块游戏中有7种方块,将这七个空物体对象创建为Prefab,一一对应命名,再接着为方块创建绑定脚本,这里,我只需要一个脚本文件,即7种方块都绑定同一个脚本。

我试过各自做出七个不同的模型,结果,很麻烦,由于模型是一个整体,消除部分是一个问题,然后我还要写七个脚本对应不同的方块,其实有很多东西是一样的,这简直就是浪费时间和资源。然后我发现了网上代码中用了一种和巧妙的方法,在绑定的脚本中,设置一个公有的字符串数组,值只有0和1,显示出方块的形状,比如直方块,即4个正方体直线连接在一起,这是我们需要填上字符串数组的size为4,然后string[1]的值为“1111”,其余都为0,大家应该都明白了吧,一个方块,算上周围的空的地方围成一个正方体,必须是正方体,不然旋转之后,你的数据没办法修改。

在Manager中创建一个公有的GameObject数组,然后把7种方块对应的Prefab拖拽到数组里面,Manager随机生成方块,绑定方块的脚本根据字符串数组组合正方体。这一步算是最重要的吧,根据字符串数组计算组成方块各正方体的位置,一开始就是因为老是弄错正方体的位置所以除了点问题,还有就是旋转轴心,轴心错了也会让你接下来的步骤漏洞百出。

首先,在之前我们创建方块空对象物体的时候,坐标都是为(0, 0, 0)的,就以这个作为轴心点坐标,如果size是奇数的话,那么处于围绕方块大正方体中间的那个正方体的中心就是轴心坐标,如果size为偶数的话,大正方体的中心便为轴心,以轴心坐标确定各个正方体的位置,这是相当重要的。注意,各个正方体在方块中的坐标不同于在世界坐标中的坐标,轴心坐标即方块在世界坐标系中的坐标,找对了轴心才能正确算出各个正方体在世界坐标系中的坐标。

还有,上文提到的,将字符串数组转换为布尔类型的数组,方便做碰撞检测,确定轴心后,各部分的正方体位置就容易计算了,可以根据size,参照值为(size-1)* 0.5,大家画一下图就知道为什么是(size-1)*0.5了,方法不是唯一的,大家有更简便易懂的方法更好啦,贴一段代码吧:

for(int y=0;y<size;y++){for(int x=0;x<size;x++){if (block[y][x] == '1'){blockMatrix[y, x] = true;    var cube = (Transform)Instantiate(Manager.manager.cube, new Vector3(x - childSize, childSize - y, 0), Quaternion.identity);    cube.parent = transform;}}}

根据字符串数组中‘1’对应的x,y与参照值做运算便可以计算出其位置了

接着便是计算各正方体在世界坐标系中的x坐标,即方块在世界坐标中的坐标减去(size-1)*0.5,求出方块外围正方体左上角的第一个正方体的x坐标,同样道理,这个应该不难理解,其y坐标为已知,可根据它来确定方块的起始下落位置,参考值也是(size-1)*0.5。哦,起始下落位置的设置也是有规律的,当size为偶数时,坐标必须是符合规定范围内的一个整数加0.5,如果是奇数的话,中心正方体的坐标可作为方块的起始坐标

当方块旋转时,判断其是否可以旋转(边界等),可以旋转,则要变换方块对应的布尔数组,跟着相同方向旋转数组即可。

当方块停止下落时,销毁方块,同时将记录下来的各个正方体的坐标对应的产生正方体,这一步是为了接下来方便销毁满一行的正方体,修改对应的整个区域的布尔数组,上文提到的。判断是否满一行也很简单,同一个y坐标,连续十个x坐标对应的布尔值为真即销毁正一行的正方体,然后大于y的正方体集体下落一个单位,再继续检测。

以上已经完成游戏的大半部分了,也算是解决了一个技术难点吧,接着是运行,本实现方法用yield来实现游戏一帧一帧的动画,初开始接触这个东西,只是普通的认为它就是一个延缓暂停的机制。最近才知道其真正的用法。这里我们需要在Start函数中执行我们要的操作,然后游戏就可以如我们所期望的那样运作了。其实,用Update函数也是可以实现的,可能会麻烦一点,用yield反而比较简便。

在while(true)循环里面使用yield,满足特定条件时跳出循环,注意,请务必设置一个跳出的特定条件,而且是程序会最终执行到那一步的,不然,程序会陷入死循环。另外,在有些时候延缓执行或者整个程序进入等待,这里需要特别注意一下,因为yield毕竟不能像update函数那样,在一帧一帧里实现你要的效果,稍微错漏某个地方都会让你的游戏没法像预期那样运行,可以自己尝试着在代码中修改让后运行,根据结果的不同可更清楚地认识了解yield的用法。还有就是yield的用法在JS与C#中大为不同,这是需要注意的,在把JS代码重写成C#代码的时候需要特别注意这一点。

大概就这么多吧,这里附上两个代码资源,一个JS版的,一个C#版,可根据自己的语言喜好选择,不过JS代码中有些东西跟上述的不符,不过大概原理是一样的。

3D俄罗斯方块JS版

3D俄罗斯方块C#版

原创粉丝点击