Respostas:
Eu não estou familiarizado com o Android, então não sei quais ferramentas você tem à sua disposição, mas posso lhe dizer uma maneira de implementar isso em termos gerais. O quão fácil será depende do que o Android fornecer para você. Você precisará de matrizes ou, pelo menos, elas simplificarão muito os cálculos.
Para iniciantes, verifique a colisão da caixa delimitadora e retorne imediatamente se não colidirem, a fim de evitar cálculos adicionais. Isso é lógico, porque se as caixas delimitadoras não colidirem, é garantido que nenhum pixel colidirá também.
Posteriormente, se uma verificação de colisão perfeita de pixels for necessária, o ponto mais importante é que você deve executar essa verificação no mesmo espaço . Isso pode ser feito pegando cada pixel do sprite A, aplicando uma série de transformações para colocá-los no espaço local do sprite B e, em seguida, verifique se ele colide com algum pixel nessa posição no sprite B. Uma colisão ocorre quando os dois pixels marcados são opacos.
Então, a primeira coisa que você precisa é construir uma matriz mundial para cada um dos sprites. Provavelmente, existem tutoriais on-line ensinando como criar um, mas deve ser basicamente uma concatenação de algumas matrizes mais simples na seguinte ordem:
Translation(-Origin) * Scale * Rotation * Translation(Position)
A utilidade dessa matriz é que, multiplicando um ponto no espaço local - e, por exemplo, se você obtiver os pixels usando um método como bitmap.getPixelAt(10,20)
10,20, é definido no espaço local -, a matriz mundial correspondente o moverá para o espaço mundial:
LocalA * WorldMatrixA -> World
LocalB * WorldMatrixB -> World
E se você inverter as matrizes, também poderá ir na direção oposta, ou seja, transformar pontos do espaço do mundo em cada um dos espaços locais do sprite, dependendo da matriz usada:
World * InverseWorldMatrixA -> LocalA
World * InverseWorldMatrixB -> LocalB
Portanto, para mover um ponto do espaço local do sprite A para o espaço local do sprite B , você primeiro o transforma usando a matriz mundial do sprite A, a fim de inseri-lo no espaço mundial e, em seguida, usando a matriz mundial inversa do sprite B , para inseri-lo espaço local de sprite B:
LocalA * WorldMatrixA -> World * InverseWorldMatrixB -> LocalB
Após a transformação, você verifica se o novo ponto está dentro dos limites do sprite B e, se o fizer, verifica o pixel nesse local, exatamente como no sprite A. Portanto, todo o processo se torna algo assim (em pseudocódigo e não testado) :
bool PixelCollision(Sprite a, Sprite B)
{
// Go over each pixel in A
for(i=0; i<a.Width; ++i)
{
for(j=0; j<a.Height; ++j)
{
// Check if pixel is solid in sprite A
bool solidA = a.getPixelAt(i,j).Alpha > 0;
// Calculate where that pixel lies within sprite B's bounds
Vector3 positionB = new Vector3(i,j,0) * a.WorldMatrix * b.InverseWorldMatrix;
// If it's outside bounds skip to the next pixel
if(positionB.X<0 || positionB.Y<0 ||
positionB.X>=b.Width || positionB.Y>=b.Height) continue;
// Check if pixel is solid in sprite B
bool solidB = b.getPixelAt(positionB.X, positionB.Y).Alpha > 0;
// If both are solid then report collision
if(solidA && solidB) return true;
}
}
return false;
}
Embora a resposta de David Gouveia pareça correta, não é a melhor solução do ponto de vista do desempenho. Existem várias otimizações importantes que você precisa fazer:
resolva e evite verificações desnecessárias verificando primeiro com uma simples colisão circular: para obter o centro e o raio de seus sprites em qualquer rotação, obtenha as coordenadas mínimas e máximas x e y de todos os 4 vértices (já rotacionados): então você pode construir um círculo se fechando no sprite por:
center = max_x-min_x / 2, max_y-min_y / 2
raio = max (max_x-min_x, max_y-min_y)
agora você não deve ter muitos candidatos a verificar rasterizando as imagens já transformadas (giradas) basicamente usando um algoritmo simples de mapeamento de textura afim . Basicamente, você acompanha 4 linhas por sprite rasterizado: 2 linhas indo do vértice a para os próximos vértices da sua caixa girada (bec) 2 linhas indo no "espaço de bitmap" do vértice u1 / v1 para os próximos vértices u2 / v2 e u3 / v3: Nota: pesquisei esta imagem no Google e ela mostra um triângulo, suas caixas são apenas dois triângulos. É vital que esse algoritmo desenhe linhas horizontais (é por isso que é chamado de " rasterizador ") para evitar "buracos" dentro do bitmap devido a erros de arredondamento. Calculando as linhas com um algoritmo de Bresenham, você precisa para cada pixel apenas 4 adições e 4 comparações (e às vezes duas adições adicionais, dependendo da inclinação). O que você faz é escrever seu próprio redutor de textura de polígono, mas sem a correção 3D cara (e difícil de otimizar).
você pode reduzir facilmente a resolução dos bitmaps de colisão (por exemplo, pelo fator 2) e economizar ainda mais tempo. uma verificação de colisão em metade da resolução deve ser imperceptível.
Se você estiver usando aceleração gráfica, pode ser possível usar algum tipo de verificação de buffer acelerado por HW (estêncil?) Para evitar a codificação do rasterizador por conta própria.
O principal problema é: Java não é muito rápido ao acessar dados de bitmap armazenados em uma matriz 2D. Eu recomendaria armazenar os dados em uma matriz unidimensional para evitar pelo menos 1 verificação de indexOutOfBounds para cada acesso. Use também dimensões de potência de 2 (como 64x64, 128x128 etc.). Dessa forma, você pode calcular o deslocamento via deslocamento de bits e não multiplicação. Você também pode otimizar o acesso à segunda textura para executá-lo somente se a primeira tiver valor! = 0 (transparente)
Todos esses problemas foram resolvidos nos mecanismos de renderização de software; pode ser útil procurar no código fonte