Каковы типичные подводные камни при написании игр на управляемом языке, таком как C #? [закрыто]

66

С какими подводными камнями вы столкнулись при написании игр для ПК на управляемом языке, таком как C #, и как вы их решили?

Майкл Клемент
источник
Этот вопрос лучше задать при переполнении стека, поскольку в нем мало что характерного для игр.
Крис Гарретт
31
@Chris: категорически не согласен: вопрос конкретно касается игр! Проблемы, возникающие при запуске обновления каждые 16 мс, сильно отличаются от тех, которые возникают в большинстве настольных приложений.
Эндрю Рассел
Вопрос довольно неясен. Java и C # достаточно различаются для того, чтобы только оба общих совета были применимы к обоим. Все ответы до сих пор были C #. Также не указана целевая платформа - полезные советы могут отличаться в зависимости от устройства (например, программирование для мобильного телефона, Zune, Xbox, отличается от программирования для ПК). Это даже был достаточно широкий вопрос, на который кто-то ответил, что сами управляемые языки являются «ловушкой».
paulecoyote
@paulecoyote: я перешел на вопрос, чтобы спросить только о C #. Кроме того, поскольку здесь не упоминается конкретная платформа, речь идет о ПК.
Майкл Клемент
@ Майкл, предполагая, что платформа является опасным допущением, поскольку они сильно отличаются друг от друга по реализации, было бы неплохо упомянуть Windows специально и отбросить «like» и «Java» в целом.
paulecoyote

Ответы:

69

Я не очень разбираюсь в Java, так что это с точки зрения разработчика .net.

Самым большим на сегодняшний день является мусор. Сборщик мусора .NET в Windows делает фантастическую работу, и вы можете обойтись без присмотра за детьми по большей части. На Xbox / Windows Phone 7 это другое дело. Если каждые несколько кадров появляются остановки, сбор мусора может вызывать проблемы. В данный момент он срабатывает после каждого выделения 1 МБ.

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

  • Нарисуйте содержимое GC.GetTotalMemory()на экране. Это дает вам приблизительное количество выделенных байтов в вашей игре. Если он едва двигается, у тебя все хорошо. Если все идет быстро, у вас есть проблемы.
  • Попробуйте выделить все ваши объекты кучи заранее. Если вы не распределяете все до начала игры, каждый раз, когда вы набираете миллион мегапикселей, вы останавливаетесь. Нет выделений, нет коллекций. Так просто, как, что.
  • После загрузки звоните GC.Collect(). Если вы знаете, что большинство ваших больших выделений не в порядке, приятно сообщить об этом системе.
  • НЕ ЗВОНИТЕ GC.Collect()каждый кадр. Может показаться, что это хорошая идея - держать мусор и так далее, но помните, что только сбор мусора - сбор мусора.
  • Ищите, откуда ваш мусор. Есть некоторые общие причины , как конкатенации строк вместо использования StringBuilder(будьте осторожны, StringBuilderэто не волшебная палочка, и все еще могут привести к распределению! . Это означает , что простые операции , как добавление номера в конец строки может создавать удивительные количества мусора) или использование foreachциклов над коллекциями, использующими IEnumerableинтерфейс, также может создавать мусор без вашего ведома (например, foreach (EffectPass pass in effect.CurrentTechnique.Passes)это распространенный случай)
  • Используйте такие инструменты, как профилировщик памяти CLR, чтобы выяснить, где выделяется память. Есть множество учебных пособий о том, как использовать этот инструмент.
  • Когда вы знаете, где вы располагаете во время игры, посмотрите, можете ли вы использовать такие приемы, как объединение объектов, чтобы уменьшить это количество.
  • Если ничего не помогает, ваши коллекции будут работать быстрее! GC на компактной платформе следует каждой ссылке в вашем коде, чтобы выяснить, какие объекты больше не используются. Рефакторинг вашего кода используйте меньше ссылок!
  • Не забудьте использовать IDisposableв классах, которые содержат неуправляемые ресурсы. Вы можете использовать их для очистки памяти, которую GC не может освободить самостоятельно.

Другая вещь, о которой нужно думать, это производительность с плавающей запятой. Хотя .NET JITer выполняет значительную часть оптимизаций для конкретного процессора, он не может использовать SSE или любые другие наборы команд SIMD для ускорения математики с плавающей запятой. Это может вызвать довольно большую разницу в скорости между C ++ и C # для игр. Если вы используете моно, у них есть некоторые специальные математические библиотеки SIMD, которыми вы можете воспользоваться.

Cubed2D
источник
Согласитесь полностью. Реализации сборщика мусора на «меньших» платформах кажутся полным мусором.
Криск
Хороший пост, однако, что касается вашего утверждения о «распределении объектов кучи заранее», я рекомендую прочитать «Правду о типах значений»
Джастин,
Замечательный момент, когда вы оптимизируете код, действительно стоит понимать платформу, для которой вы пытаетесь оптимизировать. На самом деле это не был комментарий о типах значений / ссылочных типах, так как любой долгоживущий объект вряд ли будет в стеке. На самом деле важно убедиться, что вы получаете как можно больше своих распределений во время загрузки, чтобы вы не достигли этого магического барьера в 1 Мб во время игры. Все реализации .net, на которые нацелена xna, также имеют четкую гарантию, объекты, расположенные близко друг к другу во времени, будут близки в пространстве, что может быть полезно для перфектов.
Cubed2D
Также похоже, что я забыл упомянуть, что текущий компактный каркас на xbox имеет аллергию на встроенные вызовы методов. Оставьте это для своих наихудших сценариев, хотя использование ref-версий математических методов выглядит достаточно уродливо!
Cubed2D
10

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

Для C # использование объектов-значений и выражение «using» могут снизить давление со стороны GC.

Матиас Вальденегро
источник
3
Кроме того, вы можете указать сборщику мусора запускаться явно, если вы только что завершили тяжелый цикл выделения / освобождения или обнаружили, что у вас есть свободные циклы.
BarrettJ
16
usingУтверждение не имеет ничего общего со сбором мусора! Он предназначен для IDisposableобъектов, предназначенных для освобождения неуправляемых ресурсов (то есть тех, которые не обрабатываются сборщиком мусора ).
Эндрю Рассел
9

Я бы сказал, что самой большой проблемой, с которой я столкнулся при написании игр на C #, было отсутствие приличных библиотек. Больше всего я обнаружил либо прямые порты, но неполные, либо обертки над библиотекой C ++, которые влекут за собой значительное снижение производительности при маршалинге. (Я говорю конкретно о MOgre и Axiom для библиотеки OGRE и BulletSharp для библиотеки физики Bullet)

Управляемые языки (в отличие от Interpreted - ни Java, ни C # на самом деле больше не интерпретируются) могут быть такими же быстрыми, как и родные языки, если вы хорошо понимаете, что на самом деле делает их медленными (маршалинг, сборка мусора). Реальная проблема, я думаю, в том, что разработчики библиотек еще не осознали этого.

Karantza
источник
Это хороший момент, когда C # и Java управляются, а не интерпретируются ... отредактировал мой вопрос, чтобы сделать его более точным :)
Майкл Клемент,
2
Маршаллинг в целом - это узкое место в производительности, но он также помогает осознавать блиц-типы - типы, которые могут быть сопоставлены непосредственно с неуправляемой памятью без значительного снижения производительности. msdn.microsoft.com/en-us/library/75dwhxf7.aspx
Шон Эдвардс
8

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

необычайно щедрый
источник
4

C # и Java не интерпретируются. Они скомпилированы в промежуточный байт-код, который после JIT становится таким же быстрым, как и нативный код (или достаточно близким, чтобы быть незначительным).

Самая большая ловушка, которую я обнаружил, заключается в освобождении ресурсов, которые напрямую влияют на пользовательский опыт. Эти языки не поддерживают автоматически детерминированную финализацию, как это делает C ++, что, если вы не ожидаете, может привести к таким вещам, как сетки, плавающие по сцене после того, как вы думали, что они были уничтожены. (C # выполняет детерминированную финализацию через IDisposable , я не уверен, что делает Java.)

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

Шон Эдвардс
источник
Да, спасибо за примечание, уже исправили интерпретируемую / управляемую вещь;) Кроме того, хороший момент с сетками, плавающими по сцене. Не думал об этом, когда думал о проблемах GC ...
Майкл Клемент
1
IDisposableпозволяет детерминированную очистку критичных ко времени и неуправляемых ресурсов, но не влияет напрямую на финализацию или сборщик мусора.
Сэм Харвелл
4

Ни Java, ни C # не интерпретируются. Оба они скомпилированы в машинный код.

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

GMan
источник
С сборкой мусора можно справиться, по крайней мере, в C #, достаточно хорошо, так что я бы не сказал, что это нарушитель соглашения. При правильной организации потоков и осведомленности о состоянии программы вы можете избежать проблем с производительностью. Это еще одна вещь, о которой вам нужно подумать, и она делает управляемый язык немного менее управляемым.
Каранца
4
Стоит отметить, что в .NET 4 сборщик мусора поддерживает фоновый сбор для всех трех поколений объектов. Это должно эффективно минимизировать влияние сбора мусора на производительность в играх. Соответствующая ссылка: geekswithblogs.net/sdorman/archive/2008/11/07/…
Майк Штробель
Сборщики мусора развиваются, и, как отметил Майк Штробель, некоторые уже находятся в производстве, что почти устраняет эту ловушку.
Сэм Харвелл
2
Ни C #, ни Java не компилируются в машинный код. C # компилируется в MSIL и Java для байт-кода. Сборка мусора не даст никаких «длинных» пауз, но может дать «икоту».
Cloudanger
2

Одна большая ловушка, которую я вижу в создании игр с такими языками (или с использованием таких инструментов, как XNA, TorqueX engine и т. Д.), Заключается в том, что вам будет сложно найти команду хороших людей, обладающих опытом, необходимым для создания игры, эквивалентной тому, что было бы довольно легко найти людей для C ++ и OpenGL / DirectX.

Индустрия игровых разработок все еще очень сильно погружена в C ++, так как большинство инструментов и конвейеров, которые используются для выталкивания больших или просто отточенных маленьких игр, были написаны на C ++, и, насколько я знаю, ВСЕ официальные наборы разработчика вы можете get для XBox, PS3 и Wii выпускаются только с совместимостью для C ++ (в настоящее время набор инструментов XBox может быть более управляемым, кто-нибудь знает больше?)

Если вы хотите разрабатывать игры для консолей прямо сейчас, вы в значительной степени получаете XNA и C # на XBox и только в боковой части библиотеки игр под названием XBox Live Indie Games. Тех, кто выигрывает конкурсы и т. Д., Выбирают для переноса своей игры на настоящую XBox Live Arcade. Кроме этого плана по созданию игры для ПК.

mikeschuld
источник