Como garantir que sempre haja um caminho percorrido pelos inimigos em um jogo de defesa de torre?

7

Estou fazendo um jogo de defesa em torre 2D. Até agora eu tenho uma matriz 2D que funciona como uma grade para o meu jogo. Sou capaz de colocar torres nele, executar inimigos e algumas coisas de arrasto e torre.

Agora estou enfrentando um problema na lógica de posicionamento da torre. Quero que sempre haja um caminho percorrido pelos inimigos, o que significa que o usuário não deve ser capaz de bloquear completamente o caminho colocando torres. Por exemplo. se o usuário colocar torres verticalmente em um mapa, o algoritmo deverá impedir a colocação de uma torre que completará a linha vertical. Ou, de qualquer outra forma, deve haver pelo menos um espaço livre (passável) para que o inimigo possa escapar.

Minha lógica atual verifica em todas as direções sempre que a torre é colocada. Se houver uma torre para cima, ela chamará novamente a mesma função nessa torre superior até atingir uma parede. Retorna 1 para parede superior e 5 para parede inferior e retorna a função (torre para cima / baixo) se houver uma torre. aqui está o código:

int checkCollision(tower)
{                           
    if( there is a  tower up) 
    return checkCollision(up tower);

    if(there is a tower down) 
    return checkCollision(down tower);                                      

            ......all directions.....       

    if( there is a wall on UP )     
        return 1;

    if( there is a wall DOWN ) 
        return 5;

        ....all walls......

    return 0;   
}   

Agora, o que eu quero é verificar simultaneamente se existe uma parede norte e uma parede sul ou verificar outras direções com qualquer outra possibilidade (como para cima, para baixo, diagonal para cima, diagonal para baixo ... e assim por diante) para não deixar o usuário coloque uma torre, pois deve haver um lugar para o inimigo.

Estou insatisfeito com meu código no momento. Quero dizer, meu código diz que uma parede foi encontrada, mas como posso verificar se uma parede é encontrada em uma direção e uma parede também é encontrada em outra direção? Não quero entrar em possibilidades como:

if(checkCollision(tower) == 1 && checkCollision(tower) == 5) 
  "You cannot place"

Eu quero algo como:

    if( any combination of more than 2 wall found and there is entry/exit point in between)  //so it is wall to wall
    "You cant place"

Eu também tentei pré-calcular sinalizadores se houver paredes nos dois lados (como de cima para baixo, de diagonal etc.), então não deixe o usuário colocar a torre, mas isso ainda não funciona.

Syed
fonte
Seus caminhos sempre têm a mesma largura (ou seja, dois ladrilhos de largura)?
Richard Marskell - Drackir

Respostas:

23

Não seria mais simples usar um algoritmo de busca de caminhos para verificar se a IA ainda tem uma rota clara? Presumivelmente, você já usa um para fazer com que os inimigos naveguem da entrada até a saída; basta executá-lo com a torre adicionada e, se falhar, a torre não será permitida.

SimonW
fonte
você pode me dar alguma referência a esses algos, geralmente usados ​​em jogos de defesa?
Syed
Não é específico para defesa de torre, mas aqui está uma boa introdução para iniciantes ao A * e um tratamento mais profundo . Se você estiver usando um mecanismo, provavelmente haverá uma implementação A * disponível, e existem algumas bibliotecas de localização de caminhos que você poderia usar. Específico para defesa, a resposta a esta pergunta reivindicações existe um algoritmo melhor que A *, mas eu não estou familiarizado com ele
SimonW
Ainda não codifiquei o algo para executar o inimigo do começo ao fim, pois acho que o posicionamento da grade deve ser feito primeiro. Eu cansei de tentar verificar a parede como descrito acima, pois deve rastrear a torre até encontrar acima / abaixo / direita / esquerda da parede, quero fazer o jogo exatamente como 'fieldrunners'. O que você acha que devo continuar tentando verificações na parede ou mudar para outra coisa? Estou realmente preso no meu trabalho: | em usando o mecanismo de jogo Unity3D
Syed
11
Meu plano normal ao fazer um jogo é levar todas as partes ao estado jogável mínimo e depois partir daí. Isso significa ignorar coisas difíceis, como proibir o jogador de bloquear o caminho e garantir que você possa enviar inimigos do início ao fim, e que o jogador possa atirar neles. Uma vez que você tenha um jogo, você pode ganhar ou perder, faça o que for divertido / não quebre. Repita até ficar incrível.
SimonW
11
Para encontrar o caminho para o Unity, você pode gostar deste projeto gratuito . Se você não tem muita experiência, as bibliotecas são uma ótima maneira de reduzir o tempo que você gasta sendo desmotivado por coisas chatas. Pegue o código de qualquer lugar e em qualquer lugar para jogar seu jogo e se preocupe em corrigi-lo mais tarde.
SimonW
3

Suponho que você já tenha algum código (usando algo como o algoritmo A * ) para que os inimigos encontrem um caminho para seu destino. Toda vez que o jogador tenta colocar uma torre, você pode simplesmente colocar um obstáculo temporário onde a torre estaria e executar o código de busca de caminhos para verificar se ainda haverá um caminho para os inimigos.

Você pode otimizar isso de várias maneiras. Por exemplo, se a torre recém-colocada tocar apenas no máximo um obstáculo existente, não poderá bloquear o caminho. Você também pode salvar os resultados de verificações anteriores (pelo menos até que algo mude), para que, se o jogador tentar o mesmo local várias vezes, não precise verificar novamente todas as vezes.

Também existem algumas técnicas que só funcionam em alguns casos. Por exemplo:

  • Se os locais possíveis das torres não se sobrepõem, você pode usar um algoritmo de busca de pontes para descobrir exatamente quais locais bloquearão o caminho dos inimigos.
  • Se o campo de jogo for bidimensional, há um truque inteligente baseado no fato de que existe um caminho claro do lado esquerdo para a direita, se e somente se não existir uma parede contínua de cima para baixo. Assim, você pode rotular cada torre (ou outro obstáculo) dependendo de qual lado do campo de jogo estiver conectado, se houver algum, por uma parede.

    Manter essas etiquetas à medida que novas torres são adicionadas é fácil e rápido. Se uma nova torre ligasse os dois lados, bloqueava o caminho dos inimigos. Claro que, se uma torre é removida, você ainda tem que recalcular os rótulos, mas você só precisa fazer isso quando a torre é realmente destruído, em vez de cada vez que o jogador simplesmente passa o cursor sobre um local potencial para uma nova torre.

Ilmari Karonen
fonte
1

Concordo com o SimonW que você deve executar seu algoritmo de processamento. Para ser completo, eis um truque usado pelos mapas TD de Starcraft / Warcraft, que não têm como verificar se o algoritmo de caminho passa / falha.

Digamos que os inimigos possam vir de qualquer lugar do topo da área edificável e ter que passar para qualquer lugar abaixo da área edificável; e existem paredes à esquerda / direita. Faça uma inundação de todas as torres tocando a parede esquerda. Se alguma torre encontrada tocar na parede direita, o caminho está bloqueado.

BlueRaja - Danny Pflughoeft
fonte
0

deveria ser mais como

bool checkCollision(tower)
{
if(there is tower above)
  return checkCollision(tower above);
if(there is tower below)
  return checkCollision(tower below);
...
...
...
if(wall above)
  return 1;
if(wall below)
  return 5;
...
...
...
return 0;
}

mais tarde, no seu código, onde você deseja verificar se é permitido colocar a torre ou não:

if(!checkCollision(tower above) && !checkCollision(tower below) && !checkCollision(tower left)...[check all sides]...)
  //You can place the tower
else
  //You cannot place the tower

Além disso, altere seu algoritmo para adicionar a capacidade de retornar as coordenadas de qual bloco de parede foi encontrado adjacente à sequência de torres. Verificar se o mesmo revestimento foi devolvido e descartar o que permitiria passar por loops de torres também.

Por loop de torre, quero dizer (não me importo com a minha arte ascii):

--------------------------
|                        |
|   TT                   |
|TTTTTTTTT               |
|       X T              |
|       T                |
|                        |
En                       Ex
|                        |
|                        |
|                        |
|________________________|

Não permitiria que você colocasse uma torre em X. A torre acima, a torre nas diagonais, a torre à direita e a torre abaixo de tudo reportarão colisões de paredes, no entanto, a coordenada do revestimento será a mesma. É isso que você precisa filtrar também.

Ahmed Belal Hashmi
fonte
0

Fácil, eu acabei de criar meu primeiro programa de localização de caminhos hoje, levei um pouco mais de 2 horas e, além de declarar variáveis, são apenas 27 linhas de código, incluindo colchetes.

programe no seu pathfinder uma matriz para todas as suas paredes reais e uma matriz para todas as suas paredes hipotéticas, se um jogador quiser fazer uma torre em um espaço, o espaço selecionado será uma parede hipotética; Execute o seu pathfinder para incluir paredes hipotéticas; se o programa voltar com um caminho encontrado, a torre poderá ser construída; caso contrário, a torre não poderá ser construída.

Felicidades

Aqui está o meu código,

function PathFind(){

var keepgoing:Number=1;
var count:Number=1;
var smallcount:Number=0;
var keepgoing2:Boolean=false;
var startX:Array=[];
var startY:Array=[];
startX[0]=new Array();
startX[1]=new Array();
startY[0]=new Array();
startY[1]=new Array();
startX[0][1] = disc.loca;
startY[0][1] = disc.locb;
startX[1][1] = String(disc.loca)+"$";
startY[1][1] = String(disc.locb)+"$";


for(var c:Number=1; c<=keepgoing; c++){
    smallcount=0;
    for (var a:Number=1; a<=3; a++){
        for (var b:Number=1; b<=3; b++){
            if (a==2 || b==2){
                if(Space[startX[0][c]+(a-2)][startY[0][c]+(b-2)].valid==true && SpaceII[startX[0][c]+(a-2)][startY[0][c]+(b-2)]==true){
                    count++;
                    smallcount++;
                    SpaceII[startX[0][c]+(a-2)][startY[0][c]+(b-2)]=false;
                    startX[0][count]=Space[startX[0][c]+(a-2)][startY[0][c]+(b-2)].IDx;
                    startY[0][count]=Space[startX[0][c]+(a-2)][startY[0][c]+(b-2)].IDy;
                    startX[1][count]=startX[1][c]+String(Space[startX[0][c]+(a-2)][startY[0][c]+(b-2)].IDx)+"$";
                    startY[1][count]=startY[1][c]+String(Space[startX[0][c]+(a-2)][startY[0][c]+(b-2)].IDy)+"$";
                    if(Space[startX[0][c]+(a-2)][startY[0][c]+(b-2)].goal==true){
                        keepgoing2=true;
                        disc.stringA = startX[1][count].toString();
                        disc.stringB = startY[1][count].toString();
                        //thisText.text=startX[1][count] + "%" + startY[1][count] + "*";
                        success=true;
                    }                       
                }
            }
        }
    }
    if(keepgoing2==true){
        c = keepgoing;
    } else {
        keepgoing = keepgoing + smallcount;
    }
}

}

Basicamente, meu código criará uma matriz de seqüências de caracteres X e Y de pareamento. Em seguida, minhas unidades móveis herdam as primeiras seqüências de emparelhamento bem-sucedidas e, em seguida, elas apenas desativam as seqüências, um segmento de cada vez. Cada coordenada é quebrada com um "$".

E o mais bonito do código acima é que 'keep going' só terá tamanho tão grande quanto o tamanho da grade e se tornará mais eficiente com as mais torres colocadas, sua eficiência sendo maximizada quando houver apenas um caminho definido.

Se você precisar de alguma ajuda com o que eu tenho acima aqui, informe-me.

Despertado
fonte