Як гра може обробити всіх персонажів одночасно?


31

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

Приклад

Я створюю гру в оборону вежі, в якій є 15 гнізд на башті, де будуються вежі, і кожна башта викидає снаряд з певною швидкістю; скажемо, що щосекунди по кожній з веж створюється 2 снаряди , а на поле битви йдуть вороги, скажімо 70 (у кожного з 10 видів атрибутів, таких як HP, Mana та ін.), які змінюватимуться, коли вони рухатимуться навколо поле бою).

Підсумок

Кількість башти = 15
снарядів, створених кожною вежею на секунду = 2
загальна кількість створених снарядів за секунду = 30
одиниць у кол. Поля бою = 70

Тепер, чи гра обробляє ці 30 снарядів і 70 одиниць , обробляючи їх на 100 різних нитках (що занадто багато для ПК) або 1 нитка, яка переміщує їх усіх, зменшує їх значення тощо (що буде неначе повільним) , Я думаю)?

Я не маю поняття про це, тому хтось може мене направити на те, як це вийде?


Коментарі не для розширеного обговорення; ця розмова перенесена в чат .
MichaelHouse

Додавання інших відповідей ... приклад деяких масових ігор. У Skyrim було здійснено більшу частину оновлення логіки гри на одному потоці. Спосіб, яким він керує цим настільки добре, полягає в тому, що віддалені НПС (милі, що знаходяться в милях) зближуються відповідно до їх графіку. Більшість MMO оновлюють логіку гри на одному потоці, АЛЕ кожна частина карти існує на іншій стійці потоку чи сервера.
самогонTheleocat

Відповіді:


77

Тепер, як гра обробляє ці 30 снарядів і 70 одиниць, обробляючи їх на 100 різних нитках

Ні, ніколи цього не робіть. Ніколи не створюйте новий потік на ресурс, це не масштабує роботу в мережі, як і в оновленнях об'єктів. (Хтось пам’ятає часи, коли у вас була одна нитка для читання на сокет в Java?)

1 нитка, яка переміщує їх, зменшує їх значення тощо?

Так, для початку це шлях. "Великі двигуни" розділили деяку роботу між нитками, але це не потрібно для початку такої простої гри, як гра в оборону вежі. Мабуть, ще більше роботи, щоб зробити кожен галочку, яку ви також зробите в цій одній нитці. О так, і візуалізація звичайно.

(який буде повільним, я думаю)

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


34
Більше, як півсекунди, якщо ви не робите щось дивне. Але найголовніше, що розділення роботи на кілька ниток буде робити все гірше , а не краще. Не кажучи вже про те, що багатопоточність є надзвичайно важким.
Луань

13
+1 Більшість сучасних ігрових двигунів в режимі реального часу представляють тисячі або навіть десятки тисяч полігонів, що набагато інтенсивніше, ніж відстеження руху всього 100 об’єктів у пам'яті.
фірфокс

1
«Проста гра, як гра в оборону вежі». Хм ... ти коли-небудь грав у " Захисна сітка: Пробудження" чи її продовження?
Мейсон Уілер

4
«Ніколи не створити новий потік для кожного ресурсу, це не масштабується в мережі ...» гм деякі дуже масштабовані архітектури зробити саме це!
NPSF3000

2
@BarafuAlbino: Це дивна річ сказати. Існує маса вагомих причин, щоб створити більше потоків, ніж наявних ядер. Це складність / продуктивність / і т.д. компроміс, як і будь-яке інше дизайнерське рішення.
Дітріх Епп

39

Правило номер одне з багатопотокових читає таке: Не використовуйте його, якщо вам не потрібно паралелізувати декілька ядер процесора для продуктивності чи чутливості. Вимога "x і y має відбуватися одночасно з точки зору користувачів" ще не є достатнім приводом для використання багатопотокової.

Чому?

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

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

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

Також загальною мудрістю в розробці ігор є те, що логіка гри дуже рідко є вузьким місцем гри. Критична продуктивність - це майже завжди графіка. Так, навіть для 2d ігор.


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

6
@Sam Кінцеві терміни зустрічі в системах реального часу - це випадок, коли для реагування вам потрібна багатопотокова редакція. Але навіть там логічна простота, яку ви, здавалося б, досягаєте через нарізання різьби, часто підступна, оскільки створює приховані складності у вигляді тупиків, перегонів та голодування ресурсів.
Філіпп

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

1
@LorenPechtel Я теж бачив це. Але зазвичай це було вирішено, не роблячи непотрібних обчислень шляху (наприклад, перерахунок кожного окремого шляху на кожній галочці), кешування часто запитуваних шляхів, використання багатоярусного пошуку шляху та використання більш відповідних алгоритмів визначення маршруту. Це те, де кваліфікований програміст зазвичай може знайти великий потенціал оптимізації.
Філіп

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

26

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

Наприклад, якщо у вас 1000 снарядів і 1000 ворогів, наївним рішенням є просто перевірити їх усіх один проти одного.

Це означає, що ви закінчите з p * e = 1,000 * 1,000 = 1,000,000 різних перевірок! Це О (п ^ 2).

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

Наприклад, якщо ви перераховуєте на кожному квадраті сітки, які вороги знаходяться в цьому квадраті, ви можете перевести через 1000 снарядів і просто перевірити квадрат на сітці. Тепер вам просто потрібно перевірити кожен снаряд проти квадрата, це О (n). Замість мільйона чеків кожного кадру вам потрібно лише тисяча.

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


1
Як альтернатива збереженню всієї сітки в пам'яті лише для відстеження кількох елементів, ви також можете використовувати b-дерева, по одному для кожної осі, для швидкого пошуку можливих кандидатів на зіткнення тощо. Деякі двигуни навіть роблять це за вас автоматично " "; ви вказуєте регіони звернень і запитуєте список зіткнень, і бібліотека передає вам його. Це одна з багатьох причин, чому розробники повинні використовувати двигун, а не писати з нуля (коли це можливо, звичайно).
phyrfox

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

17

Не створюйте потоки на ресурс / об'єкт, а на розділ логіки програми. Наприклад:

  1. Нитка для оновлення одиниць і снарядів - логічна нитка
  2. Нитка для візуалізації екрана - нитка GUI
  3. Нитка для мережі (наприклад, мультиплеєр) - потік вводу-виводу

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


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

1
Не занадто часто малювати (наприклад, більше 50 разів на секунду) важливо важливо, і це питання стосувалося продуктивності. Програма для поділу - це найпростіша річ, яку можна зробити для реальної вигоди від продуктивності. Це правда, це вимагає певних знань про теми, але здобувати ці знання варто.
Томаш Зато - Відновити Моніку

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

@Falco Ви наглядаєте за довгостроковими перевагами цієї моделі - як щодо проекту, так і досвіду програміста. Ваша заява, що найважче думати, реально не можна вирішити, це лише думка. Для мене дизайн GUI набагато жахливіший. Усі розвинені мови (C ++, Java) мають досить чіткі багатопотокові моделі. І якщо ви справді не впевнені, ви можете використовувати модель актора, яка не страждає від помилок для багатопотокових початківців. Ви знаєте, що є причина, чому більшість програм розроблені так, як я запропонував, але не соромтесь сперечатися з цим далі.
Томаш Зато - Відновити Моніку

4

Навіть космічні загарбники управляли десятками взаємодіючих об'єктів. Тоді як декодування одного кадра відео H264 HD включає сотні мільйонів арифметичних операцій. У вас є багато потужності для обробки.

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


2
Я не впевнений, що Space Invaders - це найкраще порівняння. Причина починається повільно і пришвидшується, коли ви вбиваєте ворогів не тому, що було створено таким чином, а тому, що апаратне забезпечення не могло впоратися з рендерінгом багатьох ворогів одночасно. en.wikipedia.org/wiki/Space_Invaders#Hardware
Майк Келлогг

А як щодо того, що кожен об’єкт підтримує список усіх об'єктів, які є досить близькими, щоб він міг зіткнутися з ними на наступній секунді, оновлюючись раз на секунду або щоразу, коли вони змінювали напрям?
Випадково832

Залежить від того, що ти моделюєш. Рішення для розділення космічного простору - це ще один поширений підхід: розділити світ на регіони (наприклад, BSP, який, можливо, доведеться зробити в будь-якому випадку для цілей візуалізації, або квадри), тоді ви можете зіткнутися лише з об'єктами в одному регіоні.
pjc50

3

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

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

Ви, мабуть, НЕ хочете одного потоку для кожного можливого зіткнення, тільки тому, що це, ймовірно, зашкодить планувальник; також є вартість, пов’язана зі створенням та руйнуванням ниток. Краще зробити деяку кількість потоків навколо ядер системи (або використовувати метрику на зразок старої #cores * 2 + 4), а потім повторно використовувати їх, коли процес завершиться.

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

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

Однак, фізичні двигуни часто використовують декілька потоків, і я можу назвати декілька ігор, які, на мою думку, виграли б з декількох логічних потоків (наприклад, Paradox RTS-ігри, такі як HOI3 і такі, наприклад).

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


2

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

Комп'ютер не обробляє відразу всі об'єкти в грі. Він обробляє їх послідовно.

Комп'ютерна гра просувається в дискретні часові кроки. Залежно від гри та швидкості роботи ПК, ці кроки, як правило, або 30, або 60 кроків в секунду, або стільки / кілька кроків, скільки ПК може обчислити.

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

Середній процесор повинен бути 2 ГГц або швидше, це означає 10 9 тактових циклів в секунду. Якщо ми обчислимо 60 тимчасових кроків в секунду, що листи 10 9 циклів / 60 тактів = 16666666 годин за час кроку. Маючи 70 одиниць, у нас залишається приблизно 2400 000 тактових циклів на одиницю. Якби нам довелося оптимізувати, ми могли б оновлювати кожну одиницю всього за 240 циклів, залежно від складності логіки гри. Як бачите, наш комп’ютер приблизно в 10000 разів швидше, ніж потрібно для цього завдання.


0

Відмова: Мій улюблений тип гри у всі часи - це текстовий текст, і я пишу це як давній програміст старого MUD.

Я думаю, що важливе питання, яке ви повинні задати собі, це таке: чи вам навіть потрібні нитки? Я розумію, що в графічній грі, мабуть, більше використовується MT, але я думаю, що це також залежить від механіки гри. (Можливо, варто також врахувати, що з графічними процесорами, процесорами та всіма іншими ресурсами, які ми сьогодні маємо, набагато сильніше, що робить ваші проблеми щодо ресурсів настільки ж проблемними, як вам може здатися; дійсно 100 об’єктів практично нульові). Це також залежить від того, як ви визначаєте "всіх символів відразу". Ви маєте на увазі точно в той самий час? У вас цього не буде, як справедливо вказує Петро, ​​так що все відразу не має значення в прямому сенсі; вона з'являється лише таким чином.

Якщо припустити, що ви підете з теми: Ви точно не повинні враховувати 100 потоків (і я навіть не збираюся вникати в те, чи занадто це для вашого процесора чи ні; я маю на увазі лише ускладнення та практичність цього).

Але пам’ятайте це: багатократна нарізка не є легкою (як вказує Філіпп) і має багато проблем. Інші мають набагато більше досвіду (на багато), ніж у мене з МТ, але я б сказав, що вони теж пропонують те саме (хоча б вони були більш здібними, ніж я - особливо без практики з мого боку).

Деякі стверджують, що вони не згодні з тим, що потоки не є корисними, а деякі стверджують, що кожен об'єкт повинен мати нитку. Але (і знову це весь текст, але навіть якщо ви вважаєте, що більше однієї теми вам не потрібно - і не слід - розглядати її для кожного об'єкта), як Філіп зазначає, що ігри, як правило, повторюються за списками. Але це не тільки (як він пропонує, хоча я розумію, що він відповідає лише на ваші параметри так мало об'єктів) для такої кількості об'єктів. У MUD Я програміст, оскільки ми маємо наступне (і це не вся діяльність, яка відбувається в режимі реального часу, тому майте це на увазі):

(Кількість екземплярів залежить від курсу - вище та нижче)

Мобілі (NPC, тобто персонаж не гравця): 2614; прототипи: 1360 Об'єкти: 4457; прототипи: 2281 Кімнати: 7983; прототипи: 7983. Кожна кімната зазвичай має власний примірник, але у нас також є динамічні кімнати, тобто кімнати всередині кімнати; або кімнати всередині мобільного, наприклад, шлунок дракона; або кімнати в предметах, наприклад, ви вводите магічний предмет). Майте на увазі, що ці динамічні кімнати існують на об'єкт / кімнату / мобільний телефон, який фактично має їх визначено. Так, це дуже схоже на World of Warcraft (я не граю в нього, але друг змусив мене грати його, коли у мене була машина Windows, якийсь час) уявлення про випадки, за винятком того, що ми мали це задовго до того, як World of Warcraft навіть існував.

Сценарії: 868 (на даний момент) (як не дивно, наша команда статистики не показує, скільки прототипів у нас є, тому я додам це). Все це проводиться в районах / зонах, і у нас їх 103. У нас також є спеціальні процедури, які проводяться в різний час. У нас також є інші події. Тоді ми також підключили розетки. Мобілі рухаються, займаються різними видами діяльності (крім бойових), взаємодіють з гравцями тощо. (Так роблять інші типи утворень).

Як ми впораємося з усім цим без зволікань?

  • розетки: select (), черги (введення, виведення, події, інші речі), буфери (введення, виведення, інші речі) тощо. Вони опитуються 10 разів на секунду.

  • символи, предмети, кімнати, бій, все: все в центральному циклі на різних імпульсах.

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

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