Cursos / Jogos Digitais / Inteligência Artificial para Jogos / Aula
Da mesma forma que Obstacle Avoidance, o comportamento Path Following seguir um caminho, pode ser implementado de várias formas. Veja aqui uma forma simplificada, que difere da original, porém, que apresenta resultados adequados para o cenário de um jogo de futebol.
Quando os comportamentos de navegação foram criados por Reynolds, ele definiu o comportamento para seguir estritamente as linhas que ligam os pontos do caminho. No seu método, ele interpola o segmento de reta entre os pontos através da projeção da posição do personagem na reta, e faz com que a força de navegação empurre o personagem para o ponto correspondente àquela fração do segmento de reta. O resultado era que o personagem seguisse o caminho como se fosse um trem em seu trilho.
Em alguns cenários, isso é ótimo! Por exemplo, se você tivesse fazendo um jogo de corrida, seria necessário que os NPCs, que seriam os carros, percorressem a pista de corrida seguindo suas retas e curvas. Porém, no cenário de jogo de futebol, não há necessariamente uma “pista" a ser seguida. Então, nesse caso, onde se quer apenas controlar possíveis posições dos jogadores, não é necessário ser tão preciso. Dessa forma, flexibilize mais a estratégia, adotando uma mais simples.
Defina, inicialmente um caminho como uma sequência de pontos de controle. A ação de seguir um caminho passa a ser simplesmente ir na direção do primeiro ponto da sequência e, assim que alcançá-lo, ele será retirado.
Fácil de entender, né?
O fato é que quando o ponto alcançado for retirado, o próximo passa a ser o primeiro da sequência. Logo, o personagem irá na direção desse próximo ponto, que, por sua vez, ao ser alcançado, será retirado da sequência e um novo ponto toma seu lugar, e assim por diante até não ter mais ponto na sequência para visitar.
Durante esse percurso, você poderá também acrescentar a possibilidade de que novos pontos sejam inseridos no caminho. Quando isso ocorrer, esse novo ponto deve ser o último a ser visitado, ou seja, deve ir para o final da sequência.
Como armazenar os pontos dessa sequência?
Em computação, sempre que se lida com uma forma de armazenar dados que possuam operações específicas sobre eles, está lidando com estruturas de dados. Existem várias estruturas de dados, cada uma com um propósito diferente e adequada para resolver um problema específico. Nesse caso, as operações que você poderá realizar sobre os pontos que foram dados para formar o caminho, é unicamente inserir um novo ponto no final e retirar um do início. Ou seja, sempre que algo novo é acrescentado na estrutura, ele vai para o final, e sempre que algo sai da estrutura, ele sai do início.
Ora, isso não lembra algo? Em determinados momentos, como quando você vai comprar os bilhetes do cinema, fazer pagamentos em bancos, entre outros. Isso mesmo, uma Fila. Pois é, na computação, também acontece algo parecido, mas se utilizando da estrutura de dados para inserir informações no final e retirar do início.
Acho que não foi muito difícil imaginar esse nome, não é?
Imagina um treinamento de cobrança de pênaltis, como o ilustrado na Figura 08. O treinador chama a molecada, e alinha eles numa fila, um atrás do outro. Quem vai bater o primeiro pênalti? Vai ser o primeiro da fila, certo? E se alguém do meio da fila tentar furar para bater o pênalti antes da vez dele?
Opa, aí o treinador não deixa… A estrutura de dados Fila funciona dessa forma: o primeiro que chegou vai ser o primeiro que vai bater o pênalti (ou o que o seu programa precisar fazer!), e quando ele bater, vai sair da fila e dar a vez ao próximo. Isso acontece até todo mundo ter batido os seus pênaltis! Se alguém chegar atrasado, só pode entrar no final da fila, e vai ser o último a treinar sua habilidade. Então sempre que se usa uma estrutura de dados Fila, os dados são armazenados em uma sequência, e ela não pode ser quebrada: o primeiro que for colocado na fila será o primeiro dado processado, e assim por diante sem ninguém pular a vez do outro.
Além da Fila, existem outras estruturas de dados cujos nomes também são bem sugestivos. Por exemplo, existe uma estrutura chamada de Pilha. Bom… como se refere a um conjunto de dados, não pense que a Pilha aqui é sinônimo daquela bateria que você coloca no seu gamepad. A Pilha que está sendo mencionada aqui é parecida com quela estrutura que normalmente sua mãe reclama de sua mesa de estudo… só que um pouco mais organizada.
Pense em uma pilha de livros…
Normalmente, quando você quer colocar um novo livro nesse “conjunto", você coloca no topo dele. Para retirar um livro desse conjunto… bom… se o que você quer é apenas remover um livro do conjunto (seja ele qual for), é mais sábio você retirar apenas o que se encontra no topo (é verdade que, com uma certa habilidade, você conseguirá remover um que está lá no meio, mas se seguir o “princípio do menor esforço", não será esse do meio que você vai remover).
Voltando ao nosso treinamento, imagina que um garoto cobra o pênalti perfeito, estilo “paradinho com giro 360º e dando cambalhota antes de chutar a bola". Ele corre com emoção e dá um mergulho de peixinho no campo…. Mas não contava que os colegas emocionados iriam querer comemorar com ele! Todos pulam em cima do pobre coitado, numa grande pilha de jogadores acumulados em cima do grande craque. Ele pode até tentar sair, mas não vai conseguir! Para isso, o jogador lá de cima da pilha tem que sair, depois o segundo, o terceiro… até que o último esteja livre do esmagamento e possa ser colocado na maca para atendimento.
Assim se comportam os elementos de uma estrutura Pilha em que os dados ainda são colocados sequencialmente para o processamento, mas o primeiro dado que será processado é o último que foi colocado (o último jogador que pulou tem que ser o primeiro a sair).
Existem muitas estruturas de dados na computação, mas Filas e Pilhas são as mais comuns, independentemente se a ação desenvolvida é um jogo ou aplicação bancária. Nesse caso, por enquanto, será utilizada apenas a Fila.
Por ser uma estrutura de dados muito comum, o C# (utilizado no Unity) já definiu uma classe própria, chamada Queue, que a implementa. O uso dessa classe irá facilitar a sua vida para implementar o caminho do jogador.
Bom, uma vez que se tem o caminho armazenado em uma Fila, o que fazer para calcular as forças de navegação?
Como sempre, a sua estratégia precisa ser adequada ao problema que está enfrentando. Tudo vai depender de quão preciso ou de quão realista você quer que os jogadores sejam, sabendo que precisão e realismo é diretamente proporcional à complexidade. Ou seja, quanto mais precisa e realista, mais complexa deve ser sua estratégia.
Imagine, por exemplo, se você fosse um jogador de futebol e seu técnico dissesse “corra em volta do campo tocando em cada uma das bandeirinhas”, como você faria isso? Obviamente, você iria em direção a uma bandeirinha até tocá-la, em seguida para a próxima até tocá-la e assim por diante. Normalmente, você não iria se preocupar com uma próxima bandeirinha até que ela seja a próxima a ser tocada. Essa é uma estratégia simples. Nela, os próximos pontos do caminho não são levados em conta.
Imagine (novamente) uma situação mais complexa, que você está modelando uma corrida de carros e coloca os pontos de controle do caminho nas curvas da pista. Nesse caso, ao se dirigir a uma curva, o NPC-carro precisa levar em conta as próximas curvas. Por exemplo, se uma curva for para a esquerda, ele precisa encostar à direita para “entrar na curva" no ângulo e velocidade adequados, como ilustra a Vídeo 01, caso contrário ele pode não ser eficiente no trajeto e, pior ainda, não frear adequadamente e passar direto.
Vídeo 01 – Exemplos de Curvas no jogo Forza Horizon
Bom, isso é para alertá-lo da necessidade de se adequar ao jogo que está sendo desenvolvido e não se limitar a receitas preestabelecidas de como algo deve ser implementado. Como dito anteriormente, tudo depende das características do seu jogo.
No jogo que você está desenvolvendo , a versão da estratégia mais simples é novamente suficiente para modelar o comportamento dos personagens. Assim adote a “visita" aos pontos sem levar em consideração os pontos seguintes. Se houver mais de um ponto no caminho, você poderá fazer com que o jogador siga ao próximo ponto através do comportamento Seek, caso o ponto atual seja o último do caminho adote o comportamento Arrival.
Por fim, você precisará definir quando o jogador “alcançou" um ponto do caminho. Podendo ser rigoroso, como o técnico que te mandou correr tocando nas bandeirinhas, ou ser mais flexível, adotando uma distância mínima dos alvos que “tocar” neles.
Bom, você é o técnico. É você quem manda! De qualquer forma, note que quanto maior a distância mínima de contato com o ponto, mais suaves serão as curvas do jogador na sequência dos pontos. E quanto menor, menos suaves elas serão.
Versão 5.3 - Todos os Direitos reservados