# 모바일 게임 매크로 만들기: 모든 안드로이드 게임 봇 제작

> Macro Automation Studio에서 모바일 게임 매크로 만들기: 마켓플레이스에서 설치하거나, MAS Agent에 요청하거나, BlueStacks에서 파이썬으로 직접 작성합니다.

Source: https://automationmacro.com/ko/docs/make-a-bot-for-any-game (가이드, updated 2026-09-05)

이 튜토리얼은 Macro Automation Studio(MAS)에서 모바일 게임 매크로 만들기를 설명합니다. 설치된 게임에서 시작해 한 계정이나 여러 계정에서 예약 실행되는 봇까지 만듭니다. 모바일 게임 매크로 만들기의 핵심은 화면입니다. MAS 봇은 이미지로 버튼을 찾고, OCR로 카운터를 읽고, 사람과 비슷한 타이밍으로 탭합니다. Windows나 Mac에서 처음 봇을 만드는 분을 위한 페이지입니다.

<div class="doc-video" data-video="SDrdMIV49a8" data-title="How to make a bot for any game"></div>

## 시작하기 전에

- MAS가 설치되어 있고 무료 체험 또는 요금제로 로그인한 상태. [MAS 설치](/docs/install)를 참고하세요.
- MAS가 조작할 수 있는 기기에서 게임이 실행 중: 이 컴퓨터의 에뮬레이터, 클라우드 기기, 또는 본인 휴대폰. [기기](/docs/devices)를 참고하세요.
- 기기가 기기 그룹에 있고 카드가 Error가 아닌 Stopped를 표시. 추가 과정은 [시작하기](/docs/getting-started) 페이지에 있습니다.
- 파이썬 경로에는 그 밖에 아무것도 필요 없습니다. MAS는 `mas` 패키지가 포함된 자체 Python 환경을 함께 제공합니다.

## 안드로이드 게임 매크로 만들기: 세 가지 방법

### Marketplace에서 설치

누군가 이미 내 게임용 봇을 만들어 두었다면 가장 빠른 길입니다.

1. **Marketplace**를 열고 게임을 검색합니다.
2. 목록을 열고 **Download**를 클릭합니다. 봇이 **Macros**에 나타납니다.
3. **Device Groups**에서 그룹을 열고, 기기의 **Macro** 선택기에서 봇을 고른 뒤 **Start**를 클릭합니다.

스크린샷이 포함된 전체 과정은 [마켓플레이스에서 매크로 실행하기](/docs/run-macro-from-marketplace)에 있습니다. [Whiteout Survival](/whiteout-survival-bot), [Kingshot](/kingshot-bot), [Last Asylum: Plague](/last-asylum-plague-bot)용 완성 봇이 있습니다.

### MAS Agent에 요청

목록에 없는 게임을 위한 노코드 경로입니다.

1. **Agent**를 열고 기기를 고릅니다.
2. 언제 멈춰야 하는지를 포함해 루틴을 한 문장으로 설명하고 **Author**를 클릭합니다.
3. 에이전트가 물으면 답합니다. 추측하지 않고 멈춰서 질문합니다.
4. 매크로가 검증 실행 3회를 통과하면 **Add to My Macros**를 클릭합니다.

작성에는 AI 크레딧이 들지만, 완성된 매크로 실행에는 들지 않습니다. 결과물은 열어서 편집할 수 있는 일반 파이썬 프로젝트입니다. [MAS Agent](/docs/agent)를 참고하세요.

### 파이썬으로 직접 만들기

봇이 내리는 모든 결정을 완전히 제어합니다. 이 페이지의 나머지는 이 경로입니다.

## 파이썬으로 안드로이드 게임 자동화하기

### 1. 봇이 봐야 할 것 정리

루틴을 손으로 한 번 플레이하며 거치는 모든 화면을 적습니다. 누르는 버튼, 방해하는 팝업, 에너지를 보여 주는 카운터, 끝났음을 뜻하는 메시지. 각 항목이 템플릿 이미지나 OCR 영역이 됩니다. 정지 상태를 아는 봇은 눈먼 채로 돌지 않습니다.

### 2. 프로젝트 만들기

1. **Macros**를 열고 **Create New Project**를 클릭합니다.
2. **Code-Based**를 선택하고 **Target Device**를 mobile로 설정한 뒤 프로젝트 이름을 입력하고 **Create Project**를 클릭합니다.
3. 프로젝트를 엽니다. Code Editor에 MAS가 실행하는 파일인 `src/app.py`가 표시됩니다.

### 3. Asset Lab에서 템플릿 캡처

1. 기기에서 게임을 시작하고 버튼이 있는 화면으로 갑니다.
2. Code Editor에서 **Assets** 패널을 열고 **Open Asset Helper**를 클릭합니다. Asset Lab이 실제 화면 위에 열립니다.
3. 버튼 주위를 꼭 맞게 사각형으로 잘라 저장합니다. Image Library에 숫자 ID와 함께 저장됩니다.
4. 팝업 닫기 버튼, 확인 버튼, "out of energy" 메시지에 대해 반복합니다.
5. **Assets** 패널로 돌아와 각 이미지에서 **Copy ID**를 쓰고 스크립트 맨 위의 `mas.images`에 ID를 붙여 넣습니다.

잘라낸 조각은 작고 구별되게 유지하세요. 화면 전체는 정확히 그 화면에서만 일치하지만, 버튼은 버튼이 나타나는 곳 어디서든 일치합니다. 템플릿은 jpg, png, gif, webp를 쓸 수 있고 각각 최대 10 MB입니다. [Asset Lab](/docs/asset-lab) 페이지에서 도구를 자세히 다룹니다.

### 4. OCR 영역 캡처

1. Asset Lab에서 읽고 싶은 카운터 주위에 상자를 그립니다.
2. 실시간 OCR 테스트를 실행하고 숫자가 깨끗하게 읽힐 때까지 상자를 조입니다.
3. 좌표를 스크립트의 `Region(x1, y1, x2, y2)`에 복사합니다.

텍스트가 잘 읽히지 않으면 [OCR 가이드](/docs/guides/ocr-text-reading)에서 분할 모드와 색 변환을 설명합니다.

### 5. 반복문 작성

아래 반복문은 모든 게임 봇이 공유하는 형태입니다. 각 줄이 SDK 호출에 대응합니다:

- `find_object_retry`는 `time_sleep=2.0`초 간격으로 최대 `total_tries=3`번 버튼을 찾고, 아무것도 맞지 않으면 `None`을 반환합니다.
- `click(x, y, delay_ms=1000)`은 일치한 곳의 중심을 탭하고 게임이 반응하도록 1초 기다립니다.
- `read_text(region, psm=7)`은 카운터를 한 줄로 읽습니다.
- `time.sleep(random.uniform(0.8, 2.0))`은 회차 사이에 사람다운 쉼을 넣습니다.
- `mas.save`와 `mas.retrieve`가 회차 카운터를 보관하므로, 멈춘 실행이 멈춘 곳에서 이어집니다.
- 정지 조건이 반복문을 끝냅니다. 예제는 다섯 가지를 씁니다: 최대 회차 수, 시간 예산, "out of energy" 템플릿, 낮은 카운터, 연속 실패 횟수.

### 6. 실행하고 로그 읽기

Code Editor에서 기기를 고르고 **Run**(<kbd>F5</kbd>)을 클릭합니다. 모든 `mas.log` 줄이 실행 콘솔에 나타납니다. **Stop**은 <kbd>Shift</kbd>+<kbd>F5</kbd>입니다. 한 번에 하나씩 고치세요. 템플릿을 찾지 못하면 로직을 건드리기 전에 다시 자르세요.

### 7. 예약하기

1. **Scheduler**를 열고 **Create New Schedule**을 클릭합니다.
2. **Name**을 입력하고 **Macro**와 **Emulator Port**를 고르고 **Date**와 **Time**을 설정합니다.
3. **Recurrence**를 **Daily**로 설정하고 **Create Schedule**을 클릭합니다.

스케줄러는 앱 안에서 실행되므로 앱을 열어 두어야 합니다. [스케줄러](/docs/scheduler)와 [반복과 예약 실행](/docs/guides/loops-and-scheduling)을 참고하세요.

### 8. 기기 그룹에서 실행

모든 계정의 에뮬레이터 인스턴스를 그룹 하나에 기기로 추가하고, 각 기기에 봇을 지정한 뒤 **Start All**을 클릭합니다. 계정 이름 같은 기기별 값은 설정 프로필에서 옵니다. [멀티 인스턴스 자동화](/docs/guides/multi-instance)를 참고하세요.

## 완전한 예제 봇

이미지 ID와 영역을 직접 캡처한 값으로 바꾸세요. 스크립트는 에너지가 낮아지거나, 시간 예산이 지나거나, 30회가 끝날 때까지 자원을 채집하며, 재시작을 견딥니다.

```python
import random
import sys
import time

import mas
from mas import Region

images = mas.images({
    "attack_button": 101,
    "confirm_button": 102,
    "close_popup": 103,
    "out_of_energy": 104,
})

ENERGY_REGION = Region(x1=380, y1=20, x2=520, y2=60)
TASK = "farm_bot"
MAX_ROUNDS = 30
TIME_BUDGET_S = 20 * 60
MIN_ENERGY = 10


def pause(low=0.8, high=2.0):
    time.sleep(random.uniform(low, high))


def read_energy():
    result = mas.read_text(region=ENERGY_REGION, psm=7)
    digits = "".join(ch for ch in result.text if ch.isdigit())
    return int(digits) if digits else None


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


def main():
    rounds = mas.retrieve(TASK).get("rounds", 0)
    misses = 0
    started = time.monotonic()
    mas.log(f"Starting at round {rounds}")

    while rounds < MAX_ROUNDS:
        if time.monotonic() - started > TIME_BUDGET_S:
            mas.log("Time budget reached", level="warning")
            break
        if clear_popups():
            continue
        if mas.find_object(images.out_of_energy):
            mas.log("Out of energy, stopping")
            break
        energy = read_energy()
        if energy is not None and energy < MIN_ENERGY:
            mas.log(f"Energy {energy} is below {MIN_ENERGY}, stopping")
            break
        button = mas.find_object_retry(images.attack_button, total_tries=3, time_sleep=2.0)
        if button is None:
            misses += 1
            mas.log(f"Attack button not found ({misses})", level="warning")
            if misses >= 5:
                mas.log("Giving up after 5 misses", level="error")
                sys.exit(2)
            continue
        misses = 0
        mas.click(button.x, button.y, delay_ms=1000)
        pause()
        rounds += 1
        mas.save(TASK, {"rounds": rounds})
        mas.log(f"Round {rounds} of {MAX_ROUNDS}")

    if rounds >= MAX_ROUNDS:
        mas.clear(TASK)
    mas.log(f"Finished with {rounds} rounds")


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

`sys.exit(2)`는 실행을 실패로 표시하므로 `macro.failed`를 구독한 웹훅이 이를 전달받습니다. 정상 종료는 `macro.completed`를 보고합니다. [웹훅](/docs/webhooks)을 참고하세요.

## 봇을 안정적으로 유지하는 팁

- **해상도.** 템플릿과 영역은 캡처할 때의 해상도와 DPI에 속합니다. 봇을 실행하는 모든 기기를 같은 설정으로 유지하세요. 에뮬레이터 가이드는 세로 540x960, 240 DPI를 기준선으로 씁니다.
- **템플릿 위생.** 버튼만 자르고 주변 배경은 넣지 마세요. 게임 업데이트로 그림이 바뀌면 다시 자르세요. 버튼의 모습이 두 가지면 둘 다 캡처하고 `find_any_object`로 찾으세요.
- **정지 조건.** 모든 반복문에는 최소 두 개가 필요합니다: 카운터나 시간 예산, 그리고 "끝"을 뜻하는 화면 상태. 없으면 봇은 사용자가 알아챌 때까지 돕니다.
- **속도 늦추기.** 회차 사이 1초에서 2초의 쉼은 비용이 거의 없고 덜 기계적으로 보입니다.
- **탭이 아니라 결정을 기록하기.** `mas.log("Energy 8, stopping")`은 실행이 왜 끝났는지 알려 주지만, 클릭 백 개의 로그는 그렇지 않습니다.

## 잘못될 수 있는 것

### 버튼을 끝내 찾지 못함

잘라낸 조각이 너무 크거나, 임계값이 그림에 비해 너무 엄격하거나, 기기가 캡처할 때와 다른 해상도로 실행 중입니다. 더 조여서 다시 자른 뒤 호출에 `threshold=0.7`을 시도하세요. [이미지 인식 가이드](/docs/guides/image-recognition-macros)에 전체 점검 목록이 있습니다.

### 카운터가 엉뚱한 숫자를 읽음

영역에 옆 아이콘이 포함되어 있거나, 텍스트가 어두운 배경 위의 밝은 글자입니다. Asset Lab에서 상자를 조이고 `color_conversion=ColorConversion.BLACK_WHITE`를 넘기세요. [OCR 가이드](/docs/guides/ocr-text-reading)에서 신뢰도를 확인하는 방법을 보여 줍니다.

### 둘째 날 봇이 바로 멈춤

저장된 카운터가 이미 한계에 있습니다. 예제는 `MAX_ROUNDS`에 도달하면 스토리지를 지웁니다. 그 부분을 바꿨다면 임시 스크립트에서 `mas.clear("farm_bot")`를 한 번 호출하세요.

### 클릭이 버튼 옆에 떨어짐

캡처 후 에뮬레이터의 해상도나 DPI가 바뀌었거나, 창이 세로가 아닙니다. 에뮬레이터 디스플레이를 캡처 설정으로 되돌리고 여전히 빗나가는 것을 다시 캡처하세요.

100% 안전한 자동화 도구는 없으므로, 책임감을 가지고 본인의 판단에 따라 자동화하세요.
