2

我正在为我正在创建的游戏设计一个新的加电系统。这是一个横向卷轴,电源提升显示为圆形物体,玩家必须触摸/移动它们才能获得它们的力量。然后启动电源,并在几秒钟后自行关闭。每次上电都有自己定义的持续时间。为简单起见,每 X 秒会产生一次(放置在屏幕上)电源。

我创建了一个 PowerUpManager,它的工作是决定何时创建新的电源,然后将它们放置在哪里。

然后,我创建了 Powerup 基类,并为每个新的 Powerup 创建了一个从该基类继承的类。每个 Power-up 都可以处于以下三种状态之一:禁用、放置在屏幕上和被玩家拾取。如果玩家没有拿起电源而是继续移动,电源会退出屏幕,应该从放置状态返回到禁用状态,这样才能再次放置。

(我)提出的要求之一是,当我编写一个新的 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 if 的长链中没有将基类型转换为所有派生类型,直到你受到打击?还有其他解决方案吗?

第二个问题是:有没有办法摆脱在 PowerupManager 构造函数中构建所有不同电源的需要?如果您想引入新的电源,这是目前唯一需要进行更改的地方。如果我能摆脱它,那会很有趣......

4

1 回答 1

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

于 2012-10-14T10:44:45.660 回答