Edit: O comentário dos OPs tem sido cético sobre a eficiência da verificação do limite circular negativo sugerido para melhorar o algoritmo, a fim de verificar se um ponto 2D arbitrário se encontra dentro de um retângulo girado e / ou em movimento. Brincando um pouco no meu mecanismo de jogo em 2D (OpenGL / C ++), suplemento minha resposta fornecendo uma referência de desempenho do meu algoritmo com relação aos algoritmos (e variações) atuais dos pontos de verificação do retângulo no OP (e variações).
Originalmente, sugeri deixar o algoritmo no lugar (como é quase ideal), mas simplificá-lo através da mera lógica do jogo: (1) usando um círculo pré-processado em torno do retângulo original; (2) faça uma verificação de distância e se o ponto estiver dentro do círculo especificado; (3) use os OPs ou qualquer outro algoritmo direto (eu recomendo o algoritmo isLeft, conforme fornecido em outra resposta). A lógica por trás da minha sugestão é que verificar se um ponto está dentro de um círculo é consideravelmente mais eficiente do que uma verificação de limites de um retângulo girado ou qualquer outro polígono.
Meu cenário inicial para um teste de benchmark é executar um grande número de pontos que aparecem e desaparecem (cuja posição muda em cada ciclo do jogo) em um espaço restrito que será preenchido com cerca de 20 quadrados rotativos / móveis. Publiquei um vídeo ( link do youtube ) para fins ilustrativos. Observe os parâmetros: número de pontos que aparecem aleatoriamente, número ou retângulos. Vou comparar com os seguintes parâmetros:
OFF : Algoritmo simples, conforme fornecido pelo OP, sem verificações negativas nos limites do círculo
ATIVADO : usando círculos por processo (limite) em volta dos retângulos como uma primeira verificação de exclusão
ON + Stack : Criando limites de círculo em tempo de execução dentro do loop na pilha
ON + Distância quadrada : Usar distâncias quadradas como uma otimização adicional para evitar o algoritmo de raiz quadrada mais caro (Pieter Geerkens).
Aqui está um resumo dos vários desempenhos de diferentes algoritmos, mostrando o tempo necessário para percorrer o loop.
O eixo x mostra uma complexidade aumentada adicionando mais pontos (e diminuindo a velocidade do loop). (Por exemplo, em 1000 pontos de verificação aleatória em um espaço confidencial com 20 retângulos, o loop itera e chama o algoritmo 20000 vezes.) O eixo y mostra o tempo necessário (ms) para concluir o loop inteiro usando uma alta resolução temporizador de desempenho. Mais de 20 ms seriam problemáticos para um jogo decente, pois não tiraria proveito dos altos fps para interpolar uma animação suave e o jogo pode parecer assim 'áspero' às vezes.
Resultado 1 : um algoritmo de limite circular pré-processado com uma verificação negativa rápida dentro do loop melhora o desempenho em 1900% em comparação com o algoritmo regular (5% do tempo original do loop sem uma verificação). O resultado é aproximadamente proporcional ao número de iterações dentro de um loop, portanto, não importa se verificamos 10 ou 10000 pontos que aparecem aleatoriamente. Assim, nesta ilustração, é possível aumentar o número de objetos com segurança para 10k sem sentir uma perda de desempenho.
Resultado 2 : Foi sugerido por um comentário anterior que o algoritmo pode ser mais rápido, mas com muita memória. No entanto, observe que o armazenamento de um flutuador para o tamanho do círculo pré-processado leva apenas 4 bytes. Isso não deve representar problema real, a menos que o OP planeje executar simultaneamente mais de 100000 objetos. Uma abordagem alternativa e eficiente em termos de memória é calcular o tamanho máximo do círculo na pilha dentro do loop e deixá-lo fora do escopo a cada iteração e, portanto, praticamente não usa memória para um preço desconhecido da velocidade. De fato, o resultado mostra que essa abordagem é realmente mais lenta do que usar um tamanho de círculo pré-processado, mas ainda mostra uma melhoria considerável no desempenho em torno de 1150% (ou seja, 8% do tempo de processamento original).
Resultado 3 : eu melhoro ainda mais o algoritmo do resultado 1 usando distâncias quadradas em vez de distâncias reais e, assim, executando uma operação de raiz quadrada computacionalmente cara. Isso apenas aumenta levemente o desempenho (2400%). (Nota: eu também tento tabelas de hash para matrizes pré-processadas para aproximações de raízes quadradas com um resultado semelhante, mas um pouco pior)
Resultado 4 : Verifico mais a movimentação / colisão dos retângulos; no entanto, isso não altera os resultados básicos (conforme o esperado), pois a verificação lógica permanece essencialmente a mesma.
Resultado 5 : eu vario o número de retângulos e acho que o algoritmo se torna ainda mais eficiente quanto menos ocupado o espaço é preenchido (não mostrado na demonstração). O resultado também é algo esperado, pois a probabilidade diminui para que um ponto apareça dentro de um espaço minúsculo entre um círculo e os limites do objeto. Por outro lado, tento aumentar demais o número de retângulos em 100 dentro do mesmo espaço minúsculo confinado E variar dinamicamente em tamanho no tempo de execução no loop (sin (iterador)). Isso ainda apresenta um desempenho extremamente bom, com aumento no desempenho em 570% (ou 15% do tempo do loop original).
Resultado 6 : testei algoritmos alternativos sugeridos aqui e encontro uma diferença muito pequena, mas não significativa, no desempenho (2%). O interessante e mais simples algoritmo IsLeft funciona muito bem com um aumento de desempenho de 17% (85% do tempo de cálculo original), mas nem de longe com a eficiência de um algoritmo rápido de verificação negativa.
Meu objetivo é considerar primeiro o design enxuto e a lógica do jogo, especialmente ao lidar com limites e eventos de colisão. O algoritmo atual dos OPs já é bastante eficiente e uma otimização adicional não é tão crítica quanto otimizar o próprio conceito subjacente. Além disso, é bom comunicar o escopo e o objetivo do jogo, pois a eficiência de um algoritmo depende muito deles.
Sugiro sempre tentar fazer benchmark de qualquer algoritmo complexo durante o estágio de design do jogo, pois apenas olhar o código simples pode não revelar a verdade sobre o desempenho real do tempo de execução. O algoritmo sugerido pode não ser aqui necessário, se, por exemplo, se desejar apenas testar se o cursor do mouse está dentro de um retângulo ou não, ou quando a maioria dos objetos já está se tocando. Se a maioria das verificações de pontos estiver dentro do retângulo, o algoritmo será menos eficiente. (No entanto, seria possível estabelecer um limite de 'círculo interno' como uma verificação negativa secundária.) As verificações de limite de círculo / esfera são muito úteis para qualquer detecção de colisão decente de um grande número de objetos que naturalmente possuem algum espaço entre eles. .
Rec Points Iter OFF ON ON_Stack ON_SqrDist Ileft Algorithm (Wondra)
(ms) (ms) (ms) (ms) (ms) (ms)
20 10 200 0.29 0.02 0.04 0.02 0.17
20 100 2000 2.23 0.10 0.20 0.09 1.69
20 1000 20000 24.48 1.25 1.99 1.05 16.95
20 10000 200000 243.85 12.54 19.61 10.85 160.58