我正在为我正在制作的游戏设计一个新的加电系统。它是一个侧卷轴,电源UPS显示为圆形物体,玩家必须触摸/移动它们才能获得它们的力量。然后加电激活,几秒钟后自动停用。每次上电都有自己的持续时间。为简单起见,每隔X秒产生一次(放在屏幕上)电源UPS。
我创建了一个PowerUpManager,一个单身人士,他的工作是决定何时创建新的电源,然后放置它们。
然后,我创建了Powerup基类,以及从每个新Powerup继承该基类的类。每次启动都可以处于以下三种状态之一:已禁用,放置在屏幕上,并由播放器拾取。如果播放器没有接通电源但继续上电,则电源将退出屏幕并应从放置状态返回到禁用状态,因此可以再次放置。
我(I)的一个要求是,当我编写新的Power up类时,应该有最少的代码更改。我能做的最好的事情就是一段代码:PowerUpManager的构造函数,你必须将新的电源添加到容纳所有电源的容器中:
PowerupManager::PowerupManager()
{
available = {
new PowerupSpeed(),
new PowerupAltWeapon(),
...
};
}
PowerUpManager,更详细(问题即将来临!): 保存一个指向PowerUp(基类)的指针向量,称为可用。这是初始容器,在游戏中保存每个电源的一个副本。 为了处理不同的状态,它有几个列表:一个列表指向当前放置的电源UPS,另一个列表保存指向当前活动电源的指针。 它还有一种方法可以在每个游戏时间点调用,以确定是否以及在何处放置新的电源并清理未被拾取的电源UPS。最后,它有一个方法,当玩家进入加电状态时调用,激活加电(将其从放置到活动列表,并调用加电的激活方法)。
最后,一旦你理解了全貌,问题就是: 我需要一种方法让客户端代码询问特定的加电当前是否处于活动状态。例如:玩家拥有武器,但有一个能够暂时取代该武器的动力。在我对投入进行轮询并认识到玩家想要开火的时候,我需要调用正确的火力方法 - 替代武器强力射击方法,而不是常规武器射击方法。
我想了一会儿的这个特殊需求并想出了这个:
template <typename T>
T* isActivated() // Returns a pointer to the derived Powerup if it exists in the activated list, or nullptr if it doesn't
{
for(Powerup *i : active) // Active is a list of currently active power ups
{
T *result = dynamic_cast<T*>(i);
if(result)
return result;
}
return nullptr;
}
所以客户端代码如下所示:
PowerUpAltWeapon *weapon = powerUpManager->isActivated<PowerUpAltWeapon>();
if(weapon)
...
我认为解决方案优雅而且整洁,但实质上是尝试将基本类型转换为派生类型。如果这不起作用,你尝试下一个派生类型... if / else if的长链,它只是在循环中伪装。这是否违反了我刚才描述的指南?在if / else的长链中没有将基类型转换为所有派生类型,直到遇到命中为止?还有其他解决方案吗?
第二个问题是:有没有办法摆脱在PowerupManager构造函数中构建所有不同的电源UPS的需要?如果您想引入新的电源,那么这是目前唯一需要进行更改的地方。如果我可以摆脱它,那会很有趣......
答案 0 :(得分:1)
这是基于您的设计,但如果是我,我会为每个PowerUp和客户端中的一组ID选择一个ID,并且每次用户拥有一个PowerUp,该ID将被添加到其集合中。你知道其余的。使用这种技术,我可以快速查找每个PowerUp并避免dynamic_cast
:
std::set<PowerUp::ID> my_powerUps;
template< class T > bool isActivated() {
return my_powerUps.find( T::id() ) != my_powerUps.end();
}
关于你的第二个问题,我有一个类似的程序,加载一些插件而不是PowerUp
,我有一个纯虚拟基类,包含该插件所需的所有方法,并在共享模块中实现它,然后在启动时我从特定文件夹加载它们。例如,每个共享模块包含create_object
,返回plugin*
(当然,在您的情况下为PowerUp*
),然后我迭代文件夹,加载模块并调用create_object
来创建我的插件来自他们并在plugin_manager