Tempos de iteração rápidos são essenciais para o desenvolvimento de jogos, muito mais do que gráficos e mecanismos sofisticados com muitos recursos em minha opinião. Não é de admirar que muitos pequenos desenvolvedores escolham linguagens de script.
A maneira do Unity 3D de pausar um jogo e modificar os ativos E o código, depois continuar e fazer com que as alterações entrem em vigor imediatamente é absolutamente boa para isso. Minha pergunta é: alguém implementou um sistema semelhante em mecanismos de jogos em C ++?
Ouvi dizer que alguns motores realmente super sofisticados funcionam, mas estou mais interessado em descobrir se existe uma maneira de fazê-lo em um motor ou jogo caseiro.
Obviamente, haveria trocas, e não consigo imaginar que você possa recompilar seu código enquanto o jogo estiver em pausa, recarregá-lo e funcionar em todas as circunstâncias ou plataformas.
Mas talvez seja possível para programação de IA e módulos lógicos de nível simples. Não é como se eu quisesse fazer isso em qualquer projeto a curto (ou longo prazo), mas fiquei curioso.
fonte
A troca a quente é um problema muito, muito difícil de resolver com código binário. Mesmo se seu código for colocado em bibliotecas de vínculo dinâmico separadas, ele precisará garantir que não haja referências diretas a funções na memória (pois o endereço das funções pode mudar com uma recompilação). Isso significa essencialmente não usar funções virtuais, e qualquer coisa que use ponteiros de função faz isso através de algum tipo de tabela de despacho.
Coisas peludas e constrangedoras. É melhor usar uma linguagem de script para código que precisa de iteração rápida.
No trabalho, nossa base de código atual é uma mistura de C ++ e Lua. Lua também não é uma parte pequena do nosso projeto - é quase uma divisão de 50/50. Implementamos o recarregamento on-the-fly do Lua, para que você possa alterar uma linha de código, recarregar e continuar. De fato, você pode fazer isso para corrigir erros de falha que ocorrem no código Lua, sem reiniciar o jogo!
fonte
(Você pode querer saber sobre o termo "remendo de macaco" ou "soco de pato" por nada além da imagem mental bem-humorada.)
Além disso: se seu objetivo é diminuir o tempo de iteração para alterações no "comportamento", tente algumas abordagens que o levem ao longo do caminho e combine bem para permitir mais disso no futuro.
(Isso sairá um pouco tangente, mas eu prometo que retorne!)
Muitos desses pontos são benéficos, mesmo se você não conseguir recarregar dados ou códigos.
Anedotas de suporte:
Em um PC RTS grande (equipe de aproximadamente 120 pessoas, principalmente C ++), havia um sistema de economia de estado incrivelmente profundo, usado para pelo menos três propósitos:
Desde então, usei registro / reprodução determinística em um jogo de cartas em C ++ e Lua para o DS. Nós nos conectamos à API que projetamos para a IA (no lado C ++) e registramos todas as ações do usuário e da IA. Usamos essa funcionalidade no jogo (para fornecer uma repetição para o jogador), mas também para diagnosticar problemas: quando houve uma falha ou comportamento estranho, tudo o que precisávamos era pegar o arquivo salvo e reproduzi-lo em uma compilação de depuração.
Desde então, também usei sobreposições mais de algumas vezes, e o combinamos com o nosso sistema "cria automaticamente este diretório e carrega novo conteúdo no computador de mão". Tudo o que precisamos fazer é deixar a cena / nível / o que quer que seja e voltar e não apenas os novos dados (sprites, layout de nível etc.) carregariam, mas também qualquer novo código na sobreposição. Infelizmente, isso está ficando muito mais difícil com os computadores de mão mais recentes, devido à proteção contra cópias e mecanismos anti-hacking que tratam o código especialmente. Ainda o fazemos para scripts lua.
Por último, mas não menos importante: você pode (e eu tenho, em várias circunstâncias específicas muito pequenas) dar um soco no pato, remendando diretamente os códigos de instrução das instruções. Porém, isso funciona melhor se você estiver em uma plataforma e compilador fixos, e porque é quase impossível de manter, muito propenso a erros e limitado no que você pode realizar rapidamente, na maioria das vezes só o uso para redirecionar o código durante a depuração. Ele faz ensinar-lhe um inferno de um monte sobre o seu conjunto de instruções com pressa, no entanto.
fonte
Você pode fazer algo como módulos de troca a quente em tempo de execução, implementando os módulos como bibliotecas de vínculo dinâmico (ou bibliotecas compartilhadas no UNIX) e usando dlopen () e dlsym () para carregar funções dinamicamente da biblioteca.
Para Windows, os equivalentes são LoadLibrary e GetProcAddress.
Este é um método C e tem algumas armadilhas ao usá-lo em C ++, você pode ler sobre isso aqui .
fonte
Se você estiver usando o Visual Studio C ++, poderá de fato pausar e recompilar seu código em determinadas circunstâncias. O Visual Studio suporta Editar e Continuar . Anexe ao seu jogo no depurador, faça-o parar em um ponto de interrupção e modifique o código após o seu ponto de interrupção. Se você deseja salvar e continuar, o Visual Studio tentará recompilar o código, reinsira-o no executável em execução e continue. Se tudo der certo, a alteração feita será aplicada ao jogo em execução sem a necessidade de um ciclo inteiro de compilação, construção e teste. No entanto, o seguinte impedirá que isso funcione:
Eu usei o Edit and Continue para melhorar drasticamente a velocidade de coisas como a iteração da interface do usuário. Digamos que você tenha uma interface de usuário funcional construída inteiramente em código, exceto que você acidentalmente trocou a ordem de desenho de duas caixas para não ver nada. Ao alterar duas linhas de código ao vivo, você pode salvar um ciclo de compilação / construção / teste de 20 minutos para verificar uma correção trivial da interface do usuário.
Essa NÃO é uma solução completa para um ambiente de produção, e eu achei a melhor solução para mover o máximo de sua lógica possível para arquivos de dados e, em seguida, tornar esses arquivos recarregáveis.
fonte
Como outros já disseram, é um problema difícil, vinculando dinamicamente C ++. Mas é um problema resolvido - você pode ter ouvido falar da COM ou de um dos nomes de marketing que foram aplicados ao longo dos anos: ActiveX.
COM tem um pouco de mau nome do ponto de vista do desenvolvedor, porque pode ser um grande esforço para implementar componentes C ++ que expõem sua funcionalidade usando-o (embora isso seja facilitado com ATL - a biblioteca de modelos ActiveX). Do ponto de vista do consumidor, ele tem um nome ruim porque os aplicativos que o usam, por exemplo, para incorporar uma planilha do Excel em um documento do Word ou um diagrama do Visio em uma planilha do Excel, tendem a travar um pouco no passado. E isso se resume aos mesmos problemas - mesmo com todas as orientações que a Microsoft oferece, o COM / ActiveX / OLE foi / é difícil de acertar.
Vou enfatizar que a tecnologia do COM não é, por si só, inerentemente ruim. Primeiro, o DirectX usa interfaces COM para expor sua funcionalidade e isso funciona bem o suficiente, assim como vários aplicativos que incorporam o Internet Explorer usando seu controle ActiveX. Em segundo lugar, é uma das maneiras mais simples de vincular dinamicamente o código C ++ - uma interface COM é essencialmente apenas uma classe virtual pura. Embora ele tenha um IDL como o CORBA, você não é obrigado a usá-lo, especialmente se as interfaces definidas são usadas apenas dentro do seu projeto.
Se você não está escrevendo para Windows, não pense que COM não vale a pena considerar. A Mozilla o reimplementou em sua base de código (usada no navegador Firefox) porque eles precisavam de uma maneira de componente seu código C ++.
fonte
Há uma implementação de C ++ compilado em tempo de execução para o código de jogo aqui . Além disso, eu sei que há pelo menos um mecanismo de jogo proprietário que faz o mesmo. É complicado, mas factível.
fonte