Quais são os desafios e benefícios de escrever jogos com uma linguagem funcional?

81

Embora eu saiba que linguagens funcionais não são as mais usadas para escrever jogos, há muitos benefícios associados a elas que parecem interessantes em qualquer contexto de programação. Especialmente a facilidade de paralelização que eu acho que poderia ser muito útil, pois o foco está se movendo em direção a mais e mais processadores.

Além disso, com o F # como um novo membro da família .NET, ele pode ser usado diretamente com o XNA, por exemplo, o que diminui bastante o limite, em oposição a LISP, Haskell, Erlang, etc.

Se alguém tem experiência em escrever jogos com código funcional, quais foram os pontos positivos e negativos? Para que era adequado, o que não?

Edit: Achando difícil decidir que existe uma única resposta boa para isso, por isso é provavelmente mais adequado como um post no wiki da comunidade.

McMuttons
fonte
3
com um dos meus jogos favoritos de todos os tempos (Rogue) sendo escrito em LISP, essa pergunta (e seu potencial de resposta) é muito interessante para mim.
Justin L.
Não sabia que Rogue estava escrito em LISP, mas acho isso interessante. Jogo clássico. :)
McMuttons
2
Não tenho certeza do que você está falando. Rogue foi escrito em C. roguebasin.roguelikedevelopment.org/index.php?title=Rogue
Mason Wheeler
2
Tenho certeza que você está certo sobre o Rogue original estar em C, desde que foi escrito no Unix, mas certamente existem vários clones do Rogue no Lisp: cl-user.net/asp/root-dir (não o link direto do jogo , tinha tantos caracteres especiais que parece ter um vínculo quebrado).
Ciclope

Respostas:

57

Atualmente, estou trabalhando em um jogo em Haskell. Não posso falar de programação funcional em geral, mas de Haskell especificamente:

O bom

Essas são as coisas impressionantes que fazem Haskell realmente se destacar.

  • Pureza significa que o raciocínio sobre seu código é muito mais fácil. Você não precisa se preocupar com o valor "atual" de uma variável ou se o uso de uma determinada função entrará em conflito com o seu estado global de alguma forma. Isso também significa que o paralelismo e a simultaneidade são muito mais fáceis de trabalhar (e, em muitos casos, triviais). Essas coisas podem ser uma dádiva de Deus para os desenvolvedores de jogos.
  • Haskell torna muito fácil escrever em um nível muito alto usando abstrações de sua própria criação. Geralmente, é mais fácil escrever código muito genérico do que o código especializado no Haskell, e os benefícios disso se manifestam em menos tempo de desenvolvimento e compreensão mais fácil do código. É mais verdade em Haskell do que em qualquer outro idioma que eu sei que usar uma interface é como usar uma linguagem totalmente nova (mantendo os benefícios do restante do ecossistema Haskell). O que a maioria dos idiomas chama de recursos de idioma, Haskell chama de biblioteca e não parece forçado. Se você se deparar com um raciocínio sobre seu jogo no psuedocode, provavelmente poderá escrever uma interface simples e torná-la um código real, e geralmente vale a pena fazê-lo.
  • Isso pode entrar em conflito com outras respostas, mas Haskell é melhor no tratamento de códigos imperativos do que em qualquer outro idioma que eu conheça. As restrições de pureza significam que Haskell tem uma separação entre os conceitos de "avaliação" (redução de expressões) e "execução" (execução de efeitos colaterais). Você pode controlar quando e como os efeitos colaterais são executados com uma facilidade incomparável com as chamadas linguagens "imperativas". Apesar do que eu disse sobre pureza anteriormente, algumas ligações C ainda são muito imperativas (OpenGL, estou olhando para você); portanto, esse controle refinado sobre o código imperativo é muito bem-vindo. Mesmo os programadores C mais experientes provavelmente apreciarão quando se acostumarem.
  • O GHC gera binários bem rápidos. Eles não são tão rápidos quanto os binários gerados a partir de compiladores C comuns, mas estão dentro de uma ordem de magnitude e são muito mais rápidos do que o que você alcançaria com uma linguagem interpretada. Mesmo que você não consiga escrever seu jogo em outras linguagens de alto nível, como o Python, porque seria muito lento, há uma boa chance de fazê-lo em Haskell.
  • Apesar de não haver muitas bibliotecas relacionadas a jogos no Haskell, conforme observado em "The Bad" abaixo, a interface de função externa do Haskell é a mais fácil que já usei. Você pode vincular a C escrevendo quase exclusivamente em Haskell.

O mal

Estas são coisas que não são boas, mas pode ser contornado sem muito muito esforço.

  • O tempo e o esforço necessários para aprender o Haskell podem ser razoavelmente altos se você estiver muito acostumado a trabalhar com idiomas não funcionais. A linguagem em si não é muito difícil, mas quando você considera extensões de linguagem comuns e a enorme quantidade de bibliotecas (lembre-se, muitas coisas que você pode considerar que os recursos de linguagem em outras línguas são bibliotecas em Haskell), parece muito mais ameaçador. Na minha opinião, vale a pena, mas outros podem discordar.
  • Algumas pessoas reclamam muito da preguiça. Se você souber o que está fazendo, não causará vazamentos de espaço difíceis de rastrear (eu não criei um vazamento de espaço há alguns anos), mas os iniciantes têm muito mais problemas com isso. Seria tolice da minha parte derrubar essas pessoas e afirmar que isso não é um problema, mas vou afirmar que é um problema que é facilmente resolvido com a experiência.
  • Não existem muitas grandes bibliotecas para a criação de jogos no Haskell, e não há muitos criadores de jogos nele, por isso é difícil encontrar recursos e ajuda nesse assunto.

O feio

São coisas que exigiriam um esforço considerável para serem superadas.

  • Se você estiver escrevendo um jogo que consome muitos recursos, o coletor de lixo do GHC pode te morder bastante. É um GC geracional e de parada mundial, portanto, de vez em quando, você pode perder alguns quadros. Para alguns jogos, tudo bem, mas para outros é um grande problema. Eu e alguns outros desenvolvedores de jogos que usam Haskell gostariamos de ver um GC em tempo real implementado para o GHC, mas ainda não estou gastando nenhum esforço nisso. Atualmente, não estou preocupado em contornar isso (e ainda não vi nenhum quadro descartado), mas pelo menos uma outra equipe recorreu a colocar seu loop de renderização principal em C.
Jake McArthur
fonte
2
O que você acha do FRP?
Wei Hu
4
@Wei Hu: Sou um grande fã da ideia de FRP e a pesquisei bastante extensivamente, incluindo a criação de algumas semânticas diferentes e a elaboração de algumas implementações. As melhores implementações ainda possuem graves erros semânticos e / ou de implementação. Eu diria que eles são viáveis ​​para o desenvolvimento de jogos, mas não com muitas vantagens (ainda) em relação às abordagens tradicionais.
Jake McArthur
1
Um usuário anônimo tentou editar esta resposta limpando a parte "feia". "O coletor de lixo em Haskell agora é simultâneo, paralelo e geracional a partir de 2011 ( ghc.haskell.org/trac/ghc/blog/new-gc-preview )"
Jari Komppa
1
Infelizmente, isso não é verdade. O GC concorrente foi encontrado para ser muito complicado para justificar a pequena melhoria global, e não foi fundida em.
Jake McArthur
1
@ Hi-Angel Existe um back-end antigo do GHC que gerou C, mas não é diferente do gerador de código nativo ou do back-end do LLVM. Ele ainda requer o tempo de execução do GHC. Além disso, não há nada de especial em um back-end em C que facilitaria evitar um coletor de lixo. Se fosse tão fácil e barato eliminar o GC, os outros back-ends provavelmente o fariam também.
Jake McArthur
19

Passei o último ano desenvolvendo um mecanismo de jogo comercial em Haskell e, para nós, a experiência foi extremamente positiva. Nosso mundo dos jogos é complexo, e Haskell facilitou a modelagem do processo de conversão de um formato de editor para um formato de mecanismo de jogo. Eu odiaria pensar como seria esse código em uma linguagem imperativa.

Ocasionalmente, vazamentos de espaço surgem e, embora tenham causado alguns problemas, no esquema geral, tem sido uma quantidade pequena (por exemplo, em comparação com a localização de impasses em projetos Java de tamanho semelhante) e, uma vez corrigidos , eles ficaram fixos.

Estamos usando o FRP semelhante ao Yampa, e certamente há uma curva de aprendizado associada a ele, mas, uma vez terminada, a experiência é muito positiva. As bibliotecas não foram um problema para nós - tudo o que precisávamos estava disponível. Atrasos na coleta de lixo foram um problema específico, pois se trata de uma plataforma incorporada. Usamos C ++ para gerenciar a animação. O desempenho também foi um problema, por ser uma plataforma incorporada (= processador lento). Fizemos C e também estamos olhando para as tecnologias Haskell emergentes como acelerar. O animador C ++ foi uma decisão de design desde o início e os locais onde o código é muito lento são apenas áreas muito pequenas. A longo prazo, queremos traduzir todo o nosso C para Haskell.

Haskell facilitou um trabalho difícil, e todas as dificuldades que acabei de mencionar foram pequenas em comparação com a grande quantidade de código complexo que produzimos que é limpo, sustentável e praticamente inquebrável. O paralelismo será um problema no desenvolvimento de jogos muito em breve, e estamos extremamente bem posicionados para lidar com isso. Parte do que eu disse pode não se aplicar a pequenos projetos, porque estamos nisso a longo prazo; portanto, os custos de inicialização, como curvas de aprendizado, suporte à biblioteca etc., são muito menos problemáticos.

Stephen Blackheath
fonte
7
Já se aproximam cinco anos desde que você postou esta resposta; você estaria disposto a atualizá-lo? Como o mecanismo de jogo comercial funcionou? Que peças provaram ser valiosas? Você conseguiu tirar proveito do paralelismo como esperava?
Levi Morrison
17

Dave menciona alguns pontos excelentes, embora eu deva ressaltar que Haskell resolveu os dois problemas. A apatridia pode ser contornada usando a mônada do estado (EDIT: na verdade não - veja abaixo para obter detalhes) , e o seqüenciamento pode ser tratado usando a mônada de E / S (EDIT: ou qualquer outra mônada, para esse assunto ...) .

Os desafios que você terá (e que eu tentei aprender programação de jogos e Haskell) são mais nesse sentido. (Estes são todos específicos de Haskell, já que ainda não me aprofundou em nenhuma outra linguagem FP).

  • Curva de aprendizado do FP: O FP requer uma mudança completa de mentalidade da programação iterativa. Aprender a pensar em termos de mapas e dobras em vez de loops requer um treino mental, se você não estiver acostumado.
  • Curva de aprendizado de Mathy: Haskell é abençoado e amaldiçoado pela sofisticação matemática de seus designers, porque depois de aprender o básico do FP, você fica preso com mônadas, morfismos, flechas e uma série de outras considerações que, embora não sejam difíceis, por si só, são muito abstratos.
  • Mônadas: esses filhotes não são particularmente difíceis de entender, especialmente se você aceitar a afirmação de Eric Raymond de que eles são "um truque para transformar a composição de funções em uma maneira de forçar o seqüenciamento de chamadas de funções". Infelizmente (embora isso esteja melhorando à medida que Haskell ganha popularidade), muitas explicações sobre mônadas apontam para a cabeça da maioria dos programadores ("eles são monóides na categoria de endofuncionadores!") Ou em uma analogia completamente inútil (por exemplo, , O exemplo angustiante e preciso de Brent Yorgey, " mônadas são como burritos !" Você acha que ele está brincando? Que ninguém jamais poderia sugerir uma analogia tão pateta em um tutorial? Pense novamente .)
  • Ecossistema: se você conseguiu superar todos os outros obstáculos na estrada, ficará de frente para a realidade prática do dia-a-dia de trabalhar com uma linguagem ainda amplamente experimental. Deus te ajude quando você chegar aqui. Aqui é onde eu comecei a tocar seriamente para Scala, ou F #, ou alguma outra linguagem em que haja pelo menos alguma garantia de interoperabilidade com outras bibliotecas. Era uma vez, pedi ao Haskell para vincular e carregar as bibliotecas OpenGL no Windows. Isso me fez sentir muito bem. Em seguida, as ligações do OpenGL foram relançadas e quebraram tudo o que eu havia feito. Essa é a vida na terra Haskell. Honestamente, pode ser uma dor. Você quer um invólucro para o mecanismo Bullet? Está no Hackage - como abandonwarea partir de novembro do ano passado. Você gosta do Ogre3d? A invasão possui ligações apenas a um subconjunto da biblioteca . Resumindo, se você seguir um idioma fora do caminho comum para o desenvolvimento de jogos, gastará muito mais tempo trabalhando no encanamento básico do que faria se seguisse o rebanho e continuasse com o C ++.

O outro lado disso é que as coisas estão melhorando rapidamente. E tudo realmente depende do que você deseja da experiência. Você quer criar um jogo e colocá-lo em seu site para buscar fama e fortuna? Fique com C ++ ou Python. Mas se você quiser aprender algo novo que exigirá que você inove seus processos, tente uma linguagem funcional. Apenas tenha muita paciência consigo mesmo enquanto estiver aprendendo.

Antti Salonen tem um blog interessante detalhando seu caso de novo com a programação de jogos Haskell. Vale a pena ler.

Editar (um ano depois): Agora que estudei mais a mônada do estado, percebo que não é uma solução particularmente boa para o estado que se destina a persistir fora de uma função específica. As soluções reais para apatridia são encontradas em Haskell em IOVar, ST, MVar (para segurança do encadeamento) ou através de algo como o Yampa, que usa Arrows e FRP para gerenciar o estado interno que, no entanto, está oculto ao desenvolvedor. (Esta lista está em ordem de dificuldade, embora os três primeiros não sejam particularmente difíceis depois que você entender as mônadas.)

rtperson
fonte
1
Alguns bons pensamentos por lá, mesmo que sejam bastante específicos de Haskell, acho que são pensamentos que se aplicam à maioria das línguas periféricas. Como você mencionou brevemente, acho que é aqui que idiomas como o F # têm potencial para brilhar. Manipulação de estado / UI com C #, por exemplo, e depois ter módulos lógicos em F # para algoritmos como potencialmente constatação caminho, AI, etc.
McMuttons
5

Caml objetivo!

O uso de linguagens funcionais pode ser uma grande vantagem na maioria dos tipos de desenvolvimento de software, principalmente porque eles reduzem consideravelmente o tempo de desenvolvimento. Percebo um grande potencial para escrever um servidor back-end em um jogo, ou as camadas de IA e lógica no cliente, em uma linguagem funcional. E como todos sabem, o LISP foi usado para scripts de NPC.

Se eu tentasse escrever o frontend de um jogo em uma linguagem funcional, eu definitivamente optaria pelo Objective Caml , que é uma linguagem híbrida. É um descendente de ML, e permite misturar estilos iterativos e funcionais, possui um sistema objetivo com objetos com estado. (Caml é a linguagem na qual o F # é modelado.)

As ligações do OpenGL parecem funcionar perfeitamente e as pessoas criam jogos no OCaml há muito tempo. O idioma pode ser compilado e é potencialmente muito rápido (ele conquistou o C em alguns benchmarks há muito tempo, não como as coisas estão agora).

Há também toneladas de bibliotecas. Tudo, desde ciência da computação a encadernações, a todos os tipos de bibliotecas.

Felixyz
fonte
4

Não é realmente uma resposta para nada, mas sinto que uma discussão sobre programação de jogos em linguagens funcionais está incompleta sem mencionar a Programação Reativa Funcional (FRP) - http://www.haskell.org/frp/ - e sua implementação mais comum ( Eu acho?), YAMPA - http://www.haskell.org/yampa/ .

user744
fonte
3

Quanto aos benefícios, confira a biblioteca de jogos limpos (http://cleangl.sourceforge.net/) e o jogo de plataformas. Depois de entender a sintaxe (encorajo você a tentar), você verá a pura elegância das construções e a proximidade com a qual a fonte é mapeada para os conceitos que estão expressando. Como um bônus adicional, a abstração do estado e a necessidade de transmitir explicitamente o estado tornam seu código muito mais amigável para vários núcleos.

Por outro lado, requer uma grande mudança de paradigma. Muito do que você costuma fazer sem pensar nisso de repente não existe mais e você ficará intrigado sobre como resolvê-lo, simplesmente porque está pensando nos padrões errados.

Kaj
fonte
2

Do meu ponto de vista, os maiores desafios serão:

  • Apatridia das linguagens funcionais: A idéia nos jogos é que cada entidade tenha um estado e esse estado esteja sendo modificado de quadro para quadro. Existem mecanismos para imitar esse comportamento em algumas linguagens (por exemplo, funções com um "!" No esquema plt), mas isso parece meio artificial.
  • Ordem de execução : Nos jogos, algumas partes do código dependem de outras partes do código para serem concluídas antes da execução. Linguagens funcionais geralmente não dão garantias sobre a ordem de execução dos argumentos de uma função. Existem maneiras de imitar esse comportamento (por exemplo " (begin..." no esquema plt).

Sinta-se à vontade para estender esta lista (e talvez adicionar alguns pontos positivos ^^).

Dave O.
fonte
1
A ordem de execução pode variar em linguagem não estrita como Haskell, mas não causa problemas para criar partes de código que dependem de outras partes. Você pode pensar nisso como avaliação sob demanda. Um cálculo não é realizado até que o resultado seja necessário. Eu acho que a única maneira de causar luto é em termos de desempenho. Por exemplo, pode ser difícil obter um bom armazenamento em cache em alguns casos, porque você não terá controle sobre o pedido na avaliação, além de especificar as dependências entre os cálculos.
Jason Dagit
1

Você pode escrever seu código em C / C ++ e usar uma linguagem incorporada, como Embedded Common Lisp, para seus scripts. Embora seja difícil fazer com que eles sejam compilados em uma plataforma diferente. Você pode consultar o Lisp In Small Pieces para aprender a escrever sua própria linguagem de script. Realmente não é tão difícil, se você tiver tempo.

WarWeasle
fonte
0

Escrevi jogos simples no LISP e foi divertido, mas não algo que eu recomendaria. A programação funcional é sobre o resultado final. Portanto, é muito útil para processar dados e tomar decisões, mas achei difícil fazer código limpo simples como um loop de controle básico.

Embora eu não tenha aprendido F #, pode ser muito mais fácil trabalhar com isso.

Jeff
fonte
Meu pensamento inicial pode ser escrever o andaime e o tratamento de estado com uma linguagem imperativa e, em seguida, fazer lógica de jogo e tal com bits funcionais. Com o .NET e C # / F #, isso seria muito fácil, por exemplo, pois eles usam as mesmas bibliotecas e podem conversar entre si sem nenhuma penalidade real que eu conheça.
21710 McMuttons
-1

o positivo seria desempenho e portabilidade e o negativo seria o gerenciamento da complexidade , hoje os jogos são seriamente complexos e precisam de ferramentas ou linguagem de programação que possam gerenciar melhor a complexidade, como metodologia orientada a objetos ou programação genérica que é difícil de implementar na programação funcional língua.

uray
fonte
11
Você está brincando comigo? FP é programação genérica em esteróides. A capacidade de generalizar tipos de dados e funções oferece ao FP um poder sério para gerenciar a complexidade.
Rtperson
Eu concordo, um dos maiores pontos fortes da FP é sua capacidade de fazer código generalizado.
McMuttons