Среда, 13 Ноября 2024, 10:42

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

[ Новые сообщения · Игроделы · Правила · Поиск ]
  • Страница 1 из 1
  • 1
Коллизии (и не только) в большом 2D мире.
HuntlierДата: Суббота, 24 Ноября 2012, 19:58 | Сообщение # 1
был не раз
Сейчас нет на сайте
Собственно, делаю 2D rpg (этим мало кого удивишь тут), мой первый опыт так скажем, но если не будет проблем с художником, то игра будет очень не плохой, в плане механики куча идей, да и сюжет возможно не плох, но до этого ещё далеко...возник вопрос как же сделать коллизии и как вообще их считать в большом мире (единый мир, практически без отдельных локаций), про коллизии знаю много, но вот код популярнейших примеров понять до конца так и не смог, кто может пожалуйста объясните до мелочей (а лучше комментариями к каждой строке кода)?

На примере этого
Code
public static bool IntersectPixels(Rectangle rectangleA, Color[] dataA, Rectangle rectangleB, Color[] dataB)
{
     // Find the bounds of the rectangle intersection
     int top = Math.Max(rectangleA.Top, rectangleB.Top);
     int bottom = Math.Min(rectangleA.Bottom, rectangleB.Bottom);
     int left = Math.Max(rectangleA.Left, rectangleB.Left);
     int right = Math.Min(rectangleA.Right, rectangleB.Right);
   
     // Check every point within the intersection bounds
     for (int y = top; y < bottom; y++)
     {
            for (int x = left; x < right; x++)
         {
             // Get the color of both pixels at this point
             Color colorA = dataA[(x - rectangleA.Left) +
                    (y - rectangleA.Top) * rectangleA.Width];
             Color colorB = dataB[(x - rectangleB.Left) +
                    (y - rectangleB.Top) * rectangleB.Width];
   
             // If both pixels are not completely transparent,
             if (colorA.A != 0 && colorB.A != 0)
             {
                 // then an intersection has been found
                 return true;
             }
         }
     }
   
     // No intersection found
     return false;
}

или этого
Code
class Sprite {

     [...]

     public bool CollidesWith(Sprite other)
     {
         // Default behavior uses per-pixel collision detection
         return CollidesWith(other, true);
     }

     public bool CollidesWith(Sprite other, bool calcPerPixel)
     {
         // Get dimensions of texture
         int widthOther = other.Texture.Width;
         int heightOther = other.Texture.Height;
         int widthMe = Texture.Width;
         int heightMe = Texture.Height;

         if ( calcPerPixel &&                    // if we need per pixel
             (( Math.Min(widthOther, heightOther) > 100) ||  // at least avoid doing it
             ( Math.Min(widthMe, heightMe) > 100)))          // for small sizes (nobody will notice :P)
         {
             return Bounds.Intersects(other.Bounds) // If simple intersection fails, don't even bother with per-pixel
                 && PerPixelCollision(this, other);
         }

         return Bounds.Intersects(other.Bounds);
     }

     static bool PerPixelCollision(Sprite a, Sprite b)
     {
         // Get Color data of each Texture
         Color[] bitsA = new Color[a.Texture.Width * a.Texture.Height];
         a.Texture.GetData(bitsA);
         Color[] bitsB = new Color[b.Texture.Width * b.Texture.Height];
         b.Texture.GetData(bitsB);

         // Calculate the intersecting rectangle
         int x1 = Math.Max(a.Bounds.X, b.Bounds.X);
         int x2 = Math.Min(a.Bounds.X + a.Bounds.Width, b.Bounds.X + b.Bounds.Width);

         int y1 = Math.Max(a.Bounds.Y, b.Bounds.Y);
         int y2 = Math.Min(a.Bounds.Y + a.Bounds.Height, b.Bounds.Y + b.Bounds.Height);

          // For each single pixel in the intersecting rectangle
          for (int y = y1; y < y2; ++y)
          {
              for (int x = x1; x < x2; ++x)
              {
                  // Get the color from each texture
                  Color a = bitsA[(x - a.Bounds.X) + (y - a.Bounds.Y)*a.Texture.Width];
                  Color b = bitsB[(x - b.Bounds.X) + (y - b.Bounds.Y)*b.Texture.Width];

                  if (a.A != 0 && b.A != 0) // If both colors are not transparent (the alpha channel is not 0), then there is a collision
                  {
                      return true;
                  }
              }
          }
         // If no collision occurred by now, we're clear.
         return false;
     }

     private Rectangle bounds = Rectangle.Empty;
     public virtual Rectangle Bounds
     {
         get
         {
             return new Rectangle(
                 (int)Position.X - Texture.Width,
                 (int)Position.Y - Texture.Height,
                 Texture.Width,
                 Texture.Height);
         }

     }


Коллизии можно ещё понять, но если учитывать, что мир огромен, не считать же коллизии на каждом шаге относительно всех объектов, да и считать ближайшие объекты, а относительно них коллизии тоже не особо радует, собственно появилась идея сделать следующим образом, весь мир разделить на сетку из большого количества секторов, в каждом секторе будет список динамических (куда будет входить персонаж, противники, снаряды, другими словами всё что там находится в данный момент времени) и статических объектов (ну это понятно, окружающий мир), таким образом можно решить проблему с огромным количеством просчётов коллизий, каждый Update сектор в котором есть активные динамические объекты (персонаж например), будет просчитывать коллизию каждого динамического объекта относительно окружающих объектов, на самом деле так как мне кажется решается ещё и проблема отрисовки большого количества объектов, одновременно будут рисоваться только ближайшие к персонажу сектора, единственное, каждый Update, нужно будет определять принадлежность динамических объектов определённому сектору (понятное дело не всех в мире, а только тех которые находятся в ближайших секторах), как считаете так будет нормально работать или есть алгоритмы по проще?

P.S. сейчас реализовать секторы до конца не могу, из за того что тупо не дошёл до списков объектов в C#, встречался с ними, но сам не использовал. В плане знания ЯП: С++ доучиваю, C# в процессе изучения.

Заранее спасибо.

Добавлено (24.11.2012, 19:58)
---------------------------------------------
Хах, только сейчас понял, что весь код уже с комментариями biggrin
Ладно, с коллизиями вопрос закрыт, а как на счёт разбиения на секторы? Есть у кого мысли по этому поводу?)

GECKДата: Суббота, 24 Ноября 2012, 20:21 | Сообщение # 2
заслуженный участник
Сейчас нет на сайте
Идея вполне правильная и активно используется в геймдеве. Больше информации в гугле, по запросу "spatial hashing" smile

Всё гениальное просто. И хреново работает.
Cpt_FlashДата: Суббота, 24 Ноября 2012, 20:28 | Сообщение # 3
почти ветеран
Сейчас нет на сайте
Quote
Коллизии (и не только) в большом 2D мире.
-звучит как название книги.


HuntlierДата: Суббота, 24 Ноября 2012, 21:01 | Сообщение # 4
был не раз
Сейчас нет на сайте
Единственный вопрос, как определять принадлежность динамических объектов к сектору? Каждый раз проверять в "активных" (которые близко к персонажу) секторах координаты всех динамических объектов и определять принадлежность или же у каждого динамического объекта сверять координаты с координатами всех секторов и так определять принадлежность?
zodiakДата: Суббота, 24 Ноября 2012, 22:16 | Сообщение # 5
постоянный участник
Сейчас нет на сайте
При изменении позиции объекта, проверять и при необходимости, менять привязку объекта к сектору. Причем лучше если объект делает это сам. Лишний цикл, в котором будут перебираться и проверяться все объекты будет давить на производительность.

Если интересно, можешь почитать, как я борюсь за производительность в громадном мире здесь.


Точка зору окремо взятого індивіда завжди суб'єктивна!

Взломщик
Battle City.Net
HuntlierДата: Среда, 13 Февраля 2013, 19:57 | Сообщение # 6
был не раз
Сейчас нет на сайте
Спасибо за ответы, ещё такой вопрос, если динамический объект ( допустим персонаж ) находится на стыке двух секторов? Как самый правильный вариант - это считать коллизию для обоих секторов, но не прибавится ли от этого существенный лагов на переходах из сектора в сектор?

P.S. проверить в действии ещё не могу...

Добавлено (13.02.2013, 19:57)
---------------------------------------------
Наконец руку дошли до реализации всей системы, сейчас возник один вопрос, как лучше реализовать загрузку\выгрузку секторов, а точнее спрайтов (я под словом спрайт имею ввиду текстуру и её координаты)...
Пока пришёл только к нескольким вариантам:
1) Загрузить сразу все сектора, ничего не выгружать и не загружать в процессе игры, а лишь рисовать или не рисовать сектор.
2) Загрузить сразу все спрайты, но без текстур (то есть только координаты расположения текстур), а в процессе игры загружать (именно загружать, а не копировать загруженные) текстуры приписанные каждому сектору по ID который будет хранится в каждом спрайте, при выгрузке просто высвобождать память под текстуру.
3) Загрузить сразу все текстуры и отдельно от текстур все сектора с их спрайтами (содержащими только координаты), а далее при активации сектора копировать уже загруженные текстуры в спрайты сектора, а когда сектор не нужен - выгружать текстуры из спрайтов сектора.

Ещё был 4 вариант: Все текстуры держать в одном большой файле, а спрайт содержал бы только два прямоугольника (1ый куда будет рисовать, то есть с координатами, 2ой что будет рисовать, то есть координаты текстуры в главном файле), тогда, всё сводилось бы только к загрузке и выгрузки 8 целочисленных переменных...НО тогда я не знаю как сделать попиксельную коллизию, ибо в моём случае она проверяется как раз таки сравнением каждого пикселя текстуры, а текстур то в спрайтах нет, есть только их прямоугольники, то есть обычное столкновение прямоугольников...
По этому 4 вариант я отбросил. Хотя может есть какой то вариант попиксельной коллизии при условии что все текстуры в одном файле?

Собственно интересует мнение, какой из этих способов будет более быстрым и качественным в плане скорости и памяти...
Делаю движок для игры, по этому сам проверить что быстрее не могу, а потом перебирать код как то не хочется.

Если есть другие варианты буду признателен если поделитесь )

nonameДата: Суббота, 16 Марта 2013, 23:06 | Сообщение # 7
заслуженный участник
Сейчас нет на сайте
Цитата (Huntlier)
а как на счёт разбиения на секторы? Есть у кого мысли по этому поводу?)

это давно используется в геймдеве и обычно с этим бывают связаны некоторые косяки.

Цитата (Huntlier)
как лучше реализовать загрузку\выгрузку секторов, а точнее спрайтов (я под словом спрайт имею ввиду текстуру и её координаты)...

попробую дать по-возможности простой но хороший вариант:

если мир реально большой, то загружать весь мир лучше не надо. загружай только сектор игрока + те, которые видны из сектора игрока. проще всего сделать сектора квадратными и расположить препятствия так, чтобы из сектора были видны не более четырёх соседних секторов- тех, в которые из этого сектора возможно перейти. и каждый сектор должен хранить инфу о том, какие сектора ему соседние.

текстуры и прочее необходимое догружай и выгружай по мере необходимости. ты всегда должен знать, какие сектора у тебя загружены и что им нужно.

это совет исходя из того, что требуется сделать реально огромный мир.

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

и ещё: как ты будешь создавать карту огромного мира?


все правила форумной игры Дуэль Программистов
divol13Дата: Суббота, 16 Марта 2013, 23:44 | Сообщение # 8
участник
Сейчас нет на сайте
да, привязывай объекты к сектору и проверяй столкновения с другими объектами в том же секторе.
этож как два пальца)
меньше слов - больше дела smile


HuntlierДата: Воскресенье, 17 Марта 2013, 01:05 | Сообщение # 9
был не раз
Сейчас нет на сайте
noname, уже реализовал секторы, сделал проще некуда, а именно в самом начале загружаю все секторы и все спрайты в них, НО спрайт это лишь два прямоугольника 1) в котором рисуется текстура 2) указывающий координаты текстуры в атласе. Во время прорисовки все объекты рисуются из нескольких атласов, таким образом оптимизировал и отрисовку. Мир большой, но вот подгружать секторы не стану, не хочу лишний раз лезть в файл за данными. Проверку коллизий и отрисовку делаю лишь 9ти секторов, каждый сектор будет размером около 1500х1500 пикселей, в идеале 2048Х2048 (если считать в координатах, тобешь столько и пикселей условно), таким образом всё получилось довольно просто. Единственное, используя атласы, мне пришлось отказаться от попиксельной коллизии.

Цитата (noname)
как ты будешь создавать карту огромного мира?

Планирую в ближайшее время создать приступить к редактору мира (mapeditor), но сначала нужно управление персонажем изменить, а то он не падает, наворотил уж очень, всё таки первый раз тяжеловато делать)


Сообщение отредактировал Huntlier - Воскресенье, 17 Марта 2013, 01:07
divol13Дата: Воскресенье, 17 Марта 2013, 14:45 | Сообщение # 10
участник
Сейчас нет на сайте
во делал когда-то неччто подобное
http://rghost.ru/44565616
ТАБ - включить сетку - как раз сектора
4 - показывает сколько объектов в данном секторе
wsad - управлние объектом
up/down/right/left - управление камерой
да, кстати, чем хороша разбивка на сектора, что рисуются только те объекты которые сейчас входят во фруструм камеры. Что тоже влияет на производительность в лучшую сторону




Сообщение отредактировал divol13 - Воскресенье, 17 Марта 2013, 14:47
nonameДата: Воскресенье, 17 Марта 2013, 20:13 | Сообщение # 11
заслуженный участник
Сейчас нет на сайте
Цитата (Huntlier)
Проверку коллизий и отрисовку делаю лишь 9ти секторов

какой же я болван: у тебя же 2d игра без просчёта области видимости! мой совет про четыре соседних сектора годился бы для 3d игры или для рогалика, в котором учитывается область видимая игроком. в обычной нормальной 2d игре действительно нужно обсчитывать 3х3сектора.


все правила форумной игры Дуэль Программистов
  • Страница 1 из 1
  • 1
Поиск:

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