Eu tenho um jogo terminado, que quero recusar em outras versões. Seriam jogos semelhantes, com mais ou menos o mesmo tipo de design, mas nem sempre, basicamente as coisas podem mudar, às vezes pequenas, às vezes grandes.
Gostaria que o código principal fosse versionado separadamente do jogo, para que, se digamos que eu corrija um bug encontrado no jogo A, a correção estará presente no jogo B.
Estou tentando encontrar a melhor maneira de gerenciar isso. Minhas idéias iniciais são:
- Crie um
engine
módulo / pasta / qualquer que seja, que contenha tudo o que possa ser generalizado e seja 100% independente do resto do jogo. Isso incluiria algum código, mas também ativos genéricos que são compartilhados entre os jogos. - Coloque esse mecanismo em seu próprio
git
repositório, que será incluído nos jogos como umgit submodule
A parte com a qual estou lutando é como gerenciar o restante do código. Digamos que você tenha sua cena de menu, esse código é específico do jogo, mas também a maioria tende a ser genérico e pode ser reutilizado em outros jogos. Eu não posso colocá-lo noengine
, mas recodificá-lo para cada jogo seria ineficiente.
Talvez o uso de algum tipo de variação do Git branches possa ser eficaz para gerenciar isso, mas não acho que esse seja o melhor caminho a percorrer.
Alguém tem algumas idéias, experiência para compartilhar ou algo sobre isso?
fonte
Respostas:
É exatamente o que eu faço e funciona muito bem. Eu tenho uma estrutura de aplicativos e uma biblioteca de renderização, e cada uma delas é tratada como submódulo dos meus projetos. Acho que o SourceTree é útil quando se trata de submódulos, pois os gerencia bem e não permite que você esqueça nada; por exemplo, se você atualizou o submódulo do mecanismo no projeto A, ele notificará você para fazer as alterações no projeto B.
Com a experiência, vem o conhecimento de qual código deve estar no mecanismo versus o que deve ser por projeto. Sugiro que, se você estiver inseguro, mantenha-o em cada projeto por enquanto. Com o passar do tempo, você verá em seus vários projetos o que permanece o mesmo e, gradualmente, poderá levar isso em consideração no código do seu motor. Em outras palavras: duplique o código até que você tenha quase 100% de certeza de que não está sendo alterado discretamente por projeto e generalize-o.
Nota sobre controle de origem e binários
Lembre-se de que, se você espera que seus ativos binários mudem com frequência, convém colocá-los no controle de fonte baseado em texto, como o git. Apenas dizendo ... existem melhores soluções para binários. A coisa mais simples que você pode fazer por enquanto para ajudar a manter seu repositório "engine-source" limpo e com bom desempenho é ter um repositório separado "binários de mecanismo" que contém apenas binários, que você também inclui como submódulo em seu projeto. Dessa forma, você reduz o dano ao desempenho causado ao seu repositório "fonte do mecanismo", que está mudando o tempo todo e no qual você precisa de iterações rápidas: commit, push, pull etc. Sistemas de gerenciamento de controle de fonte como o git operam em deltas de texto , e assim que você introduz os binários, você introduz deltas maciços a partir de uma perspectiva de texto - o que acaba custando seu tempo de desenvolvimento.Anexo GitLab . O Google é seu amigo.
fonte
Em algum momento, um mecanismo DEVE se especializar e saber coisas sobre o jogo. Vou sair pela tangente aqui.
Pegue recursos em um RTS. Um jogo pode ter
Credits
eCrystal
outroMetal
ePotatoes
Você deve usar os conceitos de OO adequadamente e usar o máx. reutilização de código. É claro que
Resource
existe um conceito aqui.Portanto, decidimos que os recursos têm o seguinte:
int
)Observe que essa noção de a
Resource
pode representar mortes ou pontos em um jogo! Não é muito poderoso.Agora vamos pensar em um jogo. Podemos ter moeda negociando moedas de um centavo e adicionando um ponto decimal à saída. O que não podemos fazer são recursos "instantâneos". Como dizer "geração de rede elétrica"
Digamos que você adicione um
InstantResource
classe com métodos semelhantes. Agora você está (começando a) poluir seu mecanismo com recursos.O problema
Vamos pegar o exemplo do RTS novamente. Suponha que o jogador doe algo
Crystal
para outro jogador. Você quer fazer algo como:No entanto, isso é realmente muito confuso. É de uso geral, mas confuso. Já que impõe um
resourceDictionary
que significa que agora seus recursos precisam ter nomes! E é por jogador, então você não pode mais ter recursos de equipe.Esta é uma abstração "demais" (não é um exemplo brilhante, eu admito). Em vez disso, você deve chegar a um ponto em que aceita que seu jogo tem jogadores e cristais; então, você pode apenas ter (por exemplo)
Com uma classe
Player
e uma classe emCurrentPlayer
queCurrentPlayer
ocrystal
objeto mostrará automaticamente o material no HUD para a transferência / envio de doações.Isso polui o mecanismo com cristal, a doação de cristal, as mensagens no HUD para os jogadores atuais e tudo mais. É mais rápido e fácil ler / gravar / manter (o que é mais importante, pois não é significativamente mais rápido)
Considerações finais
O caso do recurso não é brilhante. Espero que você ainda possa ver o ponto. Se alguma coisa demonstrei que "os recursos não pertencem ao mecanismo", como o que um jogo específico precisa e o que é aplicável a todas as noções de recursos são MUITO diferentes. O que você normalmente encontrará são 3 (ou 4) "camadas"
creature
ouship
ousquad
. Usando herança você vai ter aulas que abrangem todos os 3 camadas (por exemplo,Crystal
é umResource
que é umGameLoopEventListener
exemplo)Criando um novo jogo a partir de um mecanismo antigo
Isso é MUITO comum. A fase 1 é eliminar as camadas 3 e 4 (e 2 se o jogo for do tipo TOTALMENTE diferente) Suponha que estamos criando um RTS a partir de um RTS antigo. Ainda temos recursos, não apenas cristais e outras coisas - portanto, as classes base nas camadas 2 e 1 ainda fazem sentido, todo o cristal mencionado em 3 e 4 pode ser descartado. Então nós fazemos. No entanto, podemos verificá-lo como uma referência para o que queremos fazer.
Poluição na camada 1
Isso pode acontecer. Abstração e desempenho são inimigos. O UE4, por exemplo, fornece muitos casos otimizados de composição (portanto, se você deseja X e Y, alguém escreveu um código que faz X e Y muito rápido - sabe que está fazendo as duas coisas) e, como resultado, é MUITO grande. Isso não é ruim, mas é demorado. A Camada 1 decidirá coisas como "como você passa dados para shaders" e como você anima as coisas. Fazer da melhor maneira para o seu projeto é SEMPRE bom. Apenas tente planejar o futuro, reutilizar o código é seu amigo, herdar para onde faz sentido.
Classificando camadas
Ultimamente (prometo) não tenha muito medo de camadas. Motor é um termo arcaico dos velhos tempos de dutos de função fixa, nos quais os motores funcionavam da mesma maneira graficamente (e, como resultado, tinha muito em comum), o duto programável virou isso de cabeça para baixo e, assim, a "camada 1" ficou poluída com quaisquer efeitos que os desenvolvedores desejassem alcançar. A IA era a característica distintiva (por causa da infinidade de abordagens) dos motores, agora é AI e gráficos.
Seu código não deve ser arquivado nessas camadas. Até o famoso motor Unreal tem MUITAS versões diferentes, cada uma específica para um jogo diferente. Existem poucos arquivos (exceto estruturas de dados semelhantes, talvez) que não teriam sido alterados. Isto é bom! Se você quiser fazer um novo jogo com outro, levará mais de 30 minutos. A chave é planejar, saber quais bits copiar e colar e o que deixar para trás.
fonte
Minha sugestão pessoal de como lidar com o conteúdo que é uma mistura de genérico e específico é torná-lo dinâmico. Tomarei sua tela de menu como exemplo. Se eu entendi mal o que você estava pedindo, deixe-me saber o que você queria saber e eu adaptarei minha resposta.
Há três coisas que estão (quase) sempre presentes em uma cena do menu: o plano de fundo, o logotipo do jogo e o próprio menu. Essas coisas geralmente são diferentes com base no jogo. O que você pode fazer com esse conteúdo é criar um MenuScreenGenerator em seu mecanismo, que usa três parâmetros de objeto: BackGround, Logo e Menu. A estrutura básica dessas três partes também faz parte do seu mecanismo, mas seu mecanismo não diz exatamente como essas partes são geradas, apenas quais parâmetros você deve fornecer.
Em seguida, no código do jogo, você cria objetos para um BackGround, um Logo e um Menu e passa para o MenuScreenGenerator. Novamente, seu próprio jogo não lida com a forma como o menu é gerado, isso é para o mecanismo. Seu jogo precisa apenas informar ao mecanismo como deve ser e onde deve estar.
Essencialmente, seu mecanismo deve ser uma API que o jogo diz o que exibir. Se feito corretamente, seu mecanismo deve fazer o trabalho duro e seu próprio jogo deve apenas informar ao mecanismo quais recursos usar, quais ações executar e como o mundo se parece.
fonte