Isso pode ser óbvio para muitos (acho que o nível de habilidade das pessoas no coderwall é bastante alto), mas considerando que acabei de me inscrever e passei a explicar isso para alguém (parece que compartilhei isso várias vezes), aqui vai .
Se você tem dois vetores de posição que deseja “comparar”, deve usar a distância (na verdade, a distância ao quadrado), em vez de uma verificação de igualdade do componente. Como exemplo – um personagem do jogo (posição) e uma posição específica no espaço – “O personagem chegou à origem (0, 0, 0) do mundo?”.
Costumo ver a implementação ingênua quando alguém reclama que determinado código de jogo não funciona.
Pseudo:
// Class Vector3 skeleton below if needed.
Vector3 position; // player position
Vector3 destination; // target/destination position
// ...
// Naive check
if(position==destination) {
onTargetReached();
}
É provável que isto “não trabalho”, uma vez que a sua posição está atualizando com base em uma velocidade ou velocidade vetorial e tendo em conta deltaTime (todos eles sendo bóias ou duplas ). As aspas ao redor do não funcionam são colocadas porque o computador faz exatamente o que você programa para e nada mais. Uma razão para usar a distância é por causa do intervalo de tempo (discreto), a posição pode nunca chegar de fato ao destino (você pode pular em um quadro (ou etapa de cálculo / simulação) para outro). Outro motivo é que os números de ponto flutuante podem ter problemas de precisão.
A distância a ser usada depende da sua escala – se você já estudou matemática, física, estatística, etc., pode ter encontrado o termo erro padrão ou épsilon. Pense na distância como um erro de medição aceitável ou “perto o suficiente”. Portanto, se a escala do seu sistema ou as unidades são 1 unidade flutuante = 1m e o problema que você está resolvendo (código que você está escrevendo) não é afetado por nada menor que um centímetro, você pode usar 0,01 como seu valor épsilon .
// Check using distance
const float kEpsilon = 0.01f;
if( (position-destination).magnitude() < kEpsilon ) {
onTargetReached();
}
Isso muda sua pergunta para “O personagem chegou perto o suficiente da origem (0, 0, 0) do mundo?”. Certifique-se de que essa mudança seja apropriada para sua base de código – se você estiver na NASA ou CERN, onde a precisão pode ser mais importante do que a velocidade, saia daqui.
Para tornar isso ainda melhor, você pode usar a distância ao quadrado. “O quê ?! Adicionar mais instruções para elevar ao quadrado ambos os lados da condição / desigualdade?”
LADO Para comparar apenas dois flutuadores (em vez de vetores), pode ser apropriado que você faça isso em seu aplicativo (provavelmente não para você se você codificar para um banco!):
float a, b;
// ... (system specific operations assigning values to a and b)
// Replace:
// if(a==b)
// with
if( (a-b)*(a-b) < kEpsilon*kEpsilon )
LADO FINAL
A resposta é – uma multiplicação requer menos instruções em comparação com uma raiz quadrada que é necessária para encontrar a magnitude de um vetor. Portanto, seu cheque deve ser:
// Check using squared distance (helper defined below)
const float kEpsilonSq = 0.01f*0.01f;
if( (position-destination).sqMagnitude() < kEpsilonSq ) {
onTargetReached();
}
Além disso, a maioria das classes Vector que você encontra provavelmente já tem um lenSquared ou sqMagnitude equivalente (procure por eles se começar a trabalhar com uma base de código existente ou um projeto com um novo mecanismo, etc.); se você está escrevendo o seu, agora você sabe para que serve. Observe que se você encontrar algum código que não tenha um método de magnitude quadrada embutido, você deve adicionar um. Se você não puder (sem acesso à fonte), use apenas o comprimento se for tudo o que estiver disponível – não faz sentido elevar ao quadrado a magnitude se você já o calculou.
Finalmente, quando se trata de otimização, otimize apenas algo que beneficie o desempenho geral. Não saia por aí mudando todos os lugares em sua base de código (existente) para fazer isso, a menos que seja em uma função que seja muito chamada – como uma função de atualização / etapa chamada cada quadro. Caso contrário, pode não valer a pena o tempo gasto substituindo todos os usos. A otimização deve ser equilibrada com a legibilidade. Para uma nova base de código, você pode usar a distância quadrada em todos os lugares do ponto de vista de “boas práticas”.
// Skeleton for Vector3 for those that may need it for better understanding
class Vector3 {
float x, y, z;
// Magnitude (or length) of the vector.
float magnitude() {
return sqrtf( this.sqMagnitude() );
}
// For more details, see http://en.wikipedia.org/wiki/Magnitude_%28mathematics%29
float sqMagnitude() {
return (x*x+y*y+z*z);
}
Vector3 operator-(const Vector &other) {
return Vector3(x-other.x, y-other.y, z-other.z);
}
bool operator==(const Vector &other) {
return (x==other.x && y==other.y && z==other.z);
}
// More class details omitted
// ...
};