Como limitar o movimento do clique e arrastar para uma área?

11

Peço desculpas pelo título um tanto genérico. Na verdade, não tenho muita ideia de como realizar o que estou tentando fazer, o que torna ainda mais difícil pesquisar uma possível solução.

Estou tentando implementar um tipo de marcador de caminho (talvez exista um nome mais adequado para ele, mas esse é o melhor que eu poderia criar).

Na frente do jogador, haverá um marcador de caminho, que determinará como o jogador se moverá quando ele terminar de planejar seu turno. O jogador pode clicar e arrastar o marcador para a posição que escolher, mas o marcador só pode ser movido dentro de uma área de trabalho definida (o bit cinza).

Diagrama do marcador de caminho

Então, agora estou com dois problemas:

Primeiro de tudo, como exatamente devo definir essa área viável? Eu posso imaginar talvez dois vetores que tenham o jogador como ponto de partida para formar o ângulo viável, e talvez esses dois arcos possam vir de círculos que tenham seu centro onde o jogador está, mas eu definitivamente não sei como colocar tudo isso juntos.

Em segundo lugar, depois de definir a área em que o marcador pode ser colocado, como posso garantir que o marcador permaneça apenas nessa área? Por exemplo, se o jogador clicar e arrastar o marcador, ele poderá se mover livremente dentro da área de trabalho, mas não deve deixar os limites da área. Por exemplo, se o jogador começar a arrastar o marcador para cima, ele se moverá para cima até atingir o final da área de trabalho (primeiro diagrama abaixo), mas se depois disso o jogador começar a arrastar para o lado, o marcador deverá seguir o arrasto enquanto ainda estiver dentro da área (segundo diagrama abaixo).

Primeiro diagrama: movendo-se para cima Segundo diagrama: após o arrasto

Espero que isso não tenha sido muito confuso. Obrigado rapazes.

Edit: Caso isso faça alguma diferença, estou usando C ++ com o Marmalade SDK.

Vexille
fonte
Eu acho que o termo que você procura é "waypoint", e você pode definir matematicamente a área cinza? A solução para o seu problema provavelmente será muito mais fácil, se for.
John McDonald
Os waypoints não estão relacionados à busca de caminhos e tal? Além disso, definir matematicamente a área cinza é um dos meus problemas: os PI acham que eu poderia descobrir algo como um retângulo ou até um círculo, mas esse tipo de forma, com dois arcos para os lados e dois outros lados em um ângulo ... um pouco acima da minha cabeça.
Vexille
Você também precisará especificar qual idioma e / ou kit de ferramentas você está usando, pois as soluções dependem principalmente da plataforma.
Raceimaztion
Sério? Imaginei que uma solução algorítmica ou mesmo pseudocódigo ajudaria. Vou editar a pergunta de qualquer maneira, apenas por precaução.
Vexille
Defina a forma em coordenadas polares (ângulo máximo do ângulo dos caracteres e distância mínima / máxima do caractere). Atualize apenas o waypoint para onde o mouse está, se o mouse estiver dentro desses limites. Se exceder qualquer um desses extremos, encaixe-o no maior valor possível. Comece a atualizar quando clicar dentro da área e pare quando o mouse for liberado.
ClassicThunder

Respostas:

8

Você pode definir uma área viável como a da sua pergunta com três valores:

float innerRadius;
float outerRadius;
float maxAngle;

Esses valores serão baseados em um ponto central que pode ou não ser a posição do jogador. A forma da área viável depende de onde você coloca esse ponto.

insira a descrição da imagem aqui

No exemplo acima, a posição central fica a uma certa distância (digamos, 50 unidades) atrás do jogador. Isso pode ser facilmente calculado como:

float offset = -50;
Vector2 centerPosition = playerPosition + offset * playerForward;

Para limitar a posição do marcador a essa área viável, mova-o primeiro como faria normalmente. Em seguida, validar a distância entre o ponto central e o marcador:

Vector2 direction = markerPosition - centerPosition;
float distance = direction.Length();
direction.Normalize();
markerPosition = centerPosition + direction * clamp(distance, innerRadius, outerRadius);

Por fim, valide o ângulo do marcador para o intervalo especificado. Vou usar o pseudocódigo para este:

- Find angle between vector C->M and vector playerForward
- If abs(angle) <= maxAngle Then do nothing
- Else If angle > 0 Then rotate M around C by maxAngle-angle
- Else If angle < 0 Then rotate M around C by -maxAngle-angle

Veja como girar um ponto em torno de outro. Isso pode ser feito com trigonometria ou com uma matriz de transformação.

Você também pode levar em consideração o tamanho do marcador e diminuir o raio e o ângulo para compensar.

Edit: Pensando bem, pode parecer mais natural se você validar o ângulo primeiro, depois a distância, então tente as duas alternativas!

David Gouveia
fonte
Ótima solução! Encontrei algo que envolvia dois círculos e um triângulo, mas sua soluçãog simplifica elegantemente isso. Sobre a validação de ângulo, pensei em algo parecido com um vetor normalizado que ficava em maxAngle (e outro em -maxAngle) de playerForward, que poderia ser multiplicado pelo comprimento de C-> M, caso estivesse dentro dos limites. , em ângulo. Estou assumindo que sua solução de girar M em torno de C seria menos onerosa, certo?
Vexille
@Vexille Bem, girar envolve uma cose uma sinoperação, então não tenho certeza. Mas para calcular esses dois vetores, você também precisa rotacioná-los, embora seja necessário fazê-lo apenas quando o vetor para frente for alterado. De qualquer forma, não deve importar muito, escolha o que você prefere implementar.
David Gouveia
10

Eu estava pensando em como o problema poderia ser resolvido se a forma fosse irregular e não se pudesse defini-la matematicamente. Aviso: esta é uma solução suja, não para os fracos de coração.

1. Leve sua área:

insira a descrição da imagem aqui

2. E converta-o em um bitmap monocromático:

insira a descrição da imagem aqui e chame-o de scale_0

3. Clone o bitmap e reduza-o para 50%:

insira a descrição da imagem aqui e chame-o de scale_1

4. E assim por diante, até que haja um bitmap com menos de 4 pixels de largura / altura:

insira a descrição da imagem aqui insira a descrição da imagem aqui insira a descrição da imagem aqui insira a descrição da imagem aqui insira a descrição da imagem aqui escala: 2, 3, 4, 5, 6

5. Agora, temos nossa área como bitmaps monocromáticos de diferentes resoluções: insira a descrição da imagem aqui

6. Pegue a última imagem (aqui "scale_6") e repita todos os seus pixels.

  • x = Math.pow ( 2, scale_level );converta as coordenadas de cada pixel em coordenadas da tela: onde scale_level é o número que adicionamos após "scale_". Também podemos chamá-lo de nível de quad-tree, embora não trabalhemos realmente com um quad-tree. Faça o mesmo com y.
  • verifique se o pixel no x & y traduzido é preto. Caso contrário, não faz parte da forma e você deve apenas o continuepróximo passo do loop
  • verifique se o pixel está mais próximo do cursor do mouse do que o pixel verificado anteriormente - se sim, salve as coordenadas do pixel - use as coordenadas antes da conversão, ou seja, as coordenadas dentro do bitmap.
  • no final do loop, multiplique essas coordenadas por 2: x *= 2; y*=2;para convertê-las em coordenadas na próxima imagem (escala anterior)

7. Tire uma foto anterior (aqui "scale_5"), mas não passe por todos os pixels; comece em x = saved_x e termine com x = saved_x + 2, o mesmo com y. Ou seja, já que agora você passará apenas por 4 pixels para cada nível! O resto é como na p. 6

8. Tire a primeira imagem (a maior = a de maior resolução), passe novamente por 4 pixels e, finalmente, você terá o pixel mais próximo do cursor do mouse:

insira a descrição da imagem aqui

insira a descrição da imagem aqui

insira a descrição da imagem aqui

9. No entanto, estou tratando "M" como um ponto aqui. Se você deseja que ele seja um círculo que se encaixa completamente, contrate (reduz) a forma por circle.radiuspixels primeiro.

Pensei em acrescentar que esse algoritmo só funcionará se você não usar imagens monocromáticas, mas em escala de cinza e tratar um pixel como "cheio" se não for branco e como "vazio" se for exatamente branco ... OU se você estiver redimensionando O algoritmo altera todos os grupos de 4 pixels em 1 pixel preto toda vez que pelo menos um desses 4 pixels não era branco.

Markus von Broady
fonte
2
+1 para obter respostas para formas que são difíceis (se não impossíveis) de serem expressas matematicamente.
Cypher
Uau, muito interessante. +1 também: D
Vexille
Eu o implementei em um projeto real e devo dizer que surgiram alguns problemas. Basicamente, você precisa fazer uma lista de células da grade, onde você pega a célula da grade mais próxima chamada closeste verificar a distância até o ponto mais distante do closest- vamos nomear a distância furthest_dist. Agora você precisa remover da lista todas as células que têm seu ponto mais próximo além do furthest_distnível e vão mais fundo. Então, ao invés de algo como isto: i.imgur.com/4UuFo.png do É algo como isto: i.imgur.com/dyTT3.png
Markus von Broady