本文仅供个人记录和复习,不用于其他用途
游戏效果展示
简单的说一下,我们必须点击黑块,才能够往下走一步,点击白块是没有效果的。当点击到绿块时,计时就会停止,游戏就结束了。当然,我们只能够点击第二行(最下面的是第一行)。
具体思路
我们需要两个类,一个用于生成游戏的层,一个用于生成块。生成块的类需要设置块的大小、位置、颜色。那么为了方便起见,我们就设立好最基本的创建方法,然后通过参数传递来获取不同颜色、不同大小、不同样式的块。至于层的类,就需要设置游戏的启动、判断、结束等等逻辑。
这里我用的是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
,这样就能够获得游戏用时。
总结
游戏看上去非常简单,但要实现却不是那么容易。无论是节点的删除顺序,还是遍历的方向,都有可能影响游戏的正常运行,总而言之,在制作游戏前一定要有很好的规划,不能够盲目地开始编写代码。