Separando a física e a lógica do jogo do código da interface do usuário

12

Estou trabalhando em um jogo simples baseado em blocos.

O jogo consiste basicamente em mover blocos na área de jogo, por isso é uma simulação física trivial. Minha implementação, no entanto, está longe de ser ideal e estou pensando se você pode me dar alguma dica sobre como fazê-lo melhor.

Dividi o código em duas áreas: lógica do jogo e interface do usuário, como fiz com muitos jogos de quebra-cabeça:

  • A lógica do jogo é responsável pelas regras gerais do jogo (por exemplo, o sistema formal de regras no xadrez)
  • A interface do usuário exibe a área e as peças do jogo (por exemplo, tabuleiro e peças de xadrez) e é responsável pelas animações (por exemplo, movimento animado das peças de xadrez)

A lógica do jogo representa o estado do jogo como uma grade lógica, onde cada unidade tem a largura / altura de uma célula na grade. Portanto, para uma grade de largura 6, você pode mover um bloco de largura 2 quatro vezes até que ele colide com o limite.

A interface do usuário pega essa grade e a desenha convertendo tamanhos lógicos em pixels (ou seja, multiplica por uma constante). No entanto, como o jogo praticamente não tem lógica de jogo, minha camada de lógica de jogo [1] não tem muito o que fazer, exceto a detecção de colisão. Veja como funciona:

  1. O jogador começa a arrastar uma peça
  2. A interface do usuário solicita à lógica do jogo a área de movimento legal dessa peça e permite que o jogador a arraste para dentro dessa área
  3. Jogador solta um pedaço
  4. A interface do usuário encaixa a peça na grade (para que fique em uma posição lógica válida)
  5. A interface do usuário informa à lógica do jogo a nova posição lógica (por meio de métodos mutadores, que eu prefiro evitar)

Não estou muito feliz com isso:

  • Estou escrevendo testes de unidade para a minha camada de lógica do jogo, mas não a interface do usuário, e acabou que todo o código complicado está na interface do usuário: impedindo a peça de colidir com outras pessoas ou o limite e encaixando-a na grade.
  • Não gosto do fato de a interface do usuário informar a lógica do jogo sobre o novo estado; prefiro chamar um movePieceLeft()método ou algo parecido, como nos outros jogos, mas não fui muito longe com essa abordagem, porque a lógica do jogo não sabe nada sobre o arrastar e ajustar possível na interface do usuário.

Eu acho que a melhor coisa a fazer seria me livrar da camada de lógica do jogo e implementar uma camada de física. Eu tenho algumas perguntas sobre isso:

  1. Essa camada de física é comum ou é mais típico que a camada de lógica do jogo faça isso?
  2. O encaixe na grade e o código de arrasto da peça pertenceriam à interface do usuário ou à camada física?
  3. Essa camada de física normalmente funcionaria com tamanhos de pixel ou com algum tipo de unidade lógica, como minha camada de lógica de jogo?
  4. Já vi uma detecção de colisão baseada em eventos na base de código de um jogo, ou seja, o jogador apenas arrastava a peça, a interface do usuário renderizava isso obedientemente e notificava o sistema de física, e o sistema físico chamava um método onCollision () na peça uma vez que uma colisão é detectada. O que é mais comum? Esta abordagem ou pedindo a área de movimento legal primeiro?

[1] camada provavelmente não é a palavra certa para o que quero dizer, mas o subsistema parece exagerado e a classe é equivocada, porque cada camada pode consistir em várias classes.


fonte
Minha resposta ajudou, precisa de mais informações?
Will Marcouiller
Faça um voto positivo e aceite a resposta se isso tiver ajudado ou contado sobre seus problemas na implementação de uma arquitetura ou algo assim, mas mantenha-nos informados da sua situação para que possamos ter uma assistência melhor. =)
Will Marcouiller,

Respostas:

3

Vou tentar responder a essa pergunta, pois entendo o que você está perguntando, embora não tenha muita experiência em desenvolvimento de jogos, pois ainda estou aprendendo.

A meu ver, você deve separar o código da GUI da lógica do jogo e dos objetos de domínio, ou seja, as peças do quebra-cabeça. Essas são de fato três camadas separadas - e sim, layeré um termo apropriado na minha opinião. É frequentemente usado para explicar os conceitos de separar cada nível de objetos de um sistema em subsistemas independentes um do outro.

Em relação à programação orientada a objetos, cada objeto deve ser uma classe. Assim, cada peça do seu quebra-cabeça deve consistir em uma classe por si só, e assim no tabuleiro do jogo. O tabuleiro do jogo deve conter X peças do quebra-cabeça, dependendo do tamanho e da capacidade de movimento que você deseja dar ao jogador.

Então, aqui estão meus pensamentos sobre o tópico - espero que ajude:

  1. A camada da GUI: Essa camada deve conter métodos apenas para exibir as peças no tabuleiro de jogo, permitindo a interação entre o jogo em si e o jogador;
  2. A camada Game Controller: é responsável pelas entradas do jogador. Essa é a camada que deve instruir uma peça a se mover nas diferentes direções e perguntar ao tabuleiro de jogo se haverá colisões após o movimento e assim por diante;
  3. A camada de negócios: essa camada deve conter todas as suas classes de negócios, ou seja, as peças do seu jogo e o objeto do tabuleiro de jogo que contém as peças do quebra-cabeça.

Nessa arquitetura, a camada da GUI mostraria ao jogador o estado do jogo, a localização de cada peça do quebra-cabeça. A camada da GUI seria responsável por obter as entradas do jogador e passá-la para uma camada subjacente do Game Controller, que seria responsável pela detecção de colisão. Se não houver, a peça pode ser encomendada para se mover nessa direção de entrada. Para fazer isso, basta chamar osMoveLeft ,MoveRight, etc. métodos para fazer a peça se mover. Você também pode deixar o tabuleiro saber qual peça você quer mover e, em seguida, ela ordena o movimento da peça, e a peça se move na direção exigida. Essa arquitetura facilita o teste de cada parte do código nas diferentes camadas e permite testes de unidade, testes de integração e testes funcionais.

Eu sei que isso pode parecer um pouco confuso à vista, e graças a Deus se não for! Se você precisar de mais detalhes e ajuda, não hesite em perguntar, ficarei feliz em ajudar da melhor maneira possível, embora eu seja iniciante no desenvolvimento de jogos.

Obrigado pela leitura! =)

Will Marcouiller
fonte
2
O que soa muito semelhante ao Model View Controller.
Dezpn 07/02
Para ser honesto, qualquer descrição da interface de abstração da implementação subjacente será semelhante ao Model View Controller. Mas isso ocorre porque a terminologia e as técnicas usadas são as mesmas (abstraindo dos detalhes da implementação e separando a interface do usuário da implementação). Mas assume um ambiente muito simples. Existem várias camadas aqui e existem controladores em cada camada. Separar a visualização do modelo não é suficiente aqui, você também precisa separar o controle de interação do usuário do controle do jogo.
MrCranky
Eu me pergunto o que há de tão complexo aqui, já que estamos movendo peças de um quebra-cabeça para um tabuleiro de jogo delimitado. Os controles são obtidos da GUI, que é um bom caminho, e passam para uma camada de abstração, ou seja, o controlador do jogo. O controlador do jogo verifica se há colisões durante o movimento e, em seguida, diz à peça para se mover se não houver colisão detectada. A peça do quebra-cabeça se move adequadamente na direção solicitada e revela sua nova posição através da Positionfunção getter de propriedades, de modo que o controlador do jogo agora exige que a GUI exiba essa peça movida para a nova posição.
Will Marcouiller
Eu escrevi sobre o "MVC" no desenvolvimento de jogos há alguns meses. Sim, é um conceito boa porta de entrada para os não iniciados com desenvolvimento de jogos: codecube.net/2010/08/xna-for-the-everyday-developer
Joel Martinez
1
Desculpe pela aceitação tardia, infelizmente fiquei um pouco distraído com o projeto. Eu realmente gosto da abordagem do controlador, vou adotá-la!