Спосіб зберігання потенційно нескінченних даних двовимірної карти?


29

У мене є 2D платформер, який на даний момент може обробляти шматки зі 100 на 100 плиток, координати фрагментів зберігаються як довгі, тому це єдиний ліміт карт (maxlong * maxlong). Усі позиції юридичних осіб і т. Д. І т. Д. Є актуальними, тому немає обмежень.

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


2
Деякі структури даних, на які можна було б ознайомитись, для отримання натхнення - це розріджені матриці та (багаторівневі) таблиці сторінок .
Ендрю Рассел

Низький пріоритет: Ви можете уточнити, чи тип "довгих" даних є 32- або 64-розрядним?
Рандольф Річардсон

2
@ Рандольф, враховуючи, що це C #, імовірно, він має на увазі C #, longякий є 64-бітовим (настільки маклонговим є Int64.MaxValue).
Ендрю Рассел

Notch має кілька цікавих речей про нескінченні карти в Minecraft у своєму блозі тут: notch.tumblr.com/post/3746989361/terrain-generation-part-1
dlras2

Відповіді:


17

Створіть спеціальний формат карти для своєї гри. Це простіше, ніж можна подумати. Просто використовуйте клас BinaryWriter. Спочатку напишіть заголовок у декілька інт або уніт. Інформація, яку потрібно включити до заголовка:

  • Чарівний рядок / магічний номер формату файлу.
  • Початок / кінець / розмір фрагментів, описаних у цьому файлі

а також (і тут йдеться про критичну частину продуктивності

  • ints, які описують вихідне положення всередині файлу. Тож вам не доведеться шукати конкретні шматки.

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

Потім, коли ви хочете завантажити певний фрагмент, вам просто доведеться шукати всередині індексу. Коли ви отримаєте позицію, просто встановіть fileStream.Position = PositionOfChunkFromIndex і можете завантажити її.

Вся справа в дизайні файлового формату із заголовком, який описує вміст файлу найбільш ефективно.

Просто збережіть файли зі спеціальним розширенням, яке ви створили, і вирушайте.

БОНУС: Додайте стиснення BZip2 до конкретних областей файлу / всього вмісту (не заголовка !!), щоб ви могли розпакувати конкретні шматки з файлу для дуже невеликого сліду пам’яті.


12
Варто зазначити, що, якщо ви збираєтеся змінювати цей файл на ходу, вам потрібно буде або заголовок / індекс фіксованого розміру, або зовнішній, щоб ви могли додавати фрагменти до файлу без необхідності переписувати цілий файл (із-за зміни зрушень).
Ендрю Рассел

У цей момент ви не просто реалізуєте базу даних flatfile?
Апе-інаго

13

Я зіткнувся з подібною проблемою і вирішив створити власну структуру для обробки даних. Він заснований вільно на квадраті, але має нескінченну (принаймні велику, як Інт) розширюваність у всіх напрямках. Він був розроблений для обробки даних на основі сітки, які розширилися від центральної точки, як це робить Minecraft зараз. Це просторовий об'єм пам’яті та дуже швидкий.

Ви можете вказати мінімальну величину для кожного вузла (магнітуда 7 складе 128x128), і як тільки будь-який вузол має визначений відсоток заселених його підметних вузлів, він автоматично сплющується в двовимірний масив. Це означає, що дуже густонаселена частина (наприклад, повністю досліджений континент) буде мати масив (дуже швидко), але малонаселена частина (наприклад, берегова лінія, хто блукав вгору і вниз, але не досліджував внутрішню) мають хороші показники та низьке використання пам'яті.

Мій код можна знайти тут . Код повний, перевірений (одиничні та навантажувальні випробування) та досить оптимізований. Внутрішня робота ще не надто добре зафіксована, проте всі публічні методи такі, що слід використовувати. Якщо хтось вирішить спробувати його, не соромтесь звертатися до мене з питаннями чи коментарями.

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


Отже, це в основному дерево, що розширюється, правда? Що я пропускаю?
якDD

2
Найбільше покращення в порівнянні з розширюваним деревом полягає в тому, що воно «сплющує» певні вузли дерева, які є густо заселеними (за замовчуванням 70%) у двовимірні масиви, а не підтримують їх структурованими як дерева. Це дає вам швидкість пошуку масиву без (нескінченного) розміру нескінченного масиву.
dlras2

І листкові, і внутрішні вузли, правда? Цікава ідея, яка може дати хороші результати, я спробую, якщо мені це знадобиться. Btw, +1 для видачі коду та швидкої відповіді! О, і тестування, зроблене також, я (на жаль) ніколи цього не роблю в своїх особистих проектах :)
kaoD

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

1
Я втратив зір, вибачте! Я все одно хотів би його очистити, але я повільно переробляю частину коду між класом і домашнім завданням, щоб цього не було на деякий час. Наразі стара, не дуже хороша демонстраційна версія тут: j.mp/qIwKYt Не дуже, я частково маю на увазі, що це потребує багато пояснень, тому не забудьте прочитати README і не соромтесь задавати питання тут чи електронною поштою.
dlras2

3

Ви можете замість цього використовувати базу даних - PostgreSQL має деякі спеціальні можливості індексації, оптимізовані для цього типу даних, які розташовані за координатами X та Y. Ви також можете вказати, що дані, що повертаються, знаходяться в певному радіусі, а не в області квадратної або довгастої форми.

  PostgreSQL (вільний та відкритий код)
  http://www.postgresql.org/

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

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

Я помітив, що деякі ігри зберігають "кеш" даних карт на локальному жорсткому диску після їх першого отримання (це, безсумнівно, зменшення мережевого вводу / виводу), наприклад, Ashen Empires:

  Ешен Імперії (безкоштовно грати, прекрасна 2D-реалізація)
  http://www.ashenempires.com/

Відстеження "останніх оновлених" часових позначок з кожним фрагментом / плиткою також буде корисним, оскільки, коли доступні локально збережені дані, SQL-запит може включати додатковий пункт "WHERE timestamp_column> $ local_timestamp", щоб отримати лише оновлені шматки / плитки завантажені (дві переваги збереження пропускної здатності на кшталт цього - це менші витрати на підключення та менше відставання для ваших гравців, що стане більш очевидним, коли ваша гра стане популярною).

Екран, знятий з Ешен Імперій (декілька персонажів є в місцевому банку, і за виглядом цих кісток на підлозі схоже, що кілька скелетних монстрів, мабуть, заїхали і, ймовірно, були забиті охоронцями місцевого міста):

введіть тут опис зображення


2

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

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


@Josh Petrie дякую за вагомі та великі мовні / стилістичні виправлення моєї публікації. Шкода, що я не можу внести правки :-D
sh код

1

Чи є якийсь спосіб поділити шматки (якісь "субконтиненти / країни" у вашому світі)? Тож, можливо, у вас можуть бути якісь файли індексів, які дозволяють швидко знайти, який під-файл / частину більшого файлу потрібно завантажити, щоб мати шматок пам'яті ...


завжди є спосіб розділити шматки. ВСЕГДА. незалежно від того, вони видно програвачеві / мають відношення до решти системи чи ні, завжди існує спосіб розподілу даних світу на шматки, як правило, різними способами.
ш код

0

Ви можете взяти ідею у Minecraft. Спочатку вони мали файл за шматок. Тепер вони використовують формат MCRegion, який групує фрагменти в області 32х32 та зберігає один із них у файлі.

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