Как правильнее создавать игровой цикл?

Здравствуйте, уважаемые :)
Пытаюсь написать игровой движок для стратежки типа Dune или RedAlert. Просто для себя, покоряю, так сказать, вершины геймдева ))
Свой игровой цикл я делаю в классе мира и все операции над объектами-моделями провожу в нем же. В этом классе за все это отвечает функция tick() и выглядит примерно так:
void tick() 
{
  foreach ( body1 in mapBodies ) 
  {
    foreach ( body2 in mapBodies )
    {
       /*
         и тут я проверяю всевозможные взаимодействия этих двух
         объектов друг с другом. Если это танк, пушка, солдат и т.п., то 
         они могут кого-то видеть, атаковать или ехать по приказу и т.д.,
         если здание - что-то производить...

         В общем эта функция становится всё "жирнее и жирнее" с 
         добавлением нового функционала...
       */
    }
  }
}


но сегодня в одном из исходников я наткнулся на такую реализацию этой функции:
void tick()
{
  foreach ( body in mapBodies )
  {
    body->tick();
  }
}

И все. В этой функции цикл просто перебирает все объекты и вызывает "тик". Так вот мой вопрос: какая модель "тика" проще/правильнее ?
И как во втором случае объекты должны взаимодействовать друг с другом? Ведь по логике получается, что tick(), вызываясь в каждом объекте, порождает цикл проверки всех объектов на возможность взаимодействия с текущим объектом? А если объектов тысячи? За один игровой "тик" должно родится и уничтожится тысяча циклов? А комп не помрет? Или я что-то неправильно понимаю? :)
  • Вопрос задан
  • 2703 просмотра
Пригласить эксперта
Ответы на вопрос 4
@suslik2015
В вашей "архитектуре" "класс мира" осуществляет операции со всеми игровыми объектами. НАлицо нарушение принципов низкой связности и высокого сцепления. Функционально игровой цикл должен пнуть объекты игры или их менеджеры о том, что пора обновить свое состояние, максимум расчитав при этом дельту. Он не должен знать о графике, звуке, физике и т.п. Решение - делегировать аспекты поведения отдельным объектам Можете прочитать здесь про систему компонентных сущностей в играх.
Ответ написан
Deerenaros
@Deerenaros
Программист, математик, задрот и даже чуть инженер
Вы всё правильно понимаете. Только от того, что Вы вынесите тик в отдельный метод ничего толком не измениться в плане производительности. Ну а по поводу количества объектов - чем их больше, тем выше требования к производительности. В средней стратегии с ИИ я где-то вычитал, что проблемы начинаются свыше 1000 объектов, но они в основном решаемые, тогда как где-то с 10000000 (десять миллионов) - нерешаемые. При этом имеются ввиду полноценные объекты, для которых надо искать путь и считать статистику.
Ответ написан
gbg
@gbg
Любые ответы на любые вопросы
Кстати, проверок можно делать в половину меньше - у вас тут A взаимодействует c B, а потом B взаимодействует с A. А нужна только проверка "A и B повзаимодействовали."

Вы не рассмотрели третий вариант - наличие класса "Взаимодействие A и B", от которого наследуется куча классиков с методами, реализующих взаимодействия разных пар объектов. Это наиболее гибкий вариант, да еще и экономящий время:
Без этого вам придется написать два метода:
в классе A - описание взаимодействия с классом B,
в классе B - описание взаимодействия с классом A.

Двигаясь дальше, можно заранее составить список всех возможных пар взаимодействий (с заранее развернутыми типами, чтобы каждый раз A не выясняло, с каким классом мы считаем взаимодействие сейчас), который будет обходится одним циклом.
Ответ написан
tsarevfs
@tsarevfs
C++ developer
Вызов tick() /*мне привычнее называть ее update()*/ для каждого объекта предпочтительней. За счет полиморфизма для каждого типа объекта может быть вызвана своя реализация этой функции. В зависимости от объекта, возможны различные варианты взаимодействий. Например куст может ничего не делать на своем update. А солдат должен посмотреть на всех врагов и выбрать в которого стрелять. Такое поведение не очевидно как реализовывать вашим способом. Ну и отпадает необходимость писать гигантские функции.
Если юнитов становится слишком много, возможны различные оптимизации. Например обсчет только юнитов в определенной зоне или объединение стаи птиц в один объект с упрощенным поведением, вместо рачсета каждой птицы. Но об этом стоит думать, когда оптимизация становится необходимой.
Еще подозрительна фраза про родящиеся и уничтожающиеся циклы. Это прероготива объектов. Циклы это просто часть кода, которая исполняется практически как набор последовательных инструкций.
Ответ написан
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы