假设要做一个太空中各种天体的碰撞,其中涉及宇宙飞船、太空战、小行星等天体。
定义如下:
class GameObject
{
public:
virtual void collideResult()=0;
virtual ~GameObject()=0;
};
GameObject::~GameObject() { }
class SpaceShip:public GameObject
{
public:
void collideResult()
{
cout<<"SpaceShip::collideResult()"<<endl;
}
};
class SpaceStation:public GameObject
{
public:
void collideResult()
{
cout<<"SpaceStation::collideResult()"<<endl;
}
};
class Asteroid:public GameObject
{
public:
void collideResult()
{
cout<<"Asteroid::collideResult()"<<endl;
}
};
collideResult是各种不同的天体碰撞后各自产生的后果。
采取的方法是:使用“非成员函数”的碰撞处理函数
void ProcessCollision(GameObject* object1,GameObject* object2)
{
string type1=typeid(*object1).name();
string type2=typeid(*object2).name();
CollisionMap::HitFunctionPtr ptr=CollisionMap::theCollisionMap()->lookup(type1,type2);
if(ptr!=NULL)
(*ptr)(*object1,*object2);
}
上函数中的CollisionMap就是存放游戏对象和相应的碰撞处理函数的map结构,仿真的是虚函数表。
class CollisionMap
{
public:
typedef void (*HitFunctionPtr)(GameObject&,GameObject&);
typedef map<pair<string,string>,HitFunctionPtr> HitMap;
void addEntry(const string& type1,const string& type2,HitFunctionPtr collisionFunction,bool sysmetric=true);
void removeEntry(const string& type1,const string& type2);
HitFunctionPtr lookup(const string& type1,const string& type2);
static CollisionMap* theCollisionMap();
private:
CollisionMap() {}
CollisionMap(const CollisionMap&);
static HitMap collisionMap;
pair<string,string> makeStringPair(const string& str1,const string& str2);
};
CollisionMap::HitMap CollisionMap::collisionMap;
CollisionMap* CollisionMap::theCollisionMap()
{
static CollisionMap cm;
return &cm;
}
pair<string,string> CollisionMap::makeStringPair(const string& str1,const string& str2)
{
return make_pair(str1,str2);
}
void CollisionMap::addEntry(const string& type1,const string& type2,HitFunctionPtr collisionFunction,bool sysmetric)
{
pair<string,string> hitObjects=makeStringPair(type1,type2);
collisionMap.insert(pair<pair<string,string>,HitFunctionPtr>(hitObjects,collisionFunction));
if(sysmetric)
{
pair<string,string> hitObjects=makeStringPair(type2,type1);
collisionMap.insert(pair<pair<string,string>,HitFunctionPtr>(hitObjects,collisionFunction));
}
}
void CollisionMap::removeEntry(const string& type1,const string& type2)
{
pair<string,string> hitObjects=makeStringPair(type1,type2);
HitMap::iterator iter=collisionMap.find(hitObjects);
if(iter!=collisionMap.end())
collisionMap.erase(iter);
}
CollisionMap::HitFunctionPtr CollisionMap::lookup(const string& type1,const string& type2)
{
pair<string,string> hitObjects=makeStringPair(type1,type2);
HitMap::iterator iter=collisionMap.find(hitObjects);
if(iter!=collisionMap.end())
return (*iter).second;
else
return NULL;
}
因为一个系统中,只有一个这样的对象函数表,采用的是单实例的设计模式。
为了防止在这个函数表使用之前一定得到初始化,采用如下的措施:
class RegisterCollisionFunction
{
public:
RegisterCollisionFunction(const string& type1,const string& type2,CollisionMap::HitFunctionPtr collisionFunction,bool sysmetric=true)
{
CollisionMap::theCollisionMap()->addEntry(type1,type2,collisionFunction,sysmetric);
}
};
void shipAsteroid(GameObject& spaceShip,GameObject& asteroid)
{
spaceShip.collideResult();
asteroid.collideResult();
cout<<"SpaceShip collide with Asteroid"<<endl;
}
void shipStation(GameObject& spaceShip,GameObject& station)
{
spaceShip.collideResult();
station.collideResult();
cout<<"Spaceship collide with SpaceStation"<<endl;
}
void asteroidStation(GameObject& asteroid,GameObject& station)
{
asteroid.collideResult();
station.collideResult();
cout<<"Asteroid collide with SpaceStation"<<endl;
}
void shipShip(GameObject& spaceShip1,GameObject& spaceShip2)
{
spaceShip1.collideResult();
spaceShip2.collideResult();
cout<<"SpaceShip collide with Space Ship"<<endl;
}
void asteroidAsteroid(GameObject& asteroid1,GameObject& asteroid2)
{
asteroid1.collideResult();
asteroid2.collideResult();
cout<<"Asteroid collide with Asteroid"<<endl;
}
void stationStation(GameObject& station1,GameObject& station2)
{
station1.collideResult();
station2.collideResult();
cout<<"SpaceStation collide with Collide"<<endl;
}
RegisterCollisionFunction cf1("class SpaceShip","class Asteroid",&shipAsteroid);
RegisterCollisionFunction cf2("class SpaceShip","class SpaceStation",&shipStation);
RegisterCollisionFunction cf4("class SpaceShip","class SpaceShip",&shipShip);
RegisterCollisionFunction cf3("class Asteroid","class SpaceStation",&asteroidStation);
RegisterCollisionFunction cf5("class Asteroid","class Asteroid",&asteroidAsteroid);
RegisterCollisionFunction cf6("class SpaceStation","class SpaceStation",&stationStation);
因为这些碰撞函数都是全局变量,一定会在进入main函数之前初始化。
测试程序如下:
int _tmain(int argc, _TCHAR* argv[])
{
SpaceShip object1;
SpaceStation object2;
ProcessCollision(&object1,&object2);
return 0;
}
在处理函数中,首先调用第一个对象的collisionResult,然后调用第二个对象的collisionResult,然后提示哪两个对象发生了碰撞。
至此,采用这样的技术实现了让ProcessCollision函数根据两个对象的动态类型实现虚化。
根据多个参数实现虚化的技术类似。