《魔塔天城》发布已经有半年的时间了,一直想找时间来总结一下这个项目,但总是一拖再拖。如果再这么拖下去,就永远都不会有时间来写这个总结了,时间总是挤出来的。

魔塔天城使用的cocos2dx 3.2版本,当时这个版本刚刚发布。网上的学习资料都是2.0版本的。只有官网提供的示例demo是最新的,好在这些demo涵盖的面比较宽,对于学习API来说已经足够了。

魔塔的程序框架并不复杂

总的来说就是这几大块,对象继承结构,碰撞检测,node和layer层次结构,UI实现,剧情脚本

在魔塔世界中 World是由Cell构成的,所有的事物都是Cell。我们按照MVC思想来分析一下Cell

model:Cell的属性集合就是我们所说的MVC模式中model,我们从外部读取数据表格文件中的数据(这里我使用了csv文件),然后初始化这些Cell的model(注意:数据表格中的数据通常记录的是类的基本属性),在游戏中不同的对象model会产生不同的变化,如果我们想要保存这些变化,只要保存这些容易发生变化的属性就行了,这里假设保存在model_New里。然后下次再加载存档的时候,首先利用数据表格初始化model,然后再加载保存的model_New覆盖合并当前model,这就是基本的存档功能。

所以我的Cell必须实现这些初始化,加载并保存model的功能,这里我们就可以添加这些函数

virtual void initModel() = 0;
virtual void loadModel(ValueMap &map) = 0;
virtual ValueMap saveModel() = 0;

这里我们使用cocos2dx 3.2 提供的 ValueMap数据结构实现我们的model。

view:这里的View应该不单单是一张张图片,因为我们还需要动画。所以cocos2dx里面的Sprite显示精灵是最佳选择。

controller:事物之间是需要交互的,这样我们的世界才如此生机盎然。在程序里,事物就是对象,那么如何让对象之间交互呢,没错,利用事件Event。cocos2dx已经为我们实现好了这些事件机制,我们唯一需要做就是为每个对象添加事件侦听或回调函数,一个发送事件,一个接受事件并作出反映,这就实现了交互。当然,我们也可以通过发送事件来操纵对象,就像游戏中的控制台一样,让对象接收命令。

事件监听

_listener = EventListenerCustom::create(getName(), [=](EventCustom *event){onTrigger(event);});
_eventDispatcher->addEventListenerWithFixedPriority(_listener, 1);

 

回调函数

virtual bool onTrigger(EventCustom *event) = 0;

事件机制就是观察者模式的衍生,在cocod2dx中我们的事件都由EventDispatcher的一个实例来管理,这就像是接线员一样。会根据号码(事件id)来传递事件。这个事件id我们用对象的名字来表示,因此,这就变得非常好理解了,对象之间通过名字来交互也更现实些。

Cell是万物的基类,由此我们可以派生出Item、Actor等一系列子类。万物是由上帝创造的,在这个程序架构的世界里,我们就是上帝。但是要让我们一个一个new对象却是很累,所以工厂模式是派上用场了。CellFactory,根据我们的订单创建Cell,它有可能是一个道具,也有可能是个NPC。总之把你的需求打包成一个map数据结构传递给工厂就行了。

创建完成了Cell,但我们还要把它添加到世界里。

World是个魔塔的世界,这个世界除了塔没别的。所以World里包含Tower的数组

Tower魔塔是由一层一层的Floor构成的,所以Tower里面有Floor的数组。

我们是世界的二维的,每次只能显示一层Floor。所以Floor就是游戏地图的基本单位。我们创建的Cell都要添加到Floor里面,Floor会包含很多元素(地面、墙壁、怪物、NPC、道具等等),也就是说这些元素都要派生自Cell。

Cell也是碰撞检测的基本单位,魔塔中的碰撞检测是基于方格碰撞的。Cell的有效碰撞区域都是一个个相同大小的方块。碰撞检测说到底就是通过数学计算判断两个形体是否相交。为了简化计算,我们通常把形体简化成矩形或长方体。如果是多个物体的话,我们往往会让每个物体都跟其他物体作比较,如果物体很多的话,这会非常耗时。所以我们需要方法简化比较次数,比如有些物体之间相距很远,是绝对不可能会碰到的,通过划分区域可以很好的解决这个问题。区域的划分也是十分讲究的,比如四叉树算法、八叉树算法。这里不做具体分析,有兴趣的同学可以自行百度。在魔塔中,我们就不用使用这些复杂的算法了,因为我们的地图通常并不大,但是区域划分是不能忽略的,我们可以将地图划分为网格。网格中的每一个小方格都是一个区域,而且大小与Cell的有效碰撞区域相同。