# Macro BlueStacks com reconhecimento de imagem e loops

> Monte uma macro BlueStacks com reconhecimento de imagem: capture um template no Asset Lab, ache-o no BlueStacks ou LDPlayer e repita até um estado aparecer.

Source: https://automationmacro.com/pt-BR/docs/guides/image-recognition-macros (Guias, updated 2026-09-05)

Uma macro BlueStacks ou LDPlayer com reconhecimento de imagem olha para a tela antes de agir. Ela toca em um botão porque o botão está lá, fecha um pop-up porque o pop-up apareceu e para quando a mensagem de "sem energia" surge. Este guia monta uma no Macro Automation Studio (MAS) no BlueStacks ou no LDPlayer, do primeiro recorte de template até um loop que espera por um estado. Ele é para quem já não cabe no gravador embutido do emulador.

## Por que um gravador não consegue ramificar

O gravador de macros do BlueStacks ou do LDPlayer guarda toques e tempos e os repete. Ele nunca olha para a tela. Não consegue perceber que um pop-up cobriu o botão, que a tela de carregamento demorou mais hoje ou que o contador chegou a zero. Toda macro gravada é uma linha reta.

Uma macro do MAS é um script Python. Cada chamada de `mas.find_object` tira uma captura de tela e procura nela uma imagem template com template matching do OpenCV. Quando a melhor correspondência atinge o limiar (`threshold=0.8` por padrão), a chamada retorna o centro dela. Caso contrário, retorna `None`. Esse único `if` é o que permite a uma macro ramificar, esperar, tentar de novo e parar.

## Antes de começar

- O MAS está instalado e você fez login com o teste ou um plano. O teste começa com o [download gratuito](/download); veja [Instalar o MAS](/docs/install).
- O BlueStacks ou o LDPlayer está aberto com o ADB ativado e adicionado como dispositivo. Veja o guia do [BlueStacks](/docs/bluestacks-setup-guide) ou do [LDPlayer](/docs/ldplayer-setup-guide).
- O emulador usa uma resolução fixa que você vai manter. Os guias usam 540x960 em retrato a 240 DPI como base.
- Um projeto Baseado em código está aberto no Code Editor. Veja [Primeiros passos](/docs/getting-started).

## Capturar um template no Asset Lab

1. Leve o app até a tela com o botão que você quer encontrar.
2. No Code Editor, abra o painel **Assets** e clique em **Abrir o Asset Helper**. O Asset Lab abre com a tela ao vivo do seu dispositivo.
3. Recorte um retângulo justo em volta do botão e salve. O recorte vai para a sua Biblioteca de imagens e recebe um ID numérico.
4. Repita para todo estado que a macro precisa reconhecer: o botão de fechar do pop-up, o botão de confirmar, a mensagem de "concluído".
5. No painel **Assets**, clique em **Copiar ID** em cada imagem e declare-as no topo do seu script.

```python
import mas

images = mas.images({
    "play_button": 201,
    "close_popup": 202,
    "out_of_energy": 203,
})
```

`mas.images` dá a cada ID um nome legível e diz ao empacotador quais imagens incluir quando você publica. Os templates podem ser jpg, png, gif ou webp, com até 10 MB cada. Veja [Asset Lab](/docs/asset-lab).

> [!TIP]
> Recorte a parte que nunca muda. Um botão com um número em cima só corresponde àquele número; recorte o ícone ao lado do número em vez disso.

## Escolha a chamada certa

| Chamada | Use quando | Retorna |
|---|---|---|
| `find_object(image)` | Você quer uma única olhada na tela agora | `ObjectMatch` ou `None` |
| `find_object_retry(image, total_tries=3, time_sleep=2.0)` | A coisa pode demorar um pouco para aparecer | `ObjectMatch` ou `None` depois da última tentativa |
| `find_any_object([a, b, c])` | Vários templates servem: variantes de pop-up, dois temas | A primeira correspondência, com `matched_template_id` |
| `find_objects(image, max_matches=10)` | O mesmo ícone aparece várias vezes e você quer todas | Uma lista ordenada por confiança |

`find_object_retry` é a primitiva de espera da casa: ela chama `find_object` até `total_tries` vezes com uma pausa fixa de `time_sleep` entre as tentativas que falharam e nunca dorme depois da última. Todo outro argumento nomeado (`threshold`, `search_region`, `screenshot`) é repassado a `find_object`. `find_any_object_retry` faz o mesmo sobre uma lista. Prefira essas a `wait_for_object`, que é mantida para macros antigas.

```python
match = mas.find_object_retry(images.play_button, total_tries=5, time_sleep=1.5)
if match:
    mas.click(match.x, match.y, delay_ms=1000)
else:
    mas.log("Play button did not appear", level="warning")
```

Um `ObjectMatch` tem `x`, `y`, `center` e `matched_template_id`. Ele não carrega uma pontuação, então ajuste o limiar na chamada em vez de ler uma confiança depois.

## Restrinja a busca com search_region

O template matching desliza o template sobre a captura de tela inteira. Uma `Region` limita o deslize a um retângulo, o que é mais rápido e evita sósias em outras partes da tela. As coordenadas são pixels a partir do canto superior esquerdo.

```python
from mas import Region

TOP_BAR = Region(x1=0, y1=0, x2=540, y2=120)

energy_icon = mas.find_object(images.energy_icon, search_region=TOP_BAR)
```

Use uma região sempre que souber onde o elemento vive: a barra superior para contadores, a parte de baixo para botões de ação, o centro para diálogos. Leia a região no Asset Lab; o seletor de pontos mostra as coordenadas em pixels na tela ao vivo.

## Ajuste o limiar

`threshold` vai de 0.0 a 1.0 e o padrão é 0.8. A correspondência precisa atingi-lo para contar.

- Baixe para 0.7 quando um template correto continua falhando por causa de anti-aliasing, uma pequena mudança de escala ou um efeito de brilho no botão.
- Suba para 0.9 quando um template corresponde ao lugar errado, por exemplo dois ícones parecidos em sequência ou um botão que também aparece esmaecido.
- Recorte de novo antes de descer de 0.7. Um limiar tão baixo aceita quase qualquer coisa da mesma cor.

```python
enabled = mas.find_object(images.claim_enabled, threshold=0.9)   # strict: disabled and enabled look alike
glowing = mas.find_object(images.reward_icon, threshold=0.7)     # lenient: the icon has a pulsing glow
```

## Trate pop-ups com find_any_object

Pop-ups são o motivo pelo qual macros gravadas quebram. Dê à macro uma função que conhece todos os pop-ups e chame-a no topo de cada iteração do loop.

```python
POPUPS = [images.close_popup, images.confirm_button, images.later_button]


def clear_popups():
    popup = mas.find_any_object(POPUPS)
    if popup is None:
        return False
    mas.log(f"Closing popup {popup.matched_template_id}")
    mas.click(popup.x, popup.y, delay_ms=800)
    return True
```

`find_any_object` recebe uma lista e retorna a primeira correspondência. `search_strategy="best_match"` escolhe a maior pontuação da lista em vez disso, e `"priority_order"` tenta os templates na ordem em que você os listou. Retornar `True` permite ao loop fazer `continue`, então a próxima iteração olha para uma tela limpa.

## Um LDPlayer macro loop que espera por um estado

Esperar é um loop com um prazo. `find_object_retry` cobre esperas curtas; para uma tela de carregamento que pode levar um minuto, escreva o loop você mesmo para registrar o progresso e desistir de forma limpa.

```python
import time

def wait_for(image, timeout_s=60, every_s=2.0):
    deadline = time.monotonic() + timeout_s
    while time.monotonic() < deadline:
        match = mas.find_object(image)
        if match:
            return match
        time.sleep(every_s)
    return None
```

`time.monotonic()` conta segundos e nunca pula quando o relógio muda, o que o torna o timer certo para um prazo. Toda espera precisa terminar: retorne `None` e deixe quem chamou decidir se para a execução.

## Uma macro BlueStacks completa com reconhecimento de imagem

O script abre uma tela, limpa pop-ups, toca em um botão enquanto ele está ativo e para quando o template "sem energia" aparece ou o limite de tempo acaba. Troque os IDs de imagem pelos da sua Biblioteca de imagens.

```python
import random
import sys
import time

import mas
from mas import Region

images = mas.images({
    "play_button": 201,
    "close_popup": 202,
    "confirm_button": 203,
    "out_of_energy": 204,
    "home_screen": 205,
})

BOTTOM = Region(x1=0, y1=700, x2=540, y2=960)
TIME_BUDGET_S = 15 * 60


def clear_popups():
    popup = mas.find_any_object([images.close_popup, images.confirm_button])
    if popup is None:
        return False
    mas.click(popup.x, popup.y, delay_ms=800)
    return True


def main():
    home = mas.find_object_retry(images.home_screen, total_tries=10, time_sleep=3.0)
    if home is None:
        mas.log("Home screen never appeared", level="error")
        sys.exit(2)

    started = time.monotonic()
    taps = 0
    while time.monotonic() - started < TIME_BUDGET_S:
        if clear_popups():
            continue
        if mas.find_object(images.out_of_energy):
            mas.log("Out of energy, done")
            break
        button = mas.find_object_retry(images.play_button, total_tries=3, time_sleep=2.0, search_region=BOTTOM)
        if button is None:
            mas.log("Play button not found, looking again", level="warning")
            continue
        mas.click(button.x, button.y, delay_ms=1000)
        taps += 1
        time.sleep(random.uniform(0.8, 2.0))

    mas.log(f"Finished after {taps} taps")


if __name__ == "__main__":
    main()
```

Rode com **Executar** (<kbd>F5</kbd>) no Code Editor e acompanhe o console. Quando um template falhar, corrija o recorte primeiro e o limiar depois.

## Solução de problemas

### O template nunca corresponde

O recorte foi feito em uma resolução ou DPI diferente daquela em que o dispositivo roda agora, o recorte inclui fundo que mudou, ou o elemento é animado. Confira as configurações de tela do emulador contra as da captura, recorte mais justo e tente `threshold=0.7`. Se o elemento tem várias aparências, capture cada uma e use `find_any_object`. Um `mas.ImageNotFoundError` significa que o ID não está na sua Biblioteca de imagens; copie-o de novo no painel **Assets**.

### Corresponde à coisa errada

O template é genérico: uma seta simples, um quadrado de uma cor só, uma palavra que aparece duas vezes. Recorte algo único ao lado, suba o limiar para 0.9 ou passe uma `search_region` para a busca ficar onde o elemento vive. `find_objects` mostra todo lugar em que o template pontua acima do limiar, o que deixa o sósia fácil de achar.

### Funciona em uma instância, falha em outra

A segunda instância do emulador roda em outra resolução ou DPI. O template matching é baseado em pixels, então um template capturado a 540x960 não corresponde a 720x1280 nem a um DPI diferente. Coloque toda instância nas mesmas configurações de tela e reinicie. Em um dispositivo na nuvem, crie o dispositivo com o mesmo preset de tela da captura. Se as instâncias precisam ser diferentes, capture um conjunto de templates por resolução e escolha-o com `mas.get_screen_size()`.

Nenhuma ferramenta de automação é 100% livre de riscos, por isso automatize com responsabilidade e a seu próprio critério.
