Modifica: il commento dei PO è stato scettico sull'efficienza del controllo del limite circolare negativo suggerito per migliorare l'algoritmo, al fine di verificare se un punto 2D arbitrario si trova all'interno di un rettangolo ruotato e / o mobile. Giocherellando un po 'sul mio motore di gioco 2D (OpenGL / C ++), integra la mia risposta fornendo un benchmark delle prestazioni del mio algoritmo rispetto agli attuali algoritmi (e variazioni) di point-in-rectangle-check degli OP.
Inizialmente ho suggerito di lasciare l'algoritmo in atto (dato che è quasi ottimale), ma di semplificare attraverso la semplice logica di gioco: (1) usando un cerchio pre-elaborato attorno al rettangolo originale; (2) eseguire un controllo della distanza e se il punto si trova all'interno del cerchio dato; (3) utilizzare gli OP o altri algoritmi semplici (consiglio l'algoritmo isLeft come indicato in un'altra risposta). La logica alla base del mio suggerimento è che verificare se un punto si trova all'interno di un cerchio è considerevolmente più efficiente di un controllo al contorno di un rettangolo ruotato o di qualsiasi altro poligono.
Il mio scenario iniziale per un test di riferimento è quello di eseguire un gran numero di punti che appaiono e scompaiono (la cui posizione cambia in ogni ciclo di gioco) in uno spazio limitato che sarà riempito con circa 20 quadrati rotanti / mobili. Ho pubblicato un video ( link di YouTube ) a scopo illustrativo. Notare i parametri: numero di punti che appaiono casualmente, numero o rettangoli. Farò un benchmark con i seguenti parametri:
OFF : algoritmo semplice come fornito dall'OP senza controlli negativi al contorno del cerchio
ON : Utilizzo di cerchi perimetrati (contorno) attorno ai rettangoli come primo controllo di esclusione
ON + Stack : creazione dei limiti del cerchio in fase di esecuzione all'interno del loop sullo stack
ON + Square Distance : utilizzare le distanze quadrate come ulteriore ottimizzazione per evitare di prendere l'algoritmo di radice quadrata più costoso (Pieter Geerkens).
Ecco un riepilogo delle varie prestazioni di diversi algoritmi, mostrando il tempo necessario per scorrere il ciclo.
L'asse x mostra una maggiore complessità aggiungendo più punti (e quindi rallentando il ciclo). (Ad esempio, a 1000 controlli di punti che appaiono casualmente in uno spazio confidenziale con 20 rettangoli, il ciclo scorre e chiama l'algoritmo 20000 volte.) L'asse y mostra il tempo impiegato (ms) per completare l'intero ciclo usando un'alta risoluzione timer delle prestazioni. Più di 20 ms sarebbe problematico per un gioco decente in quanto non trarrebbe vantaggio dagli alti fps per interpolare un'animazione fluida e il gioco potrebbe apparire così "robusto" a volte.
Risultato 1 : un algoritmo a rilegatura circolare preelaborato con un controllo negativo rapido all'interno del loop migliora le prestazioni del 1900% rispetto all'algoritmo normale (5% del tempo di loop originale senza controllo). Il risultato è approssimativamente proporzionale al numero di iterazioni all'interno di un loop, quindi non importa se controlliamo 10 o 10000 punti che appaiono casualmente. Pertanto, in questa illustrazione è possibile aumentare il numero di oggetti in modo sicuro a 10k senza avvertire una perdita di prestazioni.
Risultato 2 : è stato suggerito da un precedente commento che l'algoritmo potrebbe essere più veloce ma che richiede molta memoria. Tuttavia, si noti che la memorizzazione di un float per la dimensione del cerchio pre-elaborata richiede solo 4 byte. Ciò non dovrebbe costituire un vero problema a meno che l'OP non preveda di eseguire contemporaneamente più di 100000 oggetti. Un approccio alternativo ed efficiente in termini di memoria è quello di calcolare la dimensione massima del cerchio nello stack all'interno del ciclo e lasciarlo fuori dal campo di applicazione con ogni iterazione e quindi praticamente senza utilizzo della memoria per un prezzo sconosciuto della velocità. In effetti, il risultato mostra che questo approccio è effettivamente più lento rispetto all'utilizzo di una dimensione del cerchio pre-elaborata, ma mostra comunque un notevole miglioramento delle prestazioni di circa l'1150% (ovvero l'8% del tempo di elaborazione originale).
Risultato 3 : Migliorare ulteriormente l'algoritmo del risultato 1 utilizzando le distanze al quadrato anziché le distanze effettive e quindi eseguendo un'operazione di radice quadrata computazionalmente costosa. Ciò aumenta solo leggermente le prestazioni (2400%). (Nota: provo anche tabelle hash per array pre-elaborati per approssimazioni di radici quadrate con un risultato simile ma leggermente peggiore)
Risultato 4 : controllo ulteriormente lo spostamento / scontro dei rettangoli; tuttavia, ciò non modifica i risultati di base (come previsto) poiché il controllo logico rimane essenzialmente lo stesso.
Risultato 5 : varco il numero di rettangoli e trovo che l'algoritmo diventa ancora più efficiente tanto meno lo spazio è pieno (non mostrato nella demo). Il risultato è anche un po 'atteso, poiché la probabilità diminuisce per far apparire un punto all'interno di uno spazio minuscolo tra un cerchio e i confini dell'oggetto. Dall'altro estremo, cerco di aumentare di 100 il numero di rettangoli anche all'interno dello stesso piccolo spazio confinato E di variare dinamicamente le loro dimensioni in fase di esecuzione all'interno del ciclo (sin (iteratore)). Ciò funziona ancora molto bene con un aumento delle prestazioni del 570% (o 15% del tempo di loop originale).
Risultato 6 : collaudo algoritmi alternativi suggeriti qui e trovo una differenza molto leggera ma non significativa nelle prestazioni (2%). L'interessante e più semplice algoritmo IsLeft si comporta molto bene con un aumento delle prestazioni del 17% (85% del tempo di calcolo originale) ma da nessuna parte vicino all'efficienza di un algoritmo di controllo negativo rapido.
Il mio punto è innanzitutto considerare la progettazione snella e la logica di gioco, soprattutto quando si tratta di confini ed eventi di collisione. L'attuale algoritmo dei PO è già abbastanza efficiente e un'ulteriore ottimizzazione non è così critica come l'ottimizzazione del concetto stesso stesso. Inoltre, è bene comunicare lo scopo e lo scopo del gioco, poiché l'efficienza di un algoritmo dipende in modo critico da essi.
Suggerisco di provare sempre a confrontare qualsiasi algoritmo complesso durante la fase di progettazione del gioco in quanto il semplice esame del codice semplice potrebbe non rivelare la verità sull'effettiva esecuzione in fase di esecuzione. L'algoritmo suggerito potrebbe non essere nemmeno necessario, se, ad esempio, si desidera semplicemente verificare se il cursore del mouse si trova all'interno di un rettangolo oppure no, o quando la maggior parte degli oggetti è già in contatto. Se la maggior parte dei controlli dei punti si trova all'interno del rettangolo, l'algoritmo sarà meno efficiente. (Tuttavia, sarebbe possibile stabilire un limite di "cerchio interno" come controllo negativo secondario.) I controlli di confine cerchio / sfera sono molto utili per qualsiasi rilevamento decente di collisioni di un gran numero di oggetti che hanno naturalmente uno spazio tra loro .
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