我们把那个底盘称为Board,该Board类实一个容器,是一个6*6(n*n)的棋盘。对于该棋盘,我们抽象的看,那是不是一个表格呢?
所以我设想,这个Board类从我们的Table类继承,该Board类负责对底盘中的所有容器的控制。
Board类应该封装一个6*6(n*n)的棋盘的事实(细节),然后应该提供一个AddDevice(obj,x,y)的方法,x和y很显然,就是设备要放置的位置,这个obj就是允许放置的设备。
我们所需要的设备有
光线源头(line_head)
水晶(crystal)
传光器(line_transmission)
阻碍物(obstacle)
炸弹(bomb)
时间漏斗(time_funnel)
变色器(colour_changing)
选取框(choice_box)
如果,我建立一个Devicebase类作为这些设备的基类,估计也没有什么不妥,不过这些设备之间有的相同点实在是不多,因此要建立Devicebase类似乎意思不大。我又考虑这些设备应该有的共同点是:
Draw,在给定的x和y的范围内绘制自己的形状和外观
GetFocus,当得到焦点的时候,要在当前设备框的周围绘制出手柄标记
LostFocus,当失去焦点的时候,要在当前设备框的周围撤销手柄标记
所以我打算声明一个接口IDevice,该接口声明了以上的方法。
因此Board类的AddDevice为:IDevice,x,y这样会比较好点,感觉。
然后,我要添加一个新的设备:空白(blank),6*6的表格默认存放的都是空白,该blank当然也实现IDevice接口。
接下来,我们要考虑光,光是不是对象呢?那一定是的。我们需要一个Light类。从游戏的直观角度来讲,line_head需要一个Send方法,其他的设备需要一个Receive方法。
具有Receive方法的设备体现多态,每个设备有自己对光的处理模式:延伸,阻隔和爆炸等。延伸其实就是继续对光线的绘制。所以,我们在设计的时候,如果考虑对光的绘制是由设备来控制的,我感觉就错了。
我的设计是:光由自己绘制,但绘制的控制权由Board负责,设备向Board描述光的方向性。
比如:
line_head向Board发出消息要求从x,y开始沿什么方向绘制什么颜色Light
Board沿方向计算Table中沿途的第一个设备,然后将控制权给Light绘制。
Light绘制完毕后,Board通知第一个遇到的设备接受光线
该设备依据自身的规则计算光线的延伸,并发消息给Board,Board再计算。
如果Board计算出的第一个设备是bomb,则游戏结束
有些设备允许旋转和移动
因此我们要再声明
IRotate接口
ToClockwise 顺时针
ToCounterclockwise 逆时针
IMove接口
ToUp
ToDown
ToLeft
ToRight
我们希望当设备得到焦点的时候,绘制出的句柄可以有明显的提示让用户该设备是不是可以旋转或移动,或都不行。
(所有用具的移动和旋转都需要靠选取框来完成~~选取框移动到没有用具的格子或该用具不可移动和旋转的时候~选取框表示成状态1~~当移动到可移动和旋转的用具时候~选取框表示成状态2~~玩家第一次确认后,就可移动该用具,选取框表示成状态3~玩家第二次确认后,就可旋转该用具,选取框表示成状态4~~玩家第三次确认后,完成对该用具的操作,选取框表示成状态2~~当选取框处于状态1和状态2的时候~可以上下左右的移动~来捕捉用具~~在状态3下,移动用具即移动选取框,选取框的状态应同时带进新的Cell中~~)
选中了以后~先是移动~再是旋转~~
我考虑底盘的每个单元格都应该是一个对象,我们把它声明为Cell对象,该对象是一个容器对象,当Board初始化的时候,每一个Cell都存放的是Blank对象,当Board使用AddDevice方法放入设备时,其实是将设备放入Cell中。
为什么我要这样做呢?我这样考虑的,依据MVC,视图(玩家直接观察的部分)的变化其实是对数据(Board)的操作呈现。Board提供所有对游戏行为的支持,只不过这些行为分为两种
一种是:Board的直接行为,比如提供用户对Cell选择确定等控制
一种是:对设备的控制,这些行为其实是包装了设备的行为。
所以,底盘(Board)需要一个CurrentCell属性,描述当前具有焦点的单元格。底盘实现上下左右的方法(IMove接口),这些方法影响CurrentCell属性。CurrentCell总是返回当前Board有焦点的那个Cell。
当Board执行IMove接口的方法时,会激发单元格焦点改变的事件,该事件会通知单元格中的设备改变绘制手柄(就是那个选取框)。得到焦点的时候绘制手柄,失去焦点的时候消除手柄。
绘制选取框是一个很值得考虑的问题,从效果来说,我们希望每个设备的选取框都不一样,感觉好看点,还有设备的可旋转和可移动也希望选取框有不同的绘制效果,但是也有可能大部分设备的选取框绘制效果一样,如果你认为要写一个DeviceBase来实现选取框的绘制也可以,不过我提供另外的思路。
Cell来实现默认的焦点得失时的选取框的绘制。
首先设备要实现Can系列属性,向它所在的单元格描述当前设备是否可以旋转或者移动等。这个我们可以在IDevice接口中声明CanRotate,CanMove,当Cell得到焦点的时候,它根据它所容纳的设备的Can系列来绘制选取框。如果设备提供了自己的选取框,那Cell将用设备的绘制方法替换Cell的默认,这种方式是我从ASP.Net学来的。这种设计模式,要比继承更灵活有意思。