我如何干净,优雅地处理类之间的数据和依赖关系


12

我正在研究SFML 2中的2d自上而下的游戏,需要找到一种优雅的方式来使一切正常工作并融合在一起。

请允许我解释一下。我有许多类都继承自抽象基础,该基础为所有类提供了draw方法和update方法。

在游戏循环中,我调用update然后在每个类上绘画,我认为这是一种非常常见的方法。我有瓷砖,碰撞,播放器和包含所有瓷砖/图像/纹理的资源管理器的类。由于SFML中输入的工作方式,我决定让每个类在其更新调用中处理输入(如果需要)。

到目前为止,我一直根据需要传递依赖关系,例如,在按下播放键时在玩家类中,我在碰撞类上调用了一个方法来检查玩家想要移动到的位置是否是碰撞,并且只有在没有碰撞的情况下才移动播放器。

这在大多数情况下都可以正常工作,但是我相信可以做得更好,但我不确定如何做。

现在,我需要执行一些更复杂的事情,例如:玩家能够走到地面上的某个物体,按一下键来捡起/掠夺它,然后它就会显示在库存中。这意味着需要发生一些事情:

  • 在按键时检查播放器是否在可放物范围内,否则不要继续。
  • 查找项目。
  • 将项目上的精灵纹理从其默认纹理更新为“掠夺”纹理。
  • 更新项目的碰撞:它可能已更改形状或已完全移除。
  • 需要使用添加的项目更新库存。

我如何使一切交流?在我当前的系统中,我的类将超出范围,并且方法调用到处都是。我可以将所有类捆绑在一个大管理器中,并为每个类提供对父管理器类的引用,但这似乎只好一点。

任何帮助/建议将不胜感激!如果有什么不清楚的地方,我很乐于扩展。


1
您可能要在这里考虑组合,而不是继承。看看构成示例,它可能会给您一些想法。pimpl习惯用法也可能有助于整理内容。
OriginalDaemon

5
关于构图的经典文章之一:
演化

似乎太本地化了。尝试使用Code Review SE吗?
Anko 2012年

Answers:


5

不知道组合是否可以解决所有问题。也许可以部分帮助。但是,如果您想要解耦类,那么我将研究更多的事件驱动逻辑。这样,例如,您将具有OnLoot功能,该功能需要具有玩家位置和有关可用的掠夺物的信息才能找到最接近的物品。然后函数将事件发送到被掠夺的物品。在其事件处理周期中被掠夺的物品会处理此事件,因此物品只需要知道如何更新自身即可。OnLoot函数还可以更新播放器库存,或者项目本身可以发送updateInventory / * OnLootSucess *事件,播放器/库存将在其自己的处理事件周期中对其进行处理。

优点:您已经取消了某些课程的耦合

缺点:添加了事件类,可能不需要的代码开销。

这是外观的一种可能方式:

case LOOT_KEY:
   OnLoot(PLayer->getPos(), &inventoryItems);
....

// note onLoot do not needs to know anything about InvItem class (forward decl in enough)
int onLoot(vec3 pos, InvItems& pitems)
{
    InvItem* pitem = findInRange(pos, pitems, LOOT_RANGE);
    if(pitem)
     EventManager::Instance->post( Event::makeLootEvent(pitem));
}
....

// knows only about EventManager
InvItem::processEvents()
{
    while(!events.empty())
    {
        Event* pev = events.pop();
        ...
        case LOOT_EVENT:
            // in case you broadcasted it, but better is to sort all posted/sent events and add them only if they addressed to particular item 
            if(pev->item == this && handleLoot((LootEvent)pev))
            {
                EventManager::Instance->post(Event::makeLootSuccessEvent(this));
            }
    }
}

int handleLoot(LootEvent* plootev)
{
    InvItem* pi = plootev->item;
    if(pi->canLoot())
    {
        updateTexture(pi->icon, LOOTED_ICON_RES);
        return true;
    }
    return false; 
}


...
// knows only LootSuccessEvent and player
Inventory::processEvents()
{
    while(!events.empty())
    {
        Event* pev = events.pop();
        ...
        case LOOT_SUCCESS_EVENT:
             player->GetInventory()->add( ((LootSuccessEvent*)pev)->item );
        ...
}

这只是可能的方法之一。可能您不需要太多事件。而且我敢肯定,您可以更好地了解自己的数据,这只是众多方式中的一种。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.