上一章节我们完成了shape的建立,现在游戏里面的元素(blocks,shapes)都已经完成了,背景也搭好了(array2D),让我们开始制定游戏规则吧。首先就是需要让我们的shape掉下来,还记得我们刚开始的时候每个600毫秒要刷新一下屏幕呢?那会还有一个closure我们都不太明白是干嘛用的,马上就知道了。
好了,今天章节过后,你的程序运行起来应该是这样的:
让我们来修改代码吧,这次要修改的代码比较多,而且没有上一章节那样重复的工作。不用太过担心,我们一步一步来:
在#1, 函数在执行时会根据上一章节写的每个不同shape子类的blockRowColumnPosition关系,决定每一个block旋转后的位置。
接下来,我们可以简单地从函数的名字上得出他们的作用,lowerShapeByOneRow, 每次将shape下落一行,而具体如何下落,就看 #2 中的shiftBy函数,这个都很简单,就不用详细解释了。
#3
的moveto函数是直接将blocks移动到指定的行和列,为什么有这个函数呢?别急,很快我们就将看到它了
#4 中,我们将随机生成之前建立的7个不同形状的shape中的一个。
接下来,我们需要一个新的类Swiftris来管理游戏的逻辑和进程,比如游戏的初始化,开始,结束等等。建立新的类的步骤就不需要多说了,我们直接来写代码吧
目前看来,我们的swiftris类挺简单的,放心,一会它会变得复杂起来。
我们注意到在swiftris里面我们声明了3个变量,一个是用来表示每个block位置的二维数组blockArray,一个是nextShape,最后是fallingShape,我们可以很容易从名字上得出nextShape就是我们用来预览下一个是什么形状的,玩过俄罗斯方块的童鞋都想起来了吧,而相对应的,fallingShape就是游戏中我们正在操作的shape,我们可以旋转,移动等等来操作它。
接下来init函数我们生成一个20行,10列的二维数组,用来表示blocks的位置。
beginGame中我们随机生成一个shape,用我们shape类中最后写的那个函数,然后把它放在我们制定的位置中。
在#2
中,我们有一个函数,返回的值是fallingshape和nextshape,当然fallingshape就是我们之前已经生成的nextshape了,我们用在shape类中新建的movtTo函数把他一到我们的游戏区域的中间,然后生成一个新的nextshape。逻辑上没有问题吧。
OK,逻辑关系处理好了,接下来轮到我们处理视觉上的效果了。终于可以在scene上大展手脚了!
#1, 我们定义了每个block的大小,20x20
#2, 我们定义了一些SKNodes,最下面的是gameLayer,它上一层是shapeLayer,然后是gameBoard
我们来补充下没有显示完全的gameBoard:
let gameBoard = SKSpriteNode(texture: gameBoardTexture, size: CGSizeMake(BlockSize * CGFloat(NumColumns), BlockSize * CGFloat(NumRows)))
#3, 不要小看这个函数,这其实是我们整个GameScene中最重要的函数,pointForColumn(int,int) 。他根据column和row来计算每一个block的锚点位置,所以返回的是一个point坐标,只有根据这个坐标,我们才能把每个block放置在shapeLayer上。其实它计算的就是每个block的中心点的坐标。
#4, 我们把新生成的nextShape添加到屏幕中去,注意函数其中的一个参数用的是一个空的闭包,因为函数最后有个添加动作的函数
runAction,它里面有个参数 completion,这里我们用个()->() 闭包还是个占位的,如果有我们就用,没有就空着。和最开始我们接触到的那个tick闭包一样,不过,很快我们就能看到tick不在是一个空的闭包了。这个我们后面会讲到。
这里我们把SKTexture对象存在一个字典里面,因为每一个shape会有很多block,而我们是要重复利用这些image的
#5, 这里我们用到了之前定义的pointForColumn函数精确地每一个block添加到准确的位置,我们是从row-2开始的,这样可以显得我们的动画更加平滑地进入画面中
#6, 我们添加了一组动画,我们让每个block的alpha从0变化到0.7 ,因为这样更容易让用户有一种动画的感觉。关于里面各个参数,以及时间的长短,大家可以自己手动改变一下,然后看看效果上有什么变化。
接下来的两个函数确保同样的SKAction移动和重画不同shape的每一个block
好了,我们的drawing layer已经写好了,我们的逻辑layer也已经搞定了,接下来,让我们用户交互类 GameViewController来把他们两个关联起来吧
#1 我们给我们的tick 闭包设置了一个函数,看,之前我们不能理解的()->() 到这里也能明白了,每当屏幕刷新一下时,我们就执行didTick函数,我们把fallingShape往下移动一行,然后在scene上重新画出来它的形状。
好了,让我们运行下程序,看一看我们的俄罗斯方块下落的动态吧。