Чистое функциональное программирование и игровое состояние


12

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

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

Я мог бы поместить только соответствующую информацию в функции и вернуть действия, которые будут предприняты для данного ввода. И одна функция применяет все действия к игровому состоянию. Но большинству функций нужно много «актуальной» информации. move()нужна позиция объекта, скорость, карта для столкновения, положение всех врагов, текущее здоровье, ... Так что этот подход тоже не работает.

Итак, мой вопрос: как мне справиться с огромным количеством состояний в функциональном языке программирования - особенно для разработки игр?

РЕДАКТИРОВАТЬ: Есть некоторые игровые рамки для создания игр в Clojure. Подход к решению этой проблемы частично состоит в том, чтобы нарезать все объекты в игре как «сущности» и поместить их в огромную сумку. Гигант Основная функция держит экран и сущности и обработки событий ( :on-key-down, :on-init, ...) для этого лиц и запустить главный цикл отображения. Но это не чистое решение, которое я ищу.


Я долго думал об этом; для меня, это не ввод , это единственная проблема, так как вам все равно придется (грубо) передавать те же элементы в функции в нефункциональном программировании. Нет, проблема в выходных данных (и последующих связанных обновлениях). Некоторые из ваших входных параметров должны быть объединены; потому что move()вы, вероятно, должны передавать «текущий» объект (или идентификатор для него), а также мир, в котором он движется, и просто получать текущее положение и скорость ... тогда выход - это весь физический мир, или, по крайней мере, список измененных объектов.
Заводная муза

Преимущество чистого функционала в том, что прототипы функций показывают все зависимости, которые есть у вашей программы.
tp1

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

Ответы:


7

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

Я программист на C # и вообще не имею опыта «функционального программирования». Что это за «монада», о которой я продолжаю слышать, и какая она мне польза?

Эрик Липперт на Монадах

Некоторые вещи, чтобы рассмотреть, хотя:

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

Несколько заключительных мыслей:

  • Параллелизм: В то время как игры действительно использовать его в большой степени, AFAIK большинство из них уже происходит на GPU в любом случае.
  • Безгражданство: мутации состояния являются неотъемлемой частью игр. Попытка избавиться от них может просто усложнить ситуацию без необходимости.
  • Может быть, посмотрите, как функциональный язык F # играет с объектно-ориентированной экосистемой .NET, если вам интересно.

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


Зачем комментировать тему, в которой у вас нет опыта? Мнение, исходящее от людей, запертых в одной парадигме мышления.
Энтони Раймондо

4

Я написал несколько игр, используя F # (мультипарадигма, нечистый, функционально-первый язык), с подходами, варьирующимися от ООП до FRP . Это широкий вопрос, но я сделаю все возможное.

Существует ли общая методика обработки состояния (в целом) в функциональном языке программирования?

Мой предпочтительный способ - иметь неизменный тип, представляющий всю игру State. Затем у меня есть изменяемая ссылка на текущий, Stateкоторый обновляется каждый тик. Это не совсем чисто, но оно ограничивает изменчивость в одном месте.

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

Не правда. Поскольку Stateтип является неизменяемым, вы не можете иметь какой-либо старый компонент, изменяющий мир плохо определенными способами. Это решает самую большую проблему с GameObjectподходом (популяризируемым Unity): трудно контролировать порядок Updateвызовов.

И в отличие от использования глобалов, он легко тестируется на единицах и распараллеливается,

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

Например:

let updateSpaceShip ship = 
  {
    ship with 
      Position = ship.Position + ship.Velocity
  }

let update state = 
  { 
    state with 
      SpaceShips = state.SpaceShips |> map updateSpaceShip 
  }

Здесь updateдействует на все государство, но updateSpaceShipдействует только на человека SpaceShipв изоляции.

Я мог бы поместить только соответствующую информацию в функции и вернуть действия, которые будут предприняты для данного ввода.

Я бы предложил создать Inputтип, содержащий состояния клавиатуры, мыши, геймпада и т. Д. Затем вы можете написать функцию, которая принимает Stateи Inputвозвращает следующее State:

let applyInput input state = 
  // Something

Чтобы дать вам представление о том, как это сочетается, общая игра может выглядеть примерно так:

let mutable state = initialState ()

// Game loop
while true do
  // Apply user input to the world
  let input = collectInput ()

  state <- applyInput input state

  // Game logic
  let dt = computeDeltaTime ()

  state <- update dt state

  // Draw
  render state

Итак, мой вопрос: как мне справиться с огромным количеством состояний в функциональном языке программирования - особенно для разработки игр?

Для простых игр вы можете использовать подход выше (вспомогательные функции). Для чего-то более сложного, вы можете попробовать « линзы ».

Вопреки некоторым комментариям здесь, я настоятельно рекомендую писать игры на (нечистом) функциональном языке программирования или, по крайней мере, попробовать! Я обнаружил, что это делает итерацию быстрее, кодовые базы меньше и ошибки реже.

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

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

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

Также стоит прочитать « Чисто функциональные игры» .


1

То, что вы ищете, это разработка игр FRP.

Некоторые видео введения:

На 100% возможно и предпочтительнее сделать основную игровую логику чисто функциональной, отрасль в целом просто отстает, застряв в одной парадигме мышления.

Это возможно сделать и в Unity.

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

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.