Подтвердить что ты не робот

Lua, игровое состояние и игровой цикл

  • Вызов main.lua script на каждой итерации цикла игры - это хороший или плохой дизайн? Как это влияет на производительность (относительно)?

  • Поддерживайте состояние игры от a. С++ или b. из сценариев Lua или c. от обоих и синхронизировать их?

(Предыдущий вопрос на тему: Lua и С++: разделение обязанностей

(я проголосую за каждый ответ. Лучший ответ будет принят.)

4b9b3361

Ответ 1

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

Я бы поставил состояние игры на С++ и добавил функции в lua, которые могут достигнуть, и изменить состояние. Подход, основанный на событиях, почти лучше, когда регистрация событий должна выполняться в lua (желательно только в начале игры или в конкретных игровых событиях, но не более нескольких раз в минуту), но фактические события должны быть уволены С++. Входы пользователя также являются событиями, и обычно это не происходит каждый кадр (за исключением, может быть, MouseMove, но из-за этого его следует использовать осторожно). То, как вы обрабатываете события ввода пользователя (независимо от того, обрабатываете ли вы все (например, какой ключ был нажат и т.д.) В lua или есть отдельные события для каждой клавиши на клавиатуре (в крайнем случае), зависит от игры, (в игре с поворотом может быть только один обработчик событий для всех событий, RTS должен иметь больше событий, а FPS следует рассматривать с осторожностью (главным образом, потому что перемещение мыши будет выполняться каждый кадр)). что у вас есть отдельные типы событий, тем меньше у вас есть код в lua (что увеличит производительность), но чем сложнее это получить, если "реальное событие", которое вам нужно обработать, фактически вызвано более раздельными "событиями уровня программирования" ( что может фактически снизить производительность, поскольку код lua должен быть более сложным).

В качестве альтернативы, если производительность действительно важна, вы действительно можете улучшить lua VM, добавив к ней новые коды операций (я видел, как некоторые из компаний это делали, но в основном делали более сложную декомпиляцию скомпилированных блоков lua), что на самом деле не сложно. Если у вас есть что-то, что код lua должен делать много раз (например, регистрация событий, запуск событий или изменение состояния игры), вы можете реализовать их в виртуальной машине lua, поэтому вместо нескольких getglobal и setglobal они будут принимать только один или два (например, вы можете сделать код операции SETSTATE с параметром 0-255 и 0-65535, где первый параметр описывает, какое состояние изменить, а второе описывает новое значение Конечно, это работает только в том случае, если у вас максимум 255 событий с максимальным значением 2 ^ 16, но в некоторых случаях этого может быть достаточно. И тот факт, что это только один код операции, означает, что код будет запущен Быстрее). Это также сделает декомпиляцию более сложной, если вы намерены скрыть свой код lua (хотя и не так много для тех, кто знает внутреннюю работу lua). Запуск нескольких опкодов на кадр (около 30-40 вершин) не сильно повлияет на вашу производительность. Но 30-40 опкодов в Lua VM не заставят вас далеко, если вам нужно сделать действительно сложные вещи (простое if-then-else может принимать до 10-20 или более кодов операций в зависимости от выражения).

Ответ 2

Мое основное правило для lua - или любой язык script в игре -

  • Все, что происходит в каждом кадре: С++
  • асинхронные события - ввод пользователя - lua
  • события синхронного игрового движка - lua

В принципе, любой код, вызываемый при > 33-100 Гц (в зависимости от частоты кадров), является С++ Я пытаюсь вызвать script engine < 10Hz.

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

Ответ 3

Я использую Lua в первый раз в игре, над которой я работал. На стороне С++ моего приложения фактически содержатся указатели на экземпляры каждого состояния игры. Некоторые из состояний игры реализованы на С++, а некоторые из них реализованы в Lua (например, состояние игры).

Обновление и основной цикл приложения живут на стороне С++. У меня есть функции, которые позволяют Lua VM добавлять новые игровые состояния в приложение во время выполнения.

У меня еще не было проблем с медленностью, даже работа на аппаратном обеспечении с ограниченными ресурсами (процессор Atom со встроенным видео). Функции Lua вызывают каждый кадр. Самая дорогостоящая (с точки зрения времени) работа в моем приложении - рендеринг.

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

Изменить: я использую Luabind, который, как я прочитал, работает медленнее, чем другие рамки привязки, и, конечно же, Lua C API.

Ответ 4

Сценарии IMHO Lua предназначены для конкретного поведения, это определенно будет ухудшать производительность, если вы вызываете Lua script 60 раз в секунду.

Сценарии Lua часто разделяют такие вещи, как "Поведение" и конкретные события из вашей логики Game Engine (GUI, Items, Dialogs, события игрового движка и т.д.). Хорошее использование Lua, например, было бы при запуске взрыва (частичный FX), если игровой персонаж будет куда-то ходить, жестко кодирование вывода этого события в вашем движке было бы очень уродливым выбором. Хотя, приведя к срабатыванию двигателя правильный script, будет лучшим выбором, отделив от вашего конкретного поведения определенное поведение.

Я бы порекомендовал, чтобы попытаться сохранить состояние игры в одной части, вместо того, чтобы масштабировать уровень сложности состояний, синхронизированных в двух местах (Lua и Engine), добавьте к ним потоки, и вы получите очень уродливый беспорядок. Будь проще. (В моих проектах я в основном сохраняю Game State на С++)

Удачи в вашей игре!

Ответ 5

  • Вероятно, вы не хотите выполнять всю Lua script на каждой итерации кадра, потому что любая достаточно сложная игра будет иметь несколько игровых объектов с их собственным поведением. Другими словами, преимущества Lua теряются, если у вас нет нескольких крошечных скриптов, которые обрабатывают определенную часть поведения более крупной игры. Вы можете использовать функцию lua_call для вызова любой соответствующей процедуры lua в script, а не только для всего файла.

  • Здесь нет идеального ответа, но подавляющее большинство вашего игрового состояния традиционно хранится в игровом движке (т.е. на С++). Вы раскрываете Lua только для Lua, чтобы принять решение, которое вы назначили Lua.

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

Ответ 6

Мне не нравится С++. Но мне нравятся игры.

Мой подход может быть немного атипичным: я делаю все, что в Lua, и только абсолютный минимум в С++. Игровой цикл, сущности и т.д. Все сделано в Lua. У меня даже реализована реализация QuadTree в Lua. С++ обрабатывает графическую и файловую систему, а также взаимодействует с внешними библиотеками.

Это не машинное решение, а программирующее; Я быстрее выдаю код в Lua, чем в С++. Поэтому я трачу программы программистов на новые функции, а не на экономию компьютерных циклов. Мои целевые машины (любой ноутбук за последние 3 года) могут легко справиться с этим количеством Lua.

Lua на удивление низкий уровень следа (посмотрите luaJIT, если вы этого не знаете).

Это говорит о том, что если я когда-нибудь найду узкое место (еще нет), я буду профилировать игру, чтобы найти медленную часть, и я переведу эту часть на С++... только если смогу " t найти путь вокруг него с помощью Lua.

Ответ 7

О производительности 1: если main.lua не изменяется, загрузите его один раз с помощью lua_loadfile или loadfile, сохраните ссылку на возвращаемую функцию и затем вызовите ее, когда это необходимо.

Ответ 8

Большая часть производительности будет потеряна благодаря связыванию между Lua и С++. На самом деле вызов функции должен быть завернут, а затем снова перевернут и, как правило, пару раз. Чистый Lua или чистый код С++ обычно быстрее, чем смешанный код (для небольших операций).

Сказав это, я лично не видел, чтобы какая-либо сильная производительность приводила к созданию Lua script каждого кадра.

Обычно сценарий хорош на высоком уровне. Lua используется в известных играх для Ботов (Quake 3) и для пользовательского интерфейса (World of Warcraft). Используется на высоких потоках Lua на высоком уровне. Купоны могут экономить много (по сравнению с реальными потоками). Например, для запуска некоторого кода Lua только время от времени.

Ответ 9

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

Обратите внимание, что я собираюсь поговорить конкретно о LuaJIT, потому что вы захотите использовать LuaJIT. Это plug-and-play, действительно, поэтому, если вы можете вставлять Lua, вы можете вставлять LuaJIT. Вам понадобится это, если не для дополнительной скорости, то для модуля автоматической обработки внешних функций (требуется "ffi" ), который позволит вам называть ваш собственный код непосредственно из Lua без необходимости касаться Lua C API (95 % + случаев).

  • Это отлично отлично, чтобы называть Lua на 60hz (я называю это 90hz в VR..). Уловка заключается в том, что вам нужно быть осторожным, чтобы делать это правильно. Как уже упоминалось, важно, чтобы вы загрузили script только один раз. Затем вы можете использовать C API для доступа к функциям, определенным вами в script, или для запуска самого script как функции. Я рекомендую первое: для относительно простой игры вы можете обойтись с помощью таких функций, как onUpdate (dt), onRender(), onKeyPressed (key), onMouseMoved (dx, dy) и т.д. Вы можете позвонить им в соответствующее время из вашего основного цикла на С++. Кроме того, вы можете фактически использовать весь свой основной цикл в Lua и вместо этого вызывать свой код на С++ для критически важных процедур (я делаю это). Это особенно легко сделать с LFI для LuaJIT.

  • Это очень трудный вопрос. Это будет зависеть от ваших потребностей. Можете ли вы легко сбить состояние игры? Отлично, поставьте его на С++ и получите доступ от LuaJIT FFI. Не знаете, что все будет в состоянии игры/нравится, чтобы иметь возможность прототипа быстро? Нет ничего плохого в том, чтобы держать его в Луа. То есть, пока вы не начнете говорить о сложной игре с 1000 объектами, каждая из которых содержит нетривиальное состояние. В этом случае гибрид - это путь, но выяснение того, как разделить состояние между С++ и Lua, и как воинственное состояние между двумя (особенно в первичном критическом режиме) - это что-то вроде искусства. Дайте мне знать, если вы придумаете пуленепробиваемую технику. Как и во всем остальном, общее эмпирическое правило: данные, которые проходят через критически важные для работы пути, должны быть на собственной стороне. Например, если ваши объекты имеют позиции и скорости, которые вы обновляете каждый кадр, и у вас есть тысячи указанных объектов, вам нужно сделать это на С++. Тем не менее, вы можете уйти с разбиением "инвентаря" поверх этих объектов с помощью Lua (инвентарь не нуждается в обновлении для каждого кадра).

Теперь, еще несколько заметок, которые я бы выбрал как общие, так и в ответ на некоторые другие ответы.

  • Подходы, основанные на событиях, в целом критически важны для производительности любой игры, но это вдвойне подходит для систем, написанных в Lua. Я сказал, что прекрасно позвонить Lua @60hz. Но это не совсем нормально, когда нужно запускать жесткие петли над множеством игровых объектов в каждом кадре в упомянутом Lua. Вы можете уйти с расточительным вызовом update() для всего, что есть в юниверсе на С++ (хотя вы этого не должны), но это делается в Lua, и он начнет слишком быстро терять эти драгоценные миллисекунды. Вместо этого, как говорили другие, вам нужно думать о логике Lua как "реактивной" - обычно это означает обработку события. Например, не проверяйте, что один объект находится в диапазоне от другого каждого кадра в Lua (я имею в виду, что это нормально для одного объекта, но в целом, когда вы увеличиваете свою игру, вам не нужно думать так). Вместо этого сообщите, чтобы ваш движок С++ уведомил вас, когда два объекта попадают на определенное расстояние друг от друга. Таким образом, Lua становится высокоуровневым контроллером игровой логики, отправляет команды высокого уровня и реагирует на события высокого уровня, не выполняя низкоуровневую математическую разметку тривиальной игровой логики.

  • Будьте осторожны, советуя, что "смешанный код" медленный. API Lua C легкий и быстрый. Функции Wrapping для использования с Lua в худшем случае довольно просты (и если вам понадобится время, чтобы понять, как Lua взаимодействует с C в виртуальном стеке и т.д., Вы заметите, что он был разработан специально для минимизации накладных расходов), и в лучшем случае, тривиальным (без необходимости обертывания) и 100% в качестве реальных, как нативные вызовы (спасибо, LuaJIT FFI!). В большинстве случаев я считаю, что тщательная смесь Lua и C (через LJ FFI) превосходит чистую Lua. Даже векторные операции, которые, как я считаю, в руководствах LuaJIT упоминаются где-то в качестве примера, где код должен храниться в Lua, я обнаружил, что быстрее, когда я выполняю вызовы Lua- > C. В конечном счете, не доверяйте никому или чему-либо, кроме своих собственных (тщательных) измерений производительности, когда дело доходит до фрагментов кода, чувствительных к производительности.

  • Большинство игр с небольшим количеством игр могут полностью уйти с игровым состоянием, контуром ядра, обработкой ввода и т.д., полностью выполненными в Lua и работающими под LuaJIT. Если вы строите маленькую игру, рассмотрите подход "перейти на С++ по мере необходимости" (ленивая компиляция!). Напишите в Lua, когда вы идентифицируете четкое узкое место (и измерили его, чтобы убедиться, что это преступник), переместите это бит на С++ и быть на вашем пути. Если вы пишете большую игру с 1000 сложными объектами со сложной логикой, расширенным рендерингом и т.д., Тогда вы выиграете от более масштабного проектирования.

Желаем удачи:)