游戏介绍:
顾名思义,俄罗斯方块自然是俄罗斯人发明的。这位伟人叫做阿列克谢·帕基特诺夫(Alexey Pazhitnov)。这款游戏操作简单,老少皆宜,也是一个不错的练手项目。
首先给几个经典的游戏界面先(当然,我们的目的是做出类似的效果)
游戏界面1:
游戏界面2:
游戏界面3:
游戏界面4:
游戏的基本操作很简单:
←:左移
→:右移
↑:旋转
↓:加速下降
当然,最好是可以自行设置快捷键,比方说用WSAD来代替。
游戏场景:
打开游戏,选择游戏难度,按开始键进行游戏。在游戏正上方会随机生成一个种类的方块,同时还会显示下一个方块的样子。用左右方向键控制方块的方向,用向上方向键旋转方块,用向下方向键加速下降。方块遇到墙壁或者撞到静止的方块不能穿透。底下方块如果填满一行,则进行消除。一次消除得越多,则得分越多。当方块到底后,会将之前已经预显示的方块放置到窗体正上方再下落。这样循环往复,直到静止的方块堆到顶,游戏结束。
思考:
首先,需要找出可以抽象出的类。从游戏的场景先找出一些对象:
对象:
行为:
俄罗斯方块的另一个名称是“砌墙”,我觉得这样更加形象生动。因此可以将上面的对象转换成:
哈哈,现在我们终于有了一些基本的类了:
现在我们就看看这些对象之间的关系。砖头和墙的唯一关系就是当砖头触底的时候,就会成为墙的一部分。因此Wall需要有一个接收砖头Brick的操作,我们称之为砌墙BuildWall(brick). 而Wall和Brick都是依附于房子House的,因此这两个类(Wall,Brick)是House的一个部分,并且墙的宽度不能超过房子的宽度,而砖头只能在房子里左、右、下,旋转的移动。
首先来看一下这些移动方法(MoveLeft,MoveRight,MoveDown,Rotate)应该加入到哪个类中。
最直接的是放到Brick类中,因为我们是移动砖头,看看有什么问题呢?对了,Brick在左移和右移时,需要知道是否碰壁了。因此,在这些方法中需要知道房子的宽度,同时要知道是不是到底碰到墙了。这样Wall要包含Brick,而Brick也要包含Wall和House。嘿嘿,似乎有点问题~~~~~~
直觉,放到Wall方法中是相当没有道理的,那就放到House中。由House来控制Brick的移动和旋转,似乎不错,比较完美了。
再接下想想,有好多种不同样子的砖头,除了样子不同以外,操作都是类似的。那Brick就变成抽象类了,其他种类的砖头从Brick中继承。
(写累了,先到这里...)
继续....
问题来了......砖头有很多种,每一种都可以旋转成其他样式的砖头(除了四四方方那个大方块怎么转都是一样外)。那具体的砖头类有多少种呢?
第一种做法:
把每次旋转后的不同样式的砖头当成另一个Class,这样的好处是,可以在Class中标记下次旋转后变成那种类型的Class。不足之处是需要扩展4倍的Class(汗~~~~~~)。爱偷懒的就用这种:)
第二种做法:
只生成基本的旋转类型,其他类型的位置通过旋转方法获得,这种方法的好处是Class会比较少,推荐这种做法。
接下来开始找行为了,之前已经确定了一些行为,如下 (可能还有些不合理,之后会再重构):
回到之前移动砖头的问题,砖头在左移和右移的过程中,可能会撞到墙壁,因此需要有是否撞墙的行为判断:IsAgainstWall。为了更好的表达出撞的是哪边的墙,我们再把这个行为拆分:IsAgainstLeftWall(), IsAgianstRightWall()。同时,在MoveDown的过程中,我们还需要检测是否碰到墙了。因此也要增加一个行为:IsAgainstDownWall()。到底后,如果某一行已经填满,还要有一个消去一行墙的行为:RemoveWall()。同时,到底后,原来的砖头就变成墙了,需要再生成新的砖头:CreateNewBrick()。
当然,如果越堆越高,到顶了,砌墙就结束了,所以还要有一个是否到顶的行为:IsAgainstHead().至于建筑工人,当然是有开始/结束砌墙的行为了。
好事情,行为(方法)越来越多了.....我们先将这些行为堆积到Class中(还是以自觉为主,随便放^_^).
这下子我们的类图又丰富了: