자동 플레이가 지원되는 게임들이 많다.
하지만, 자동 플레이 중에서도 "특정 상황일 때 특정 행동을 하고싶다" 가 있을 것이다.
간단하게도, 특정 상황이 시간이라면, 타이머를 맞추고 타이머가 울리면 해당 행동을 하면 된다.
하지만, 특정 상황이 "아이템 드랍"이거나, "팝업 발생"과 같은 주로 "시각"에 의해 결정되는 경우가 많았다.
ex) 자동 사냥 도중, 죽었을 때 (상황) 나한테 알림이 왔으면 좋겠다 (행동).
ex) 게임 플레이 도중 게임을 멈추는 팝업이 나오면 (상황) 스킵 하고싶다 (행동).
아래는 실제 프로젝트에서 자동화 한 경험을 토대로, 이전에 만든 파이썬 자동화 아이디어를 소개하고자 한다.
프로젝트를 진행하면서, OPENCV 라이브러리에 대해 전혀 알지 못하고 가져다 쓴 케이스여서, 더 좋은 방법이 많겠지만 아래 방법만으로도 충분히 작동하기는 해서.. 아래 방법으로 진행했다!
(지적은 환영합니다.)
상황
게임을 자동 플레이 시켜 놓으면, 랜덤 확률로 특정 팝업이 등장했었다.
해당 팝업을 수동으로 터치해야 게임 자동플레이를 이어갈 수 있었다.
통계 목적으로 게임 자동 플레이 데이터를 누적시키기 위해 자동 플레이를 진행하며 업무를 진행 했으나, 간헐적으로 팝업이 나오면 수동으로 눌러줘야 하는 번거로움이 있었다.
또한, 업무 시간 외에는 데이터 누적이 되지 않는다는 점도 큰 단점으로 작용했다.
아이디어
팝업이 등장했는지 여부를 판단하기 위해, OPENCV의 이미지 유사도 비교 기능을 활용했다.
게임 화면에서 실시간으로 캡처한 이미지를, 저장해둔 팝업 이미지와 비교하여 일정 유사도 이상이면 "팝업 등장"으로 판단하는 방식이다.
# 이미지 유사도 측정
import cv2
def process_image(image_path):
image = cv2.imread(image_path)
image = cv2.resize(image, (300, 300))
gray_image = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
hist = cv2.calcHist([gray_image], [0], None, [256], [0, 256])
return hist
def compare(hist1, hist2):
return print(f"Correl = {cv2.compareHist(hist1, hist2, cv2.HISTCMP_CORREL)})
image_1 = process_image("image_1.png")
image_2 = process_image("image_2.png")
compare(image_1,image_2)
이렇게 이미지를
- 읽고 (cv2.imread)
- 사이즈를 300x300으로 조정하고 (cv2.resize)
- BGR2GRAY를 통해 흑백 이미지로 변경하고 (cv2.COLOR_BGR2GRAY) // 대부분 흑백이미지로 해서 따라했다.
- calcHist를 통해 이미지를 히스토그램이라고 부르는 데이터로 변경했다.
위 과정을 통해 히스토그램이 도출되면, cv2.compareHist를 이용해 이미지 두개를 비교하면 유사도 값이 나왔다.
아래는 이미지 세개를 샘플로 사용해 보겠다!
1번 이미지와 2번 이미지 히스토그램을 compare 했을 때, 아래와 같은 값이 나왔다.
Correl = 0.5612258110033705
즉, HISTCMP_CORREL 방식을 이용했을 시 56%의 유사도가 있다. 라고 해석할 수 있다.
다음은, 2번 이미지와 3번 이미지 히스토그램을 compare 했을 때, 아래와 같은 값이 나왔다.
Correl = 0.857824606065485
즉, 이건 85.7%의 유사도가 있다. 라고 해석할 수 있다.
위 유사도 측정 알고리즘을, 이렇게 표현할 수 있다.
이러한 자동화를 안드로이드 환경에서 진행했고, 따라서 adb 기능을 활용해 이미지를 실시간으로 갱신할 수 있었다.
os.system('adb shell screencap -p /sdcard/screenshot.png')
os.system('adb pull /sdcard/screenshot.png screenshot.png >nul')
screencap으로 스크린샷을 찍고, pull 기능을 통해 스크린샷을 자동화 스크립트 폴더로 추출, While True 반복문으로 무한 반복하는 기능
이후, 무한 반복으로 실시간 이미지와 미리 찍어둔 팝업 이미지가 일정 유사도보다 클 경우 특정 행동을 하게끔 만들었다.
if correl > 0.85:
print("팝업 발생")
os.system(f'adb shell input tap {tapx} {tapy}')
결과
기존에는 업무시간에만, 데이터 누적이 가능했던 상황을 24시간 가동 가능하게끔 환경을 만들 수 있었다.
또한, 중간에 팝업이 나오면 눌러줘야하는 번거로움 + 업무가 중간에 끊겨 몰입이 깨지는 상황을 제거하여 업무에 몰입할 수 있었다.
Q&A
Q1. Appium으로 element를 찾아서 하는 방식은 왜 시도 안했을까?
A1. Unity 기반 게임이었는데, Unity 기반 게임은 uiautomator로 화면 내 element가 잡히지를 않았다.
Q2. 왜 팝업 화면이 100%가 아니고 85%의 유사도일까?
A2. 팝업이 나온 화면이라도, 뒷 배경에서 이펙트같은 게 나오곤 했다. 만약 이펙트 같은게 없고 뒷 배경도 동일한 게임이라면 100%의 유사도가 나올 것. 이 프로젝트를 진행할 때 실제 게임에서 팝업이 나왔을땐 Correl 값이 0.9 이상으로 나왔었다.
Q3. 따라해봤는데 비슷한 화면이었는데 유사도값이 잘 안나온다..
A3. History 비교 기능에는 'CHISQR', 'INTERSECT', 'BHATTACHARYYA', 'EMD' 와 같은 다양한 방식이 있다. 혹시 이 글을 보고 따라해봤는데 안되면 다양한 방식을 찾아보는것도 추천!
또한, 위 케이스는 전체 화면을 가리는 팝업에 대한 이미지 유사도 분석이지만, 실시간 이미지와 미리 찍어둔 이미지 특정 좌표만을 비교할 수도 있다.
Q4. 더 나은 방법은 없었을까?
A4. 개발중인 게임이라면 로그가 나올 수도 있다. (adb logcat과 같은) 로그에서 팝업이 나오는 시그널이 나올 수도 있다! 로그에서 팝업 시그널이 나오면 특정 좌표를 누르는 형식으로도 자동화가 가능할 것 같다.
결론
OPENCV 라이브러리를 보면서, 아래와 같은 기능을 보게 되었다.
- 화면 내 특정 이미지의 좌표 찾기
이런 기능도 배워두면 좋지 않을까?
또, 우연찮게 OPENCV를 400페이지정도는 될 것 같은 책으로 보게 되었다.
유튜브에서는 Opencv 라이브러리를 이용해서 자동차 번호 인식하는 기능도 쓴다고 한다..!
음.. 그냥 나는 당장은 저정도 기능을 쓰는걸로 만족해야겠다고 다짐했다.. ㅎㅎ;
언젠가 필요하면 배워보는게 좋을 것 같다!
'자동화' 카테고리의 다른 글
[자동화] 테스트 자동화 ROI 계산 (1) | 2025.02.19 |
---|