(声明:以下面向对象思路是基于C++和2D图形的)
在代码量较小的程序中,我们可能会直接把所有需要的对象的碰撞检测封装在类当中,然后在
角色移动等动作的渲染过程中进行自动的碰撞检测,但这样的做法有个弊端,那就是我们已知了需求,
而我们如果需要扩展程序,则需要不断的在类内部进行添加,这样的做法显然破坏了程序的扩展性,
使得代码难以使用,因此我们需要该角色的类对外提供一个接口,利用这个接口,将那些需要进行
碰撞检测的对象进行检测,然后进行角色的移动等动作的渲染。
但是这里有一个问题,因为碰撞检测的对象并不是唯一的,特别是自定义的怪物类对象,地图对象等,
更是不尽相同的,如果我们为每个对象都提供一个多态版本的对外碰撞检测接口,那么随着需要碰撞检测
类型对象的增多,这些多态函数的数目也会增多,这样一来会使程序更加的累赘,二来会不断的修改角色
类中碰撞检测的内容,使得角色类的通用型,可扩展性和简洁性降低,这样的做法并不比原来好上多少,
因此我们需要换个思路。
一个更有效的做法是在类内部存储一种作为容器的数据结构,然后在该类的public当中提供获得该数据
容器对象的接口,然后在外部环境中,将需要碰撞检测的对象不断插入到该容器数据结构中。而在该角色
类内部,则通过碰撞检测函数不断遍历该容器中的元素,从而实现碰撞检测的功能,这样一来我们就能将
角色类从纷繁复杂中分离出来,从而实现更加模块化的功能。
但是这里依旧有一个问题没有解决,那就是容器中需要存储不同类型的数据结构对象来提供碰撞检测。
对于此,我们可以这样来处理,我们并不存储太过具体的对象,如怪物类,地图类等。我们只存储一些碰撞
检测中用到的比较本源的东西,例如矩形,圆形,三角形对象等,而在那些比较具体的对象中统一提供这些
碰撞检测区域的接口。利用这种方法,我们可以把碰撞检测的类型减缩到个位数,使原来复杂的问题变的
简单化了。但仅仅是这样还是不够的,如果我们分别用矩形,圆形,三角形的容器存储,那么我们就需要
提供三个对外的接口来分别接收不同类型的对象,这样的做法似乎可行,但我依然认为这不够友好和简洁,
我希望用一个容器来存储这些不同类型的对象。
一开始我想自己写这么一个数据结构的容器来存储,但是基于每个对象所占的内存是不同的,我们如何来
让该容器来进行寻址定位呢?保存数据结构的大小可能是一个好的方法,但我想更好的方法是只在该容器中
保存各种对象指针,因为指针的大小只是由操作系统的寻址能力和编译器来决定的,这意味着在同一台机器
上在一个编译平台下各种数据类型的指针大小都是相同的,因此如果我们使用指针则可以使容器很快的寻址
到我们需要的元素。
因为想到了指针,于是我就马上联想到了void*类型,如果我们使用一个void*类型的容器,不就意味着我们
可以用该容器来存储所有数据类型的指针了吗?这似乎是一个很好的思路,但是我们在该角色类内部如何将其
转化为原来具体的类型以供调用呢?这就需要我们的容器在插入的时候同时额外提供一个表示该数据类型的索
引,这样我们就能通过该索引来判断出我们所碰撞检测的对象是什么了!而且还不需要我自己来实现,在STL
当中便提供了这样的容器!!!
具体方法我们可以这样做:
1.首先我们定义一种枚举类型来提供不同数据类型的索引号。
enum CollType { COLL_TYPE_RECT = 0, COLL_TYPE_CIRCLE = 1, COLL_TYPE_TRIANGLE = 2 };
2.然后在角色类中声明一种将CollType和void*构成pair用来作为双向链表list的元素,例如:
std::list<std::pair<CollType,void*>> mCollList;
这样一来,我们就能用mCollList来存储不同碰撞区域类型的对象来在类内部进行碰撞检测了,这里之所以选用
std::list双向链表而不用线性表的原因有两点:
#1.插入和删除的速度非常快,因为我们可能在之后的一段时间内对某个对象不进行碰撞检测,因此会有频繁的删除。
#1.在碰撞检测时,在算法没有优化的情况下,通常我们是遍历容器每一个元素来进行碰撞检测,这样一来,借助list
的迭代器,我们遍历该容器的时间和线性表相当。
(这里暂时不考虑借用树结构对区域分割来达到算法优化,这一部分以后再考虑实现。)
3.在角色类中提供一个对外的接口以供调用:
std::list<std::pair<CollType, void*>>& getCollList(){ return mCollList; }
4.在角色类内部遍历mCollList的元素来进行碰撞检测。
5.我们在类外环境中通过getCollList()不断的添加需要进行碰撞检测的区域即可。
(如果我的思路中有什么不对地方你可以指出,或者你有更好的方法可以和我一起交流,
让我们一起加油,向着世界不断的进步吧!!)