Вторник, 26 Ноября 2024, 00:47

Приветствую Вас Гость

[ Новые сообщения · Игроделы · Правила · Поиск ]
  • Страница 1 из 1
  • 1
Логика RTS (Стратегий) игр
SlamerДата: Вторник, 23 Октября 2012, 02:24 | Сообщение # 1
был не раз
Сейчас нет на сайте
Привет всем игроделам! Я тут новенький, долго не решался зарегестрироваться, но ситуация вынудила обратиться за помощью.
Примерно месяца два назад открыл для себя 2D движок - HGE. Решил возродить почти забытый жанр - RTS и попробовать сделать игру в этом стиле на С++. Сначало вроде бы всё шло отлично, но недавно столкнулся с проблемой: при помещении на карту примерно около 100 боевых юнитов, игра начинает тормозить, и чем больше появляется юнитов, тем соответственно болие дикие тормоза. Вроде бы, проблема мне ясна - в игре идёт проверка каждого юнита на нахождение рядом с ним врагов или препятствий примерно таким образом:
Code

      for (int i=0;i<tankV1x1.size();i++)
      {
                // Проверка на препятсвия для движения
       // С союзными танками
              for(int st=0; st<tankV1x1.size(); st++)
              { ... }
                     // С декорациями
              for(int st=0; st<dec1Limit; st++)
              { ... }
       // С вражескими танками
       for(int st=0; st<tankEnV1x1.size(); st++)
       { ... }
             }

То есть, идёт перебор, для каждого союзного танка идёт проверка каждой декорации и каждого врага, такая же проверка идёт и для врагов. На форуме HGE мне советовали разбить карту на квадраты, в эти квадраты помещать объекты и делать проверку только внутри квадрата. Идея не плохая, но при попытки реализовать её, пришёл к тому, что если, например я разделю карту на 100 квадратов, то для проверки лишь одного вида юнита (например танка) мне придётся создать 100 хранилищ для союзных танков, 100 хранилищ для вражеских танков, и ещё по 300 хранилищ для спрайтов танка. Итого 800 динамических массивов только на одни лишь танки.

Очевидно что такой метод не годиться, так вот вопрос: каким образом можно реализовать проверку для большого количества объектов и при этом иметь нормальную производительность?
И вот, выкладываю то, что имеется на данный момент, может кому интересно: Видео игры


Сообщение отредактировал Slamer - Вторник, 23 Октября 2012, 02:31
НохчиДата: Вторник, 23 Октября 2012, 02:43 | Сообщение # 2
заслуженный участник
Сейчас нет на сайте
Slamer, есть ли уверенность в том, что производительность падает именно из-за этой проверки? Если да:
По сути, нужно придумать как избавиться от проверок юнитов, которые заведомо не находятся рядом друг с другом. Можно к примеру, карту на зоны разбить - для каждой зоны отдельный список юнитов находящихся на ней.
Имеем одну зону со ста юнитами:
100*100 = 10000 проверок.
Имеем 4 зоны с двадцатью пятью юнитами на каждую:
25*25*4 = 2500 проверок.

и т.д.


Многие вопросы по Windows отпадут, если посмотреть тут

Сообщение отредактировал Нохчи - Вторник, 23 Октября 2012, 02:44
MR_BorgДата: Вторник, 23 Октября 2012, 07:23 | Сообщение # 3
участник
Сейчас нет на сайте
Есть и другой способ повысить производительность, скорее всего лагает не из-за этой проверки(в моей игре 200-300 обьектов могут одновременно взаимодействовать друг с другом). В Вашем же случае можно сделать таймер, например каждые 5 секунд делать проверку на наличие врагов,припятствий и пр.
P.S и зачем делать 3 вектора когда можно в 1?


Изучаю C++ попутно пишу игру.

Сообщение отредактировал MR_Borg - Вторник, 23 Октября 2012, 07:24
SlamerДата: Вторник, 23 Октября 2012, 14:26 | Сообщение # 4
был не раз
Сейчас нет на сайте
Quote (MR_Borg)
Есть и другой способ повысить производительность, скорее всего лагает не из-за этой проверки(в моей игре 200-300 обьектов могут одновременно взаимодействовать друг с другом). В Вашем же случае можно сделать таймер, например каждые 5 секунд делать проверку на наличие врагов,припятствий и пр.
P.S и зачем делать 3 вектора когда можно в 1?


Надо будет попробовать сделать таймер. Была такая идея, но я подумал что это просто приведёт к тормозам через каждые 5 секунд.
3 вектора сделаны для того, чтобы хранить в одном спрайты корпуса танка, в другом спрайт башни, в третьем, спрайт пули. Проблема ещё в том, что сам по себе спрайт никак не связан с объектом танка и чтобы распознавать какой спрайт принадлежит какому объекту, я просто храню их в определённом порядке не перемешивая. В противном случае будет уничтожаться один танк, а анимация взрыва корпуса и башни может проигрываться совершенно у других танков.

Quote (Нохчи)
Slamer, есть ли уверенность в том, что производительность падает именно из-за этой проверки?


Ещё + к этому придётся делать проверку каждой зоны на нахождение в ней каждого юнита, каждой декорации и т.д. и я боюсь как бы это не сделало ещё хуже.
MR_BorgДата: Вторник, 23 Октября 2012, 14:33 | Сообщение # 5
участник
Сейчас нет на сайте
Для каждого юнита свой таймер, проверка тогда будет более менее равномерна для каждого юнита.
Потом, сделай свои и вражеские юниты в 1 вектор.
Далее, когда танк стоит проверять препятствия надо только после того как вражеский зашел в зону видимости.
+еще можно много что придумать.

Quote (Slamer)
Проблема ещё в том, что сам по себе спрайт никак не связан с объектом танка и чтобы распознавать какой спрайт принадлежит какому объекту, я просто храню их в определённом порядке не перемешивая. В противном случае будет уничтожаться один танк, а анимация взрыва корпуса и башни может проигрываться совершенно у других танков.

Чет не очень понятно, скиньте хоть кусок кода.


Изучаю C++ попутно пишу игру.
SlamerДата: Вторник, 23 Октября 2012, 15:40 | Сообщение # 6
был не раз
Сейчас нет на сайте
Quote (MR_Borg)
Чет не очень понятно, скиньте хоть кусок кода.


Ну примерно вот так, куски кода из разных частей. Пока ещё не успел переделать массивы в вектора, поэтому не обращайте на это внимания.
Code
vector<tank1> tankV1x1; // Массив танков
hgeAnimation*       spr[troopLimit]; // Массив спрайтов танка
hgeAnimation*       sprB[troopLimit]; // Массив спрайтов башен танка
hgeAnimation*       sprP[troopLimit]; // Массив спрайтов снаряда танка

bool RenderFunc()
{
   // Отображение танков (Рендер)
   for (int j=0;j<troopLimit;j++)
   {
    // Танк
    spr[j]->RenderEx(tankV1x1[j].x+camX,tankV1x1[j].y+camY,tankV1x1[j].rot);
    sprP[j]->Render(tankV1x1[j].xP+camX,tankV1x1[j].yP+camY);
    sprB[j]->RenderEx(tankV1x1[j].x+camX,tankV1x1[j].y+camY,tankV1x1[j].rotB);
    // Обновление анимации
    spr[j]->Update(dt);
    sprB[j]->Update(dt);
    sprP[j]->Update(dt);
   }
}

bool FrameFunc()
{
         // Проверка танков
   for (int i=0;i<tankV1x1.size();i++)
   {
              // Взрыв танка
    if(tankV1x1[i].hp<0 && tankV1x1[i].activ)
    {
     tankV1x1[i].activ=0;
     spr[i]->Play();   // Анимация взрыва танка
     sprB[i]->Play();
    }
          }
}

Объекты класса "Танк" хранятся в tankV1x1, а рисунки танка в spr[troopLimit]. Сами по себе рисунок и объект никак не связаны, просто сделано так, что если уничтожается танк под номером 2, то и анимация проигрывается у спрайта, который лежит в массиве под номером 2.
Вообщем не парьтесь по этому поводу, пока это вроде бы не критично.
П.С. И спасибо за советы.


Сообщение отредактировал Slamer - Вторник, 23 Октября 2012, 15:40
MR_BorgДата: Вторник, 23 Октября 2012, 17:48 | Сообщение # 7
участник
Сейчас нет на сайте
Обращайтесь biggrin

Изучаю C++ попутно пишу игру.
ArchidoДата: Вторник, 23 Октября 2012, 20:17 | Сообщение # 8
Сэнсэй
Сейчас нет на сайте
Quote
На форуме HGE мне советовали разбить карту на квадраты, в эти квадраты помещать объекты и делать проверку только внутри квадрата. Идея не плохая, но при попытки реализовать её, пришёл к тому, что если, например я разделю карту на 100 квадратов, то для проверки лишь одного вида юнита (например танка) мне придётся создать 100 хранилищ для союзных танков, 100 хранилищ для вражеских танков, и ещё по 300 хранилищ для спрайтов танка

Идея в общем-то очень правильная и в нее очень хорошо вписывается принцип ООП. Есть некий базовый объект для юнитов от которых они все наследуются, он определяет базовый интерфейс для взаимодействия между наследуемыми объектами. В итоге для каждой клетки карты хранится единственный дин. массив подобных объектов ("базовых" юнитов) у которых можно спросить "сторону", "тип юнита", коондиранты или что-то еще и делать необходимые действия... Хранить кучу массивов для каждого типа юнитов смысла не имеет.


C++ - он особенный. С помощью него можно не только выстрелить себе в ногу, но и повеситься в пустой комнате:)
SlamerДата: Четверг, 25 Октября 2012, 22:37 | Сообщение # 9
был не раз
Сейчас нет на сайте
Вообщем, попробовал сделать таймер, не помогло: во первых юниты стали тупить и двигаться рывками, а во вторых тормоза не исчезли, как я и предполагал, начало тормозить рывками. Провёл ещё несколько тестов, игра смогла выдержать без тормозов 300 вражеских юнитов-троллей(это те, что зелёные), а вот на союзных юнитах, на танках или на пулемётчиках начинает тормозить уже при 100 единицах. Думаю, без квадратов всё же тут не обойтись, только вот как их сделать, пока смутно представляю.
Меня интересует сейчас такой вопрос: Например я сделал класс unit, который определяет общие для всех юнитов методы и характеристики, далее я создаю два класса-наследника tank и trooper. Каким образом мне сделать проверку поведения юнитов на карте, чтобы действовало для всех наследников? Если я создам вектор, хранящий класс unit, я смогу помещать в него экземпляр tank и trooper?
Извините если вопросы нубские, стаж программирования у меня менее 1 года.


Сообщение отредактировал Slamer - Четверг, 25 Октября 2012, 22:38
daunДата: Четверг, 25 Октября 2012, 23:17 | Сообщение # 10
постоянный участник
Сейчас нет на сайте
Карта делится на квадраты (двухмерный массив), выступает в роли базы данных.
По x y юнита, записываем его положение в карту и смотрим соседние ячейки на наличие врагов (они тоже отмечают свое положение на карте).
Тое перебираем юнитов и просматриваем их окружение. tongue
SlamerДата: Четверг, 25 Октября 2012, 23:40 | Сообщение # 11
был не раз
Сейчас нет на сайте
daun, это всё понятно) меня как раз и интересует способ перебора юнитов, без особых затрат и правильная реализация классов для этого дела.
daunДата: Суббота, 27 Октября 2012, 00:00 | Сообщение # 12
постоянный участник
Сейчас нет на сайте
Quote (Slamer)
меня как раз и интересует способ перебора юнитов, без особых затрат

Без особых затрат, это убрать вложенные циклы при переборе юнитов, а коллизии юнитов определять по записи на карте.
x-and1988Дата: Суббота, 27 Октября 2012, 00:18 | Сообщение # 13
постоянный участник
Сейчас нет на сайте
Каждому танку создать список объектов, которые он видит, или знает (с которыми возможно любое взаимодействие), и через некий интервал опрашивать все юниты на заданную дистанцию. Таким образом каждый танк будет иметь список в пару объектов, с которыми он реально может столкнутся. Вопрос только в реализации.

Изучаю Java
My new project
My old project
MR_BorgДата: Суббота, 27 Октября 2012, 14:00 | Сообщение # 14
участник
Сейчас нет на сайте
Может вы испозьзуете эффекты?
Или в самой реализации кода проблемы.Просто не может лагать из-за проверки векторов так сильно.


Изучаю C++ попутно пишу игру.
ArchidoДата: Суббота, 27 Октября 2012, 16:09 | Сообщение # 15
Сэнсэй
Сейчас нет на сайте
Quote (Slamer)
Если я создам вектор, хранящий класс unit, я смогу помещать в него экземпляр tank и trooper?

Да, это так и делается. Думаю, стоит немного почитать про ООП и полиморфизм, ибо это оно и есть.
Основной смысл в том, что нужно определить для класса unit некоторые универсальные методы, вызывая которые можно получить необходимую информацию о любом юните. Например, в классе unit мы определяем два метода GetUnitType() и GetUnitPosition() для определения типа юнита и его точной позиции, далее в наследниках tank и trooper мы переопределяем (или реализуем) эти два метода для каждого из этих классов (хотя для позиции не обязательно для каждого класса, достаточно лишь один раз определить). Теперь созданные объекты классов tank и trooper можно хранить как объекты класса unit и, "позвав" у последних два вышеописанных метода, получаем нужную информацию о типе юнита и его позиции. Как-то так вообщем.


C++ - он особенный. С помощью него можно не только выстрелить себе в ногу, но и повеситься в пустой комнате:)
SlamerДата: Понедельник, 26 Ноября 2012, 23:46 | Сообщение # 16
был не раз
Сейчас нет на сайте
Quote (MR_Borg)
Может вы испозьзуете эффекты?

Нет, эффектов нет, возможно что-то сделано коряво и не по нормальному, но я пока не выявил.
Quote (Archido)
Как-то так вообщем.

Понятно, будем делать, спасибо!

Добавлено (26.11.2012, 23:46)
---------------------------------------------
Привет народ, снова я и у меня есть очередной вопрос. Может кто-нибудь из знающих С++ рассказать или дать ссылку почитать, какие вообще существуют принципы оптимизации кода? Дело в том, что я перелопатил код своей игры, сделал его как мне советовали выше, выкинул много лишнего, но кое где пришлось добавить. Результат стал не лучше, а даже чуть хуже в плане производительности, но зато теперь намного легче ориентироваться в коде и не придёться заниматься копипастой при создании новых юнитов.


Сообщение отредактировал Slamer - Среда, 28 Ноября 2012, 21:17
  • Страница 1 из 1
  • 1
Поиск:

Все права сохранены. GcUp.ru © 2008-2024 Рейтинг