我正在为自己创建的游戏设计新的开机系统。这是一个侧面滚动器,电源上升器显示为圆形物体,玩家必须触摸/移动它们才能获得电源。上电然后被激活,并在几秒钟后停用自身。每次加电都有自己定义的持续时间。为简单起见,每X秒生成一次电源提升(放置在屏幕上)。

我创建了一个PowerUpManager,这是一个单例,其任务是确定何时创建新的功率提升器,然后在何处放置它们。

然后,我创建了Powerup基类,并且为每个新的Powerup创建了一个从该基类继承的类。每次加电都可以处于以下三种状态之一:禁用,置于屏幕上并由播放器拾取。如果播放器没有拿起电源但继续前进,则加电将退出屏幕,并且应从放置状态返回到禁用状态,因此可以再次放置。

(我)提出的一项要求是,当我编写新的Power up类时,应该对代码进行的更改最少。我能做的最好的事情就是编写一段代码:PowerUpManager的构造函数,在该构造函数中,您必须将新的power-up添加到容纳所有power-up的容器中:

PowerupManager::PowerupManager()
{
    available = {
        new PowerupSpeed(),
        new PowerupAltWeapon(),
        ...
    };
}

有关更多详细信息,PowerUpManager(即将出现问题!):
包含指向PowerUp(基类)的指针的 vector ,称为可用。这是初始容器,其中包含游戏中每次加电的副本。
为了处理不同的状态,它具有两个列表:一个列表包含指向当前放置的电源启动的指针,另一个列表包含指向当前 Activity 的电源启动的指针。
它还有一种方法,该方法会在每个游戏滴答声中被调用,该方法决定是否以及在何处放置新的电源并清理未拾取的电源。最后,它有一个方法,当播放器启动电源时会被调用,该方法将激活电源(将其从放置的列表移动到 Activity 列表中,并调用电源的activate方法)。

最后,一旦您了解了整体情况,就可以提出以下问题:
我需要一种让客户端代码询问当前是否正在启动特定加电的方法。例如:玩家拥有武器,但是有一个加电可以暂时替换该武器。当我轮询输入并识别出玩家想要发射他的武器时,我需要调用正确的发射方法-替代武器加电发射方法,而不是常规武器发射方法。

我考虑了一段时间的这种特殊需求,并提出了以下建议:
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构造函数中构造所有不同的power ups的需要?如果要引入新的电源,这是当前唯一需要进行更改的地方。如果我能摆脱它,那将很有趣...

最佳答案

这是基于您的设计的,但是如果是我,则为每个PowerUp选择一个ID,并在客户端中选择一组ID,并且每当用户提出PowerUp时,该ID就会添加到其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从中创建我的插件并将其注册到我的ojit_code中

关于c++ - 我在这里违反OOP设计指南吗?几个有趣的设计泡菜,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/12881156/

10-11 01:03