Як я можу чітко та елегантно обробляти дані та залежності між класами


12

Я працюю над грі 2d вгору в SFML 2, і мені потрібно знайти елегантний спосіб, коли все буде працювати і поєднуватися разом.

Дозвольте мені пояснити. У мене є ряд класів, які успадковують абстрактну базу, яка забезпечує метод малювання та метод оновлення для всіх класів.

У циклі ігор я закликаю оновлення, а потім малюю на кожному класі, я думаю, що це досить поширений підхід. У мене є класи з плитки, зіткнення, програвач та менеджер ресурсів, який містить усі плитки / зображення / текстури. Зважаючи на те, як працює вхід у SFML, я вирішив включити вхід для кожного класу (якщо потрібно) у своєму виклику оновлення.

До цих пір я проходив залежно від необхідності, наприклад, у класі гравців, коли натискається клавіша руху, я називаю метод класу зіткнення, щоб перевірити, чи буде позиція, на яку хоче перейти гравець, зіткнення, і рухати програвач лише у випадку зіткнення.

Здебільшого це чудово працює, але я вважаю, що це можна зробити і краще, я просто не знаю як.

Зараз у мене є більш складні речі, які мені потрібно здійснити, наприклад: гравець може підійти до об'єкта на землі, натисніть клавішу, щоб забрати / розблокувати, і він відобразиться в інвентарі. Це означає, що потрібно зробити кілька речей:

  • Перевірте, чи програвач знаходиться в діапазоні певного предмета на натисканні клавіші, інакше не продовжуйте.
  • Знайдіть предмет.
  • Оновіть текстуру спрайту на елементі від його тексту за замовчуванням до "розграбованої" текстури.
  • Оновіть зіткнення предмета: воно могло змінити форму або було видалено повністю.
  • Інвентаризацію потрібно оновити доданим елементом.

Як змусити все спілкуватися? З моєю нинішньою системою я закінчую, щоб мої заняття виходили за межі сфери, а методи викликали один одного в усьому місці. Я міг би зв'язати всі класи в одному великому менеджері і дати кожному посилання на батьківський клас менеджера, але це здається лише трохи кращим.

Будь-яка допомога / порада буде дуже вдячна! Якщо щось незрозуміле, я із задоволенням розширюю питання.


1
Ви можете розглянути тут склад, а не спадщину. Огляньте приклади композиції, і це може дати вам кілька ідей. Ідіома pimpl також може допомогти впорядкувати речі.
OriginalDaemon

5
Одна з статей канону про композицію: Еволюціонуй свою Ієрархію
doppelgreener

Здається, занадто локалізовано. Спробуйте перегляд коду SE?
Анко

Відповіді:


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 );
        ...
}

Це лише один із можливих способів. Напевно, вам не потрібно стільки подій. І я впевнений, що ви можете краще знати свої дані, це лише один із багатьох способів.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.