Como posso processar uma barra de progresso circular processualmente?

7

Estou trabalhando no Flash AS3, mas pseudo-código ou qualquer outro idioma está bem.

Como posso fazer uma barra de progresso circular? Semelhante às barras de navios em Pax Britanica, você pode vê-las na captura de tela aqui .

Além de torná-los quadro a quadro, há alguma solução óbvia que estou perdendo aqui?

Estou procurando o equivalente a esticar um retângulo para uma barra de progresso regular ou usar uma máscara simples (se essa solução existir).

(Também não posso marcar isso com círculo ou barra de progresso devido à falta de representante, se alguém puder, isso seria ótimo)

Adrian Seeley
fonte

Respostas:

19

Primeiro vou explicar o princípio, depois a parte do desenho:

No meu exemplo, suponho que você tenha um valor de progresso de 0 a 100 (%), mesmo que qualquer outra coisa faça.

Em uma linha reta, a posição atual da barra de progresso seria simplesmente esse valor de progresso.

Em um círculo, você obtém essa posição com trigonometria. Qualquer ponto em um círculo é dado como:

point.x = r * cos(angle);
point.y = r * sin(angle); 

onde r é o raio do círculo. Como você vê, o ângulo é a única variável nessas equações. Isso significa que você de alguma forma precisa integrar seu progresso ao ângulo.

A solução é simples: uma "corrida" ao redor do círculo é igual a 2 * Pi (ou 360 graus). Você precisa dividir essa distância em 100 partes menores, para que, quando o valor do progresso for 100, o valor dentro de cos () e sin () seja 2 * pi (ou 360 graus).

Você consegue isso simplesmente dividindo 2 * pi (ou 360 graus) por 100 e multiplicando-o pelo progresso:

float step = 2*pi/100;

point.x = r * cos(progress * step);
point.y = r * sin(progress * step);

Agora para a parte do desenho.

Não sei nada sobre o Actionscript3 (ou o Flash, por sinal), por isso não posso lhe dizer os procedimentos exatos, mas em geral haveria duas maneiras de fazer isso:

Primeiro, e (na minha opinião, mais fácil dos dois): desenhe o círculo parcial manualmente como simplesmente uma conexão de linhas coloridas com uma certa espessura (se você não precisar de uma textura e bastar uma cor simples) ou desenhe o circule como uma conexão de quadriláteros texturizados (se você precisar de texturas).

Segundo: você desenha uma imagem que representa seu círculo totalmente carregado e o carrega como textura, coloca-o em um quadrilátero e o renderiza. Em seguida, desenhe manualmente um círculo parcial sobre esse quadrilátero e renderize apenas a parte do quadrilátero texturizado onde o círculo é renderizado à sua frente. No OpenGL, por exemplo, você pode fazer isso facilmente usando o buffer de estêncil.

Se você deseja saber como calcular os diferentes pontos do seu círculo para usá-los na montagem dos polígonos / linhas necessários, observe as equações acima novamente:

point.x = r * cos(angle);
point.y = r * sin(angle); 

Você pode executar aqueles dentro de um loop for que vai de 0 ao seu progresso atual, por exemplo, se fosse um círculo que consistia em uma série de pontos conectados (na sintaxe c ++):

float granularity = 2*PI/required_granularity; //determines how smooth your circle will look
float step = 2*PI/100.0f;

list<Vector2> points; //list of all the calculated points
for(float angle=0; angle < progress*step; angle += granularity)
{
    Vector2 point(radius*cos(angle), radius*sin(angle));
    points.push_back(point); //adds the point to the list
}

Agora, todos esses pontos serão centralizados na origem do seu sistema de coordenadas, portanto, é necessário movê-los para qualquer posição em que você queira que seu círculo esteja. Mas acho que você pode descobrir isso sozinho: P

TravisG
fonte
4

Parece que você está querendo desenhar um arco. Um arco que é grosso. Encontrei este artigo para fazer exatamente isso no AS3. Confira a demonstração na parte inferior.

MichaelHouse
fonte
11
Este é provavelmente o método que utilizarei (apenas porque é específico do AS3), mas aceitei a resposta que funciona em todos os idiomas para ajudar a maioria das pessoas que pesquisam. Obrigado!
Adrian Seeley
1

Não tentei o seguinte, mas parece que deve funcionar.

Você precisa de duas imagens:

  1. o gráfico externo do HUD, faça a parte que deseja que o progresso seja mostrado através de
  2. uma imagem de círculo de cor sólida com o raio do medidor de progresso, depois corte a metade inferior e torne-a transparente (pode ser meio quadrado mesmo porque a parte do HUD cobre o restante)

O seguinte pressupõe que sua marca de 0% (e 100%) está na posição de 9 horas.


Se o seu progresso for 50% ou menos:

  • Gire e desenhe o semicírculo para que a porcentagem desejada fique visível na parte superior
  • Faça um corte retangular da meia área inferior para remover a partida esquerda do semicírculo exibida na área inferior
  • Desenhar o gráfico HUD da camada superior

Se o seu progresso for> 50%:

  • Desenhe o semicírculo para que ele preencha toda a metade superior (marca de 50%). Você não precisa de rotação
  • Gire e desenhe o semicírculo novamente para refletir a porcentagem restante
  • Por exemplo, se você desejar 60%, desenhe a primeira cópia para mostrar 50% e gire e desenhe a segunda cópia para mostrar outros 10%
  • Desenhar o gráfico HUD da camada superior

Não sei como fazer isso especificamente (ou se é possível) no AS3. Espero que isso faça sentido (ah, e realmente funcione!).

Doug.McFarlane
fonte
0

Outro método que vi foi usar shaders.

Você só precisa de uma imagem, a imagem inteira do HUD, com a área de progresso em uma escala de cinza degradê. Comece na posição 0% (9 horas, por exemplo) com branco puro e progrida gradualmente para preto na posição 100%.

Você não pode ter nenhum cinza na parte do HUD, incluindo branco ou preto. Se você precisar usar essas cores, escolha outra cor pura para sua escala de gradiente (uma cor que não é usada em nenhum outro lugar).

Faça com que seu programa passe ao sombreador o valor do progresso no intervalo de '0,0' a '1,0' (ou qualquer escala que o idioma do seu sombreamento use para os intervalos de cores). Você também pode passar ao shader uma cor da barra de progresso (e a cor 'off', se desejar), se quiser que a cor mude dinamicamente, como quando estiver vazia (ou cheia) para avisar o jogador.

No seu sombreador, você avalia cada pixel de origem e, se for cinza puro (cada R, G, B tem o mesmo valor), esse pixel faz parte do medidor de progresso. Se não for apenas passar a cor original. Se for um pixel do medidor de progresso, é necessário determinar se deve ser a cor 'ativada' ou a 'desativada'. Basta verificar a cor do pixel (é cinza puro, então você só precisa dizer o componente 'vermelho') para ver se está acima ou abaixo do valor do progresso passado. O efeito é que qualquer cor cinza abaixo do valor do progresso será ativada, as outras serão desativadas.

Doug.McFarlane
fonte
3
OMG, shaders para uma seção círculo ...
Kromster