Как игра может обрабатывать всех персонажей одновременно?

31

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

пример

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

Резюме

Башенные Count = 15
Снаряды Создано Каждая башня в секунду = 2
Общее количество ударников Created в секунду = 30
единиц в Battlefield Count = 70

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

Я понятия не имею по этому поводу, поэтому кто-нибудь может подсказать мне, как это будет работать?

Нация разработчиков
источник
Комментарии не для расширенного обсуждения; этот разговор был перенесен в чат .
MichaelHouse
Добавление к другим ответам ... пример некоторых массовых игр. У Skyrim была большая часть обновления игровой логики в одном потоке. То, как он справляется с этим настолько хорошо, заключается в том, что отдаленные NPC (NPC, находящиеся за много миль) аппроксимируются в соответствии с их графиком. Большинство MMO обновляют игровую логику в одном потоке, НО каждая часть карты существует в другом потоке или серверной стойке.
СамогонТелеокат

Ответы:

77

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

Нет, никогда не делай этого. Никогда не создавайте новый поток для ресурса, это не масштабируется в сети, равно как и при обновлении сущностей. (Кто-нибудь помнит времена, когда у вас был один поток для чтения на сокет в Java?)

1 поток, который перемещает их всех, уменьшает их ценность и т. Д.?

Да, для начала, это путь. «Большие двигатели» распределяют некоторую работу между потоками, но это не нужно для запуска простой игры, такой как игра в защиту башни. Вероятно, есть еще больше работы, чтобы сделать каждый тик, который вы также сделаете в этой теме. Ах да, и рендеринг конечно.

(что будет немного медленно, я думаю)

Ну ... Как вы определяете медленный ? Для 100 объектов это не должно занять больше половины миллисекунды, возможно, даже меньше, в зависимости от качества вашего кода и языка, с которым вы работаете. И даже если это занимает две полных миллисекунды, это все равно достаточно, чтобы набрать 60 т / с (тиков в секунду, в данном случае речь не идет о кадрах).

tkausl
источник
34
Больше похоже на полсекунды, если вы не делаете что-то странное. Но самое главное, что разделение работы на несколько потоков сделает все хуже , а не лучше. Не говоря уже о том, что многопоточность чрезвычайно сложна.
Luaan
13
+1 Большинство современных игровых движков рендерит тысячи или даже десятки тысяч полигонов в режиме реального времени, что намного интенсивнее, чем отслеживание движения всего 100 объектов в памяти.
phyrfox
1
«Простая игра, похожая на игру Tower Defense». Хм ... Вы когда-нибудь играли в " Сетка Защиты: Пробуждение" или ее продолжение?
Мейсон Уилер
4
«Никогда не создавайте новый поток для каждого ресурса, это не масштабируется в сети ...» Гм, некоторые очень масштабируемые архитектуры делают именно это!
NPSF3000
2
@BarafuAlbino: Это странная вещь, чтобы сказать. Существует множество веских причин для создания большего количества потоков, чем доступно для ядра. Это компромисс между сложностью, производительностью и т. Д., Как и любое другое дизайнерское решение.
Дитрих Эпп
39

Правило номер один для многопоточности: не используйте его, если вам не нужно распараллеливать на нескольких ядрах процессора для повышения производительности или скорости отклика. Требование «x и y должно происходить одновременно с точки зрения пользователя» еще не является достаточной причиной для использования многопоточности.

Зачем?

Многопоточность это сложно. Вы не можете контролировать выполнение каждого потока, что может привести к невозможности воспроизвести все виды проблем («условия гонки»). Существуют методы, позволяющие избежать этого (блокировки синхронизации, критические секции), но они имеют свои собственные проблемы («тупики»).

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

Даже относительно слабые процессоры смартфонов могут выполнять миллиарды инструкций в секунду. Это означает, что даже когда логика обновления ваших объектов сложна и требует около 1000 инструкций на объект и такт, и вы стремитесь к щедрым 100 тактам в секунду, у вас достаточно процессорной мощности для десятков тысяч объектов. Да, это чрезвычайно упрощенный расчет за пределами конверта, но он дает вам представление.

Кроме того, общепринятым в разработке игр является то, что игровая логика очень редко является узким местом игры. Критически важная часть производительности - это почти всегда графика. Да, даже для 2d игр.

Philipp
источник
1
«Правило номер один для многопоточности таково: не используйте его, если вам не нужно распараллеливаться на нескольких ядрах процессора для повышения производительности или скорости отклика». Может быть верно для разработки игр (но я даже сомневаюсь в этом). При работе с системами реального времени основной причиной добавления потоков является соблюдение сроков и логическая простота.
Сэм
6
@Sam Соблюдение сроков в системах реального времени - это тот случай, когда вам нужна многопоточность для быстрого реагирования. Но даже там логическая простота, которую вы, по-видимому, достигаете благодаря многопоточности, часто бывает коварной, поскольку она создает скрытую сложность в виде тупиков, условий гонки и нехватки ресурсов.
Филипп
К сожалению, много раз я видел, как игровая логика ломала всю игру, если есть проблемы с поиском пути.
Лорен Печтель
1
@LorenPechtel Я тоже это видел. Но обычно это можно было решить, не выполняя ненужных вычислений пути (таких как пересчет каждого отдельного пути на каждом такте), кэшируя часто запрашиваемые пути, используя многоуровневый поиск путей и используя более подходящие алгоритмы поиска путей. Это то, где опытный программист обычно может найти большой потенциал для оптимизации.
Филипп
1
@LorenPechtel Например, в игре Tower Defense вы можете использовать тот факт, что обычно есть только несколько пунктов назначения. Таким образом, вы можете запустить алгоритм Дейкстры для каждого пункта назначения, чтобы вычислить карту направлений, которая направляет все подразделения. Даже в динамичной среде, где вам приходится пересчитывать эти карты каждый кадр, это все равно должно быть доступным.
CodesInChaos
26

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

Например, если у вас 1000 снарядов и 1000 врагов, наивным решением будет просто сравнить их друг с другом.

Это означает, что вы получите p * e = 1,000 * 1,000 = 1,000,000 различных чеков! Это O (n ^ 2).

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

Например, если вы укажете на каждом квадрате сетки, какие враги находятся в этом квадрате, вы можете просмотреть свои 1000 снарядов и просто проверить квадрат на сетке. Теперь вам просто нужно проверить каждый снаряд по квадрату, это O (n). Вместо миллиона проверок в каждом кадре вам нужна только тысяча.

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

Тим Б
источник
1
В качестве альтернативы хранению всей сетки в памяти только для отслеживания нескольких элементов, вы также можете использовать b-деревья, по одному для каждой оси, для быстрого поиска возможных кандидатов на столкновения и т. Д. Некоторые движки даже делают это для вас автоматически «; вы указываете регионы попадания и запрашиваете список коллизий, а библиотека предоставляет его вам. Это одна из многих причин, почему разработчики должны использовать движок вместо того, чтобы писать с нуля (когда это возможно, конечно).
phyrfox
@phyrfox Конечно, есть много разных способов сделать это - в зависимости от вашего варианта использования, который лучше будет существенно различаться.
Тим Б
17

Не создавайте потоки для ресурса / объекта, но для раздела вашей логики программы. Например:

  1. Поток для обновления юнитов и снарядов - логический поток
  2. Поток для рендеринга экрана - поток GUI
  3. Поток для сети (например, мультиплеер) - поток IO

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

Томаш Зато - Восстановить Монику
источник
1
Для новичка я бы не рекомендовал использовать отдельные графические и логические потоки, поскольку, если вы не скопируете необходимые данные, рендеринг игрового состояния требует доступа для чтения к игровому состоянию, поэтому вы не можете изменять игровое состояние при его рисовании.
CodesInChaos
1
Не слишком частое рисование (например, более 50 раз в секунду) очень важно, и этот вопрос касался производительности. Разделить программу - это самое простое, что может принести реальную производительность. Это правда, это требует некоторых знаний о потоках, но приобретение этих знаний стоит.
Томаш Зато - Восстановить Монику
Разделить программу на несколько потоков - одна из самых сложных задач для программиста. Большинство действительно раздражающих ошибок проистекают из многопоточности, и это огромные хлопоты, и большую часть времени они просто не стоят этого. Первое правило: проверьте, есть ли у вас проблемы с производительностью, ТО оптимизируйте. И оптимизируйте прямо там, где есть узкое место. Может быть, один внешний поток для определенного сложного алгоритма. Но даже тогда вы должны подумать, как продвинется ваша игровая логика, когда этот алгоритм занимает 3 секунды, чтобы завершиться ...
Falco
@Falco Вы наблюдаете за долгосрочными преимуществами этой модели - как для проекта, так и для программиста. Ваше утверждение о том, что трудно думать, не может быть решено, это просто мнение. Для меня дизайн GUI гораздо страшнее. Все развитые языки (C ++, Java) имеют довольно четкие модели многопоточности. И если вы действительно не уверены, вы можете использовать модель актера, которая не страдает от ошибок многопоточности новичка. Вы знаете, что есть причина, по которой большинство приложений разработано так, как я предлагал, но не стесняйтесь спорить об этом дальше.
Томаш Зато - Восстановить Монику
4

Даже Space Invaders управляли десятками взаимодействующих объектов. Принимая во внимание, что декодирование одного кадра HD H264 видео включает в себя сотни миллионов арифметических операций. У тебя есть много вычислительной мощности.

Тем не менее, вы все равно можете сделать это медленно, если вы тратите его впустую. Проблема не столько в количестве объектов, сколько в количестве выполненных испытаний на столкновение; простой подход проверки каждого объекта относительно другого объекта возводит в квадрат количество требуемых вычислений. Таким образом, тестирование 1001 объекта на столкновения потребовало бы миллиона сравнений. Часто это решается, например, не проверяя снаряды на предмет столкновения друг с другом.

pjc50
источник
2
Я не уверен, что Space Invaders - лучшее сравнение. Причина, по которой он начинается медленно и ускоряется, когда вы убиваете врагов, не в том, что он спроектирован таким образом, а в том, что аппаратное обеспечение не может обрабатывать столько врагов одновременно. en.wikipedia.org/wiki/Space_Invaders#Hardware
Майк Келлогг
Как насчет того, чтобы каждый объект содержал список всех объектов, которые достаточно близки, чтобы он мог столкнуться с ними в следующую секунду, обновляемый раз в секунду или каждый раз, когда они меняют направление?
Random832
Зависит от того, что вы моделируете. Решения по разделению пространства - это еще один распространенный подход: разделить мир на регионы (например, BSP, который вам, возможно, придется сделать в любом случае для целей рендеринга, или quadtree), тогда вы можете столкнуться только с объектами в одном регионе.
pjc50
3

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

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

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

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

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

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

Я согласен с другими постами, что вам, вероятно, не нужно будет использовать темы в этом конкретном примере, даже если это может быть полезно. Потоки должны быть зарезервированы для случаев, когда у вас чрезмерная загрузка ЦП, которую нельзя оптимизировать другими способами. Это огромная задача, которая повлияет на фундаментальную конструкцию двигателя; это не то, что вы можете использовать после факта.


источник
2

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

Компьютер не обрабатывает сразу все объекты в игре. Он обрабатывает их в последовательности.

Компьютерная игра развивается в дискретных временных шагах. В зависимости от игры и скорости ПК эти шаги обычно составляют 30 или 60 шагов в секунду, или столько / несколько шагов, сколько может рассчитать компьютер.

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

Средний процессор должен быть 2 ГГц или выше, что означает 10 9 тактов в секунду. Если мы вычислим 60 временных шагов в секунду, что листы 10 9 циклов / 60 тактов = 16666666 часов за время шага. С 70 единицами у нас все еще есть приблизительно 2 400 000 тактов на единицу, оставшуюся. Если бы нам пришлось оптимизировать, мы могли бы обновить каждый юнит всего за 240 циклов, в зависимости от сложности логики игры. Как видите, наш компьютер работает примерно в 10000 раз быстрее, чем нужно для этой задачи.

Питер
источник
0

Отказ от ответственности: мой самый любимый тип игр - текстовый, и я пишу это как давний программист старого MUD.

Я думаю, что важный вопрос, который вы должны задать себе, заключается в следующем: вам даже нужны темы? Я понимаю, что графическая игра, вероятно, больше использует МТ, но я думаю, что это также зависит от механики игры. (Возможно, также стоит учесть, что с графическими процессорами, процессорами и всеми другими имеющимися у нас сегодня ресурсами гораздо мощнее, что делает ваши проблемы с ресурсами столь же проблематичными, как вам может показаться; в действительности, 100 объектов практически равны нулю). Это также зависит от того, как вы определяете «все символы одновременно». Вы имеете в виду в одно и то же время? У вас этого не будет, как справедливо указывает Питер, так что все сразу не имеет значения в буквальном смысле; это только кажется таким.

Предполагая, что вы будете использовать потоки: вам определенно не следует рассматривать 100 потоков (и я даже не собираюсь вдаваться в подробности, слишком ли это много для вашего процессора или нет; я имею в виду только сложности и практичность этого).

Но помните: многопоточность не легка (как указывает Филипп) и имеет много проблем. У других гораздо больше опыта (чем у меня), чем у МТ, но я бы сказал, что они тоже предложили бы то же самое (даже если бы они были более способными, чем я, особенно без практики с моей стороны).

Некоторые утверждают, что они не согласны с тем, что потоки не выгодны, а некоторые утверждают, что каждый объект должен иметь поток. Но (и опять же, это весь текст, но даже если вы рассматриваете более одного потока, вам не нужно - и не следует - рассматривать его для каждого объекта), как указывает Филипп, игры имеют тенденцию проходить по спискам. Но все же это не только (как он предлагает, хотя я понимаю, что он реагирует только на ваши параметры стольких объектов) для стольких объектов. В MUD я программист, потому что у нас есть следующее (и это не все действия, которые происходят в режиме реального времени, так что имейте это в виду):

(Количество экземпляров, конечно, различается - все выше и ниже)

Мобильные телефоны (NPC, т.е. неигровые персонажи): 2614; прототипы: 1360 объектов: 4457; прототипов: 2281 номеров: 7983; прототипы: 7983. У каждой комнаты обычно есть свой экземпляр, но у нас также есть динамические комнаты, то есть комнаты внутри комнаты; или комнаты внутри мобильного телефона, например, живот дракона; или комнаты в объектах, например, вы вводите волшебный объект). Имейте в виду, что эти динамические комнаты существуют для каждого объекта / комнаты / мобильного телефона, который фактически определил их. Да, это очень похоже на идею World of Warcraft (я не играю в нее, но мой друг заставлял меня играть на ней, когда я какое-то время работал на машине с Windows), за исключением того, что у нас это было задолго до появления World of Warcraft.

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

Как мы справимся со всем этим без промедления?

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

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

Мы также (моя реализация, основанная на обсуждении между основателем / другим программистом и мной) провели обширное отслеживание связанного списка и проверку достоверности указателей, и у нас более чем достаточно свободных ресурсов, если мы действительно в этом нуждаемся. Все это (за исключением того, что мы расширили мир) существовало годы назад, когда было меньше оперативной памяти, мощности процессора, места на жестком диске и т. Д. И действительно, даже тогда у нас не было проблем. В описанных циклах (сценарии вызывают это так же, как и перезагрузка области / повторное заполнение, как и другие вещи), монстры, объекты (предметы) и другие вещи создаются, освобождаются и так далее. Соединения также принимаются, опрашиваются и все остальное, что вы ожидаете.


источник