二重调度(七):再谈 初始化模拟虚函数表

这就是关于二重调度的所有要说的,但是,用如此悲观的条款来结束是令人很不愉快

的。因此,让我们用概述初始化collisionMap的两种方法来结束。 


按目前情况来看,我们的设计完全是静态的。每次我们注册一个碰撞处理函数,我们
就不得不永远留着它。如果我们想在游戏运行过程中增加、删除或修改碰撞处理函数,将怎么样?不提供。 


但是是可以做到的。我们可以将映射表放入一个类,并由它提供动态修改映射关系的

成员函数。例如: 

class CollisionMap { 
public: 
  typedef void (*HitFunctionPtr)(GameObject&, GameObject&); 
  void addEntry(const string& type1, 
                const string& type2, 
                HitFunctionPtr collisionFunction, 

                bool symmetric = true);               // see below 


  void removeEntry(const string& type1, const string& type2); 


  HitFunctionPtr lookup(const string& type1, const string& type2); 


  // this function returns a reference to the one and only map

  static CollisionMap& theCollisionMap(); 


private: 
  // these functions are private to prevent the creation of multiple maps
  CollisionMap(); 
  CollisionMap(const CollisionMap&); 

}; 


这个类允许我们在映射表中进行增加和删除操作,以及根据类型名对查找相应的碰撞

处理函数。限制CollisionMap对象的个数为1,因为我们的系统中只有一个映射表。 (更复杂的游戏需要多张映射表是可以想象到的。 )最后,它允许我们简化在映射表中增加对称性的碰撞(也就是说,类型T1的对象撞击T2的对象和T2的对象撞击T1的对象,其效果是相同的。 )的过程,它自动增加对称的映射关系,只要addEntry被调用时可选参数symmetric 被设为true。 


借助于CollisionMap类,每个想增加映射关系的用户可以直接这么做: 
void shipAsteroid(GameObject& spaceShip, GameObject& asteroid); 

CollisionMap::theCollisionMap().addEntry("SpaceShip", "Asteroid", &shipAsteroid); 


void shipStation(GameObject& spaceShip, GameObject& spaceStation); 
CollisionMap::theCollisionMap().addEntry("SpaceShip", "SpaceStation", &shipStation; 


void asteroidStation(GameObject& asteroid, GameObject& spaceStation); 

CollisionMap::theCollisionMap().addEntry("Asteroid", "SpaceStation", &asteroidStation); 

... 


必须确保在发生碰撞前就将映射关系加入了映射表。一个方法是让GameObject的子类
在构造函数中进行确认。这将导致在运行期的一个小小的性能开销。另外一个方法是创建一个RegisterCollisionFunction 类: 
class RegisterCollisionFunction { 
public: 
  RegisterCollisionFunction( 
          const string& type1, 
          const string& type2, 
          CollisionMap::HitFunctionPtr collisionFunction, 
          bool symmetric = true) { 
    CollisionMap::theCollisionMap().addEntry(type1, type2, collisionFunction,                                                                                     symmetric); 
    } 

}; 


用户于是可以使用此类型的一个全局对象来自动地注册他们所需要的函数: 

RegisterCollisionFunction cf1("SpaceShip", "Asteroid", &shipAsteroid); 


RegisterCollisionFunction cf2("SpaceShip", "SpaceStation", &shipStation); 


RegisterCollisionFunction cf3("Asteroid", "SpaceStation", &asteroidStation); 
... 
int main(int argc, char * argv[]) 

  ... 


因为这些全局对象在main被调用前就构造了, 它们在构造函数中注册的函数也在main
被调用前就加入映射表了。如果以后增加了一个派生类

class Satellite: public GameObject

... 

}; 


以及一个或多个碰撞处理函数 
void satelliteShip(GameObject& satellite, GameObject& spaceShip); 

void satelliteAsteroid(GameObject& satellite, GameObject& asteroid); 


这些新函数可以用同样方法加入映射表而不需要修改现存代码: 
RegisterCollisionFunction cf4("Satellite", "SpaceShip", &satelliteShip); 

RegisterCollisionFunction cf5("Satellite", "Asteroid", &satelliteAsteroid);

 

这不会改变实现多重调度没有完美解决方法的事实。 但它使得容易提供数据给基于map的实现,如果我们认为这种实现最接近我们的需要的话。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值