Recentemente, surgiu uma discussão sobre como criar um jogo multiplayer de rolagem lateral 2D que pode ter um design de nível de loop (pense em Starbound e como seus mundos estão em loop).
Eu pensei que a maneira mais simples seria ter um mapa retangular com zonas de gatilho que pudessem teleportar jogadores de um lado para o outro. No entanto, o problema óbvio com essa abordagem é o caso de ter vários jogadores na borda do mapa de uma só vez. Você não quer apenas teleportar jogadores na frente um do outro e precisaria de uma maneira de transportar jogadores sem que outros jogadores desaparecessem.
Para acrescentar essa idéia e corrigir o problema, vim com o seguinte: tenha uma zona de acionamento (quadrado vermelho na imagem) onde os jogadores poderão ver uma "zona de clone" (quadrado verde). Nesse quadrado verde, os objetos do lado oposto da zona de disparo seriam copiados para sua zona de clone correspondente (pode ser vista com as formas A e B). Quando um jogador chega à borda inicial da "zona de clone", ele é teleportado para o outro lado do mapa.
Neste exemplo, o Jogador 2 pensaria que está vendo o Jogador 1, no entanto, na verdade, ele estaria vendo o clone dele e vice-versa.
Isso parecia um pouco extremo e complexo para o problema em questão. Minha pergunta agora é saber se esta solução é uma boa abordagem para resolver o problema ou se existe uma maneira mais simples de resolver esse problema?
fonte
Respostas:
Este sistema com todos esses gatilhos parece um pouco complicado e propenso a erros.
Você pode quebrar a posição do jogador usando o módulo com algo como
playerPositionX = playerPositionX % mapWidth
Desta forma, quando o seu jogador atingir
playerPosition == mapWidth
o valorplayerPosition
, voltará a 0.Esta solução pode ser estendida com todo o sistema de renderização.
fonte
pos - map_width
).A solução canônica é usar portais . No seu exemplo, existe apenas um nível, exceto que há um portal conectando as extremidades esquerda e direita.
Qualquer coisa que se desloque através desse portal terá suas coordenadas traduzidas para a outra extremidade do portal, de modo que, se algo estiver se movendo para a esquerda, ele reaparecerá no lado direito do nível e vice-versa.
Sua câmera também precisa suportar os portais; se o portal estiver dentro da câmera, ele deverá renderizar partes do nível em ambos os lados do portal. Se você conhece os editores de imagem para gráficos de blocos sem costura, é a mesma coisa aqui.
A parte tediosa é que tudo que lida com distância ou caminho também precisará suportar portais. Isso inclui IA, algos na linha de visão, atenuação de som e assim por diante.
O bom dos portais é que ele é muito poderoso. O mecanismo de compilação o usou para simular níveis de várias histórias, apesar de não ser um mecanismo 3D "verdadeiro". Alguns mecanismos modernos também usam portais para criar espaços não euclidianos; Portal e Antichamber são exemplos notáveis em 3D.
fonte
Lembre-se de que o que você exibe na tela e o que está na memória são duas coisas totalmente diferentes. Imagine que você tem uma janela que precisa preencher com dados sobre o mundo. Você preenche a janela da esquerda para a direita. Enquanto estiver analisando seus dados para preencher o mundo, se você chegar ao fim do mundo, simplesmente retorne ao início dos dados. Usar uma operação de módulo é ideal. Lembre-se de que você precisa fazer isso para tudo . Projéteis, raios, jogadores, física; todos eles precisam ter suas posições embrulhadas ao cruzar os limites do mundo.
Cada jogador compartilha dados, mas tem sua própria perspectiva dos dados. Suas janelas são preenchidas de maneira diferente, dependendo de onde elas estão no mundo.
Isso significa que não há necessidade de criar clones ou teleportar ninguém. Essencialmente, você está criando clones, apenas renderizando caracteres nas telas um do outro.
fonte
Desconecte a renderização do mundo e você poderá executar uma renderização abrangente e correta sem recorrer a nenhum artefato de clonagem ou teletransporte.
Primeiro, no seu mundo, você tem um mundo de tamanho fixo, de
0
paraWidth
. Sempre que um objeto ficar abaixo de 0, você o agrupa até o fim, e a qualquer momento que um objeto termina, oWidth
envolve até o início. Isso significa que todos os objetos lógicos do seu mundo estão sempre dentro do alcance0...Width
.Segundo, para renderizar, você fará um módulo na posição. Portanto, o lado esquerdo da tela é "Base" e o lado direito é "Base + Tamanho". Então você procura no seu mundo qualquer coisa dentro desse intervalo. Na verdade, você procurará o intervalo do módulo, que o mapeia de volta
0...Width
.O truque durante a pesquisa é retornar a posição do objeto em relação à do
Base
lado esquerdo. Isso se converte em coordenadas locais da tela, para que o renderizador em si não precise se preocupar com o módulo, apenas a pesquisa.Você não precisa clonar nada, pois cada renderizador lida apenas com o objeto em um local.
Se o seu mundo é produzido em segmentos ou usando estruturas 3D, você precisará segmentá-lo. Portanto, não é um bloco consituno, mas pode ser movido para acomodar essa renderização. Você não precisa de muitos blocos, no mínimo 2.
fonte
Penso que a única abordagem razoável seria implementar o seu mundo embrulhado em uma estrutura de dados subjacente completamente transparente para o jogo e o usuário. Assim, em alguns de baixo nível, você tem uma função mapCoordinate () que agrupa suas coordenadas reais no seu recurso de mapa subjacente ...
Portanto, se o seu mundo atual tiver apenas 10 unidades de largura, o jogador e o jogo não o conhecerão. Para o jogador, o mundo é infinito - e se o jogo perguntar o que está na posição 15 - a função subjacente traduzirá essa solicitação, módulo10 e fornecerá o item na posição 5.
Portanto, para toda a lógica do jogo e tudo o mais, é como se você tivesse um mundo infinito, onde havia cópias de tudo.
fonte
Não é exatamente o mesmo, mas eu implementei algo semelhante em um game jam. O jogo teve jogadores se movendo em um pequeno nível circular, contornado quando o jogador alcançou uma posição 'x' de pi. A renderização foi fácil, porque acabamos de renderizar tudo e giramos uma câmera offset para rastrear o que estava acontecendo. Você pode implementar algo semelhante, como sugerido acima:
fonte