本文仅供个人记录和复习,不用于其他用途
游戏效果展示

简单的说一下,我们必须点击黑块,才能够往下走一步,点击白块是没有效果的。当点击到绿块时,计时就会停止,游戏就结束了。当然,我们只能够点击第二行(最下面的是第一行)。
具体思路
我们需要两个类,一个用于生成游戏的层,一个用于生成块。生成块的类需要设置块的大小、位置、颜色。那么为了方便起见,我们就设立好最基本的创建方法,然后通过参数传递来获取不同颜色、不同大小、不同样式的块。至于层的类,就需要设置游戏的启动、判断、结束等等逻辑。
这里我用的是3.10的版本,所以不会使用2.x版本的编写方法。
Block类
Block.h
|
|
这里我没有使用CREATE_FUNC()这个宏,因为这个宏生成的方法没有参数,而创建Block对象时需要传入参数,所以我们得自己编写创建方法。winSize、vector、getBlocksVector()我就不多说了,这里讲一下这个CC_SYNTHESIZE():
|
|
这个宏主要是快速生成一个变量,并且实现对应的get()方法和set()方法。例如这里,我们定义了一个protected下的int类型变量_lineIndex,这个变量主要的作用是记录Block对象的行号。然后,我们可以使用setLineIndex()改变它的行号,还可以使用getLineIndex来获取它的行号。
至于moveDownAndCleanUp(),这个就是为了方块在下移出屏幕时,将方块从vector中移除,并且在渲染树中清除掉它。
Block.cpp
|
|
首先,初始化静态成员变量vector,create()方法和我们之前所讲的基本相同,改变的只有传入了四个参数,以及在成功分配内存后将Block对象加入vector中。
在init()中可以看到,我们对Block对象进行各种参数的设置。setContentSize()是设置容器大小;setTextureRect()其实就是控制显示的内容范围,使得这个对象成为一个矩形;颜色和锚点也需要我们设置一下。另外,这里我们创建了一个label,用于在方块中央显示文字。当然,如果你不需要显示就可以传入空的字符串。
getBlocksVector()就是返回vector,这里只说一说moveDownAndCleanUp()。我们在点击了黑块之后,所有的方块都会往下移动一行。所以,这里我们要编写一个移动动作,将方块向下移动1/4个屏幕的大小(也就是一行)。还有,当方块移出了屏幕时,我们就应该立马删除它。不过这里的顺序不能反,应该先从vector中删除,然后再从渲染树上移除。vector如果在后的话,删除时就会报错。
LayerGame类
LayerGame.h
|
|
showEnd和isRunning都是用于判断,之后会提到。startTime用于记录开始时间,_lineCount和_lineIndex差不多,不过这个是用于记录游戏总共进行了多少行。
LayerGame.cpp
|
|
看看init()方法,我们调用startGame()开始游戏,并将总行数_linCount初始化为0,label是用来显示时间的。接下来,我们创建一个监听器,并且将之绑定到层上(注意,这个绑定方法只能绑定到层上,不能指定对象)。
startGame()很简单,我们先调用addStartLineBlocks(),这个其实就是生成最下面的那个黄色长条,中间有一个Start Game。然后我们生成第二、三、四行。那么至此,游戏的初始化就完成了。
addStartLineBlocks()里面有一点要注意,我们必须给这个长条标上行号0,并且总行数加1。另外,我们每执行一次addNormalLineBlocks(),我们都要将总行数加1。我们每创建一个Block对象,都要给它标注行号,不然的话之后的下移和删除操作将没法进行。addEndLineBlocks()其实就是加上最后的绿色结束块。
onTouchBegan()就是触摸事件的回调函数,这里我们用for的范围循环来遍历vector中的每一个元素。boundingBox()表示对象的范围,containsPoint()用于检测坐标有没有在对象上。那么这里呢,我们可以使用getLocation()来获取鼠标点击的位置,从而可以检测到方块有没有被点中。当然,这里我们还要加一个条件,那就是方块必须处于第二行_lineIndex = 1,也就是我们只能在第二行点击方块。
随后,如果方块是黑色,我们调用moveDown()让方块下移,并把方块改成灰色。这里我们调用startTime()开始计时。如果是绿色,我们就调用stopTimer()停止即使,并且让绿色结束块下移一行。
moveDown()中,如果总行数小于10,那么我们就继续增加第四行。如果超过了10行,那么我们就应该出现结束块,这里showEnd是为了让结束块只出现一次。当然,这里最主要的是下移操作,我们这里使用的是反向遍历。为什么要使用反向遍历?让我们先看看moveDownAndCleanUp()。
之前已经说过了,moveDownAndCleanUp()先将方块从vector中删除,然后从渲染树中移除。那么,如果我们正向遍历,在vector删除一个元素之后,Cocos会将后面的元素往前移动进行填补。当迭代器移动到最后一个时,因为元素全都前移了,所以最后一个对象为空,就会发生错误。所以我们只能够反向遍历。
startTimer()开启了一个定时器,用于改变label中的内容,从而显示出游戏用时。isRunning用于确保startTimer()只被运行一次。stopTimer()用于关闭定时器,停止计时。至于时间是怎么获取的,我们可以先用startTime获取一次时间,然后不断地用clock()返回的时间减去startTime,这样就能够获得游戏用时。
总结
游戏看上去非常简单,但要实现却不是那么容易。无论是节点的删除顺序,还是遍历的方向,都有可能影响游戏的正常运行,总而言之,在制作游戏前一定要有很好的规划,不能够盲目地开始编写代码。