Guías
Macro BlueStacks con reconocimiento de imagen y bucles
Crea una macro BlueStacks con reconocimiento de imagen: captura una plantilla en Asset Lab, búscala en BlueStacks o LDPlayer y repite hasta ver un estado.
- Windows
- Mac
- Emulador
- Dispositivo en la nube
- Teléfono
- Studio
- SDK de Python
En esta página
- Por qué un grabador no puede ramificarse
- Antes de empezar
- Capturar una plantilla en Asset Lab
- Elegir la llamada adecuada
- Acotar la búsqueda con search_region
- Ajustar el umbral
- Gestionar avisos emergentes con find_any_object
- Un LDPlayer macro loop que espera a un estado
- Una macro BlueStacks completa con reconocimiento de imagen
- Solución de problemas
- La plantilla nunca coincide
- Coincide con lo que no debe
- Funciona en una instancia y falla en otra
Una macro BlueStacks con reconocimiento de imagen, o una de LDPlayer, mira la pantalla antes de actuar. Toca un botón porque el botón está ahí, cierra un aviso porque el aviso apareció y se detiene cuando aparece el mensaje de “sin energía”. Esta guía construye una macro BlueStacks con reconocimiento de imagen en Macro Automation Studio (MAS), también válida para LDPlayer, desde el primer recorte de plantilla hasta un bucle que espera a un estado. Está pensada para quien se ha quedado corto con el grabador integrado del emulador.
Por qué un grabador no puede ramificarse
El grabador de macros de BlueStacks o LDPlayer guarda toques y tiempos y los reproduce. Nunca mira la pantalla. No puede saber que un aviso tapó el botón, que la pantalla de carga tardó más hoy o que el contador llegó a cero. Toda macro grabada es una línea recta.
Una macro de MAS es un script de Python. Cada llamada a mas.find_object hace una captura y busca en ella una imagen de plantilla con la coincidencia de plantillas (template matching) de OpenCV. Cuando la mejor coincidencia alcanza el umbral (threshold=0.8 por defecto), la llamada devuelve su centro. Si no, devuelve None. Ese único if es lo que permite a una macro ramificarse, esperar, reintentar y parar.
Antes de empezar
- MAS está instalado y con sesión iniciada con la prueba o un plan. La prueba empieza con la descarga gratuita; consulta Instalar MAS.
- BlueStacks o LDPlayer está en marcha con ADB activado y añadido como dispositivo. Consulta la guía de BlueStacks o de LDPlayer.
- El emulador usa una resolución fija que vas a mantener. Las guías usan 540x960 en vertical a 240 DPI como línea base.
- Un proyecto basado en código está abierto en el Code Editor. Consulta Primeros pasos.
Capturar una plantilla en Asset Lab
- Lleva la app a la pantalla con el botón que quieres encontrar.
- En el Code Editor, abre el panel Recursos y haz clic en Abrir Asset Helper. Asset Lab se abre con la pantalla en directo de tu dispositivo.
- Recorta un rectángulo ajustado alrededor del botón y guárdalo. El recorte va a tu biblioteca de imágenes y recibe un ID numérico.
- Repite con cada estado que la macro deba reconocer: el botón de cerrar del aviso, el botón de confirmar, el mensaje de “hecho”.
- En el panel Recursos, haz clic en Copiar ID en cada imagen y decláralas al principio de tu script.
import mas
images = mas.images({
"play_button": 201,
"close_popup": 202,
"out_of_energy": 203,
})mas.images da a cada ID un nombre legible e indica al empaquetador qué imágenes incluir cuando publiques. Las plantillas pueden ser jpg, png, gif o webp, de hasta 10 MB cada una. Consulta Asset Lab.
Elegir la llamada adecuada
| Llamada | Úsala cuando | Devuelve |
|---|---|---|
find_object(image) | Quieres una sola mirada a la pantalla ahora mismo | ObjectMatch o None |
find_object_retry(image, total_tries=3, time_sleep=2.0) | El elemento puede tardar un momento en aparecer | ObjectMatch o None tras el último intento |
find_any_object([a, b, c]) | Valen varias plantillas: variantes de un aviso, dos temas | La primera coincidencia, con matched_template_id |
find_objects(image, max_matches=10) | El mismo icono aparece varias veces y las quieres todas | Una lista ordenada por confianza |
find_object_retry es la primitiva de espera de la casa: llama a find_object hasta total_tries veces con una pausa fija time_sleep entre intentos fallidos y nunca duerme después del último. Todos los demás argumentos con nombre (threshold, search_region, screenshot) se reenvían a find_object. find_any_object_retry hace lo mismo sobre una lista. Prefiere estas a wait_for_object, que se mantiene por las macros antiguas.
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")Un ObjectMatch tiene x, y, center y matched_template_id. No lleva puntuación, así que ajusta el umbral en la llamada en lugar de leer una confianza después.
Acotar la búsqueda con search_region
La coincidencia de plantillas desliza la plantilla por toda la captura. Una Region limita el barrido a un rectángulo, lo que es más rápido y evita parecidos en otras partes de la pantalla. Las coordenadas son píxeles desde la esquina superior izquierda.
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)Usa una región siempre que sepas dónde vive el elemento: la barra superior para los contadores, la inferior para los botones de acción, el centro para los diálogos. Lee la región en Asset Lab; el selector de puntos muestra las coordenadas en píxeles sobre la pantalla en directo.
Ajustar el umbral
threshold va de 0.0 a 1.0 y por defecto es 0.8. La coincidencia debe alcanzarlo para contar.
- Bájalo a 0.7 cuando una plantilla correcta sigue fallando por el suavizado, un ligero cambio de escala o un brillo en el botón.
- Súbelo a 0.9 cuando una plantilla coincide en el sitio equivocado, por ejemplo dos iconos parecidos seguidos o un botón que también aparece en gris.
- Vuelve a recortar antes de bajar de 0.7. Un umbral tan bajo acepta casi cualquier cosa del mismo color.
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 glowGestionar avisos emergentes con find_any_object
Los avisos emergentes son el motivo por el que se rompen las macros grabadas. Dale a la macro una función que conozca todos los avisos y llámala al principio de cada pasada del bucle.
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 Truefind_any_object recibe una lista y devuelve la primera coincidencia. search_strategy="best_match" elige en cambio la puntuación más alta de la lista, y "priority_order" prueba las plantillas en el orden en que las listaste. Devolver True permite al bucle hacer continue, así que la siguiente pasada mira una pantalla limpia.
Un LDPlayer macro loop que espera a un estado
Esperar es un bucle con un plazo. find_object_retry cubre las esperas cortas; para una pantalla de carga que puede tardar un minuto, escribe el bucle tú mismo para registrar el progreso y rendirte limpiamente.
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 Nonetime.monotonic() cuenta segundos y nunca salta cuando cambia el reloj, lo que lo convierte en el temporizador adecuado para un plazo. Toda espera debe terminar: devuelve None y deja que quien llama decida si detener la ejecución.
Una macro BlueStacks completa con reconocimiento de imagen
El script abre una pantalla, cierra los avisos, toca un botón mientras está activo y se detiene cuando aparece la plantilla de “sin energía” o se agota el presupuesto de tiempo. Sustituye los ID de imagen por los de tu biblioteca de imágenes.
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()Ejecútalo con Ejecutar (F5) en el Code Editor y observa la consola. Cuando una plantilla falle, arregla primero el recorte y después el umbral.
Solución de problemas
La plantilla nunca coincide
El recorte se hizo con una resolución o un DPI distintos de los que usa ahora el dispositivo, el recorte incluye fondo que cambió, o el elemento está animado. Compara los ajustes de pantalla del emulador con los de la captura, recorta más ajustado y prueba threshold=0.7. Si el elemento tiene varios aspectos, captura cada uno y usa find_any_object. Un mas.ImageNotFoundError significa que el ID no está en tu biblioteca de imágenes; cópialo otra vez desde el panel Recursos.
Coincide con lo que no debe
La plantilla es genérica: una flecha simple, un cuadrado de un solo color, una palabra que aparece dos veces. Recorta algo único junto a ella, sube el umbral a 0.9 o pasa una search_region para que la búsqueda se quede donde vive el elemento. find_objects te muestra todos los sitios donde la plantilla supera el umbral, lo que hace fácil detectar el parecido.
Funciona en una instancia y falla en otra
La segunda instancia del emulador va a otra resolución u otro DPI. La coincidencia de plantillas se basa en píxeles, así que una plantilla capturada a 540x960 no coincide a 720x1280 ni con otro DPI. Pon todas las instancias con los mismos ajustes de pantalla y reinícialas. En un dispositivo en la nube, crea el dispositivo con el mismo preajuste de pantalla con el que capturaste. Si las instancias deben ser distintas, captura un juego de plantillas por resolución y elígelo con mas.get_screen_size().
Ninguna herramienta de automatización está libre de riesgos al 100 %, así que automatiza con responsabilidad y bajo tu propio criterio.
Siguientes pasos
Páginas relacionadas
Gracias. Si algo está mal, cuéntanoslo en Discord.
¿Dudas? Pregunta en Discord