O framwork ET, voltado para clientes Unity3D e servidores em C#, utiliza uma arquitetura de máquina de comportamento para a lógica de IA. Entretanto, o desempenho do pathfinding em ambientes com obstáculos dinâmicos continua sendo um desafio significativo. Este artigo apresenta técnicas para melhorar a evasão de obstáculos em tempo real e a suavização de trajetórias de NPCs.
Problemas na Arquitetura de Máquina de Comportamento
Na implementação padrão do ET, o comportamento de patrulha de NPCs utiliza uma coroutine assíncrona para movimentação entre pontos de destino. A estrutura básica segue o padrão de ciclo condicional de execução:
// Comportamento de patrulha simplificado
while (executando) {
var pontoDestino = GerarPontoAleatorio(posicaoOrigem, raioPatrulha);
var sucesso = await MoverParaAsync(pontoDestino, tokenCancelamento);
if (!sucesso) break;
await AguardarTempoAleatorio(min, max, tokenCancelamento);
}
Essa abordagem apresenta dois problemas críticos:
- Ignorância de obstáculos dinâmicos: Quando outros NPCs ou objetos surgem na trajetória, a IA colide ou trava sem reação.
- Ausência de suavização: As curvas nos cantos geram linhas abruptas, resultando em animações desconexas.
Estratégia de Desvio de Obstáculos em Tempo Real
Detecção Otimizada por Raycasting
Integrar verificação de colisão a cada quadro dentro da coroutine de movimentação, utilizando Physics.RaycastNonAlloc para minimizar alocações de memória:
private async ETVoid MoverComDesvio(Vector3 destino, float velocidade) {
var posicaoAtual = transform.position;
var raioDeteccao = 2.0f;
var bufferHits = new RaycastHit[3];
while (Vector3.Distance(posicaoAtual, destino) > 0.5f) {
var direcao = (destino - posicaoAtual).normalized;
var qtdHits = Physics.RaycastNonAlloc(posicaoAtual, direcao, bufferHits, raioDeteccao);
if (qtdHits > 0) {
var perpendicular = Vector3.Cross(direcao, Vector3.up).normalized;
destino += perpendicular * 0.5f;
}
posicaoAtual += direcao * velocidade * Time.deltaTime;
transform.position = posicaoAtual;
await ETTask.NextFrame();
}
}
Ajuste Dinâmico do Raio de Detecção
O alcance do raycast deve se adaptar à velocidade de deslocamento da entidade:
| Velocidade (m/s) | Raio de Detecção (m) | Amostragem (por segundo) |
|---|---|---|
| Menor que 3 | 1.5 | 10 |
| Entre 3 e 6 | 2.5 | 15 |
| Maior que 6 | 4.0 | 20 |
Sistema de Prioridade para Evasão
Implementar pesos baseados no tipo de obstáculo permite que a IA tome decisões mais inteligentes ao escolher qual entidade evitar com mais urgência:
private int ObterPrioridadeDesvio(Collider obstaculo) {
if (obstaculo.CompareTag("Jogador")) return 3;
if (obstaculo.CompareTag("NPC")) return 2;
return 1;
}
Suavização de Trajetória com Curvas de Bézier
Redução de Pontos com Douglas-Peucker
Antes de interpolar, é necessário simplificar a lista de pontos do caminho calculado, mantendo apenas os vértices essenciais nas curvas:
private List<Vector3> ReduzirCaminho(List<Vector3> pontosOriginais, float tolerancia) {
if (pontosOriginais.Count < 3) return new List<Vector3>(pontosOriginais);
var resultado = new List<Vector3> { pontosOriginais[0] };
SimplificarRecursivo(pontosOriginais, 0, pontosOriginais.Count - 1, tolerancia, resultado);
resultado.Add(pontosOriginais[^1]);
return resultado;
}
Interpolação com Curva Cúbica
Após a smiplificação, aplicar interpolação por curvas de Bézier de terceira ordem para gerar movimentos fluidos:
private Vector3 CalcularBezierCubica(Vector3 p0, Vector3 p1, Vector3 p2, Vector3 p3, float t) {
float u = 1f - t;
float u2 = u * u;
float u3 = u2 * u;
float t2 = t * t;
float t3 = t2 * t;
return (u3 * p0) + (3f * u2 * t * p1) + (3f * u * t2 * p2) + (t3 * p3);
}
Resultados de Desempenho
Testes realizados com 100 unidades de IA simultâneas em hardware i7-12700K e RTX 3060 demonstraram melhorias significativas:
| Métrica | Implementação Original | Implementação Otimizada | Ganho |
|---|---|---|---|
| FPS Médio | 45 | 58 | +29% |
| GC por Quadro | 12 KB | 3 KB | -75% |
| Custo de Raycasting | 8.2 ms | 3.5 ms | -57% |
Parâmetros de Configuração
As constantes de pathfinding devem ser definidas em um arquivo de configuração acessível pelo projeto:
{
"PathFinding": {
"DistanciaVerificacaoObstaculos": 2.5,
"IntensidadeSuavizacao": 0.8,
"MaximoPassosEvasao": 5,
"ToleranciaReducaoCaminho": 0.3
}
}
Direções Futuras
- Pathfinding baseado em heightmap: Integrar com o sistema de terreno do Unity para transições suaves em elevações.
- Navegação em grupo: Aplicar regras de separação, alinhamento e coesão para evitar aglomeração de NPCs.
- Processamento paralelo com GPU: Utilizar Compute Shaders para calcular trajetórias de grande escala de forma concorrente.