Criando um nível multiplayer 2D sem repetições?

15

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.

imagem

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?

KenQueso
fonte
Os jogadores podem voltar para uma área anterior?
XiaoChuan Yu
sim, de um lado para o outro, dando um efeito de "andar ao redor do mundo". Semelhante a como é um mundo em estrelas
KenQueso
2
Você já pensou em fazer do mundo um grande círculo? ou tratando o nível como um grande círculo e traduzindo-o para um estágio 2D plano?
Nzall 17/07
você não pode sempre alinhar a posição da câmera com o player sendo controlado?
Ali1S232

Respostas:

16

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 == mapWidtho valor playerPosition, voltará a 0.

Esta solução pode ser estendida com todo o sistema de renderização.

Exaila
fonte
11
isso não teria o problema de fechar quando os jogadores vissem jogadores cuja posição redefinisse o teletransporte?
KenQueso
Como você estenderia isso ao sistema de renderização?
Mikael Högström
Você pode ter um jogador sempre no centro da câmera e ter o mapa em volta. Como um mapa na civilização no modo Terra. Uma outra abordagem poderia ser tornar a parte visível de um jogador para ambos os lados do mapa.
Exaila
4
@ MikaelHögström Basta renderizar como de costume, mas as coisas próximas à borda direita precisam ser renderizadas uma segunda vez à esquerda (ou seja, em pos - map_width).
Mario
11
Em qualquer lugar do código que você estiver procurando por 'qual objeto está nessa coordenada' ou 'quais são as coordenadas desse objeto', você o faria xcoord% mapWidth. É difícil dizer sem o seu código, mas isso provavelmente renderizaria corretamente.
Homem de Lata
13

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.

congusbongus
fonte
2
Se você ouvir os comentários do jogo do portal, parte da maneira como os portais funcionam é implementada pela clonagem do que é visível através do orifício. (mas por razões físicas, em vez de renderização)
Mooing Duck
6

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.

MichaelHouse
fonte
3

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 0para Width. Sempre que um objeto ficar abaixo de 0, você o agrupa até o fim, e a qualquer momento que um objeto termina, o Widthenvolve até o início. Isso significa que todos os objetos lógicos do seu mundo estão sempre dentro do alcance 0...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 Baselado 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.

edA-qa mort-ora-y
fonte
1

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.

Falco
fonte
1

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:

  • Ao desenhar, verifique a posição da câmera e determine o que precisa ser desenhado, levando em consideração a posição da câmera e seu alcance de visão.
  • Nos casos em que a câmera vê além da 'borda' do mapa, selecione uma quantidade apropriada de conteúdo do outro lado do mundo para desenhar sobre essa borda, geralmente apenas adicionando ou subtraindo a largura do nível à sua posição.
  • A lógica do jogo precisa estar ciente dessa costura e ajustá-la conforme mencionado em outras respostas. Casos específicos a serem observados são colisões em que um objeto está de um lado, mas está colidindo com um objeto do outro lado.
Chris
fonte