[철권7]무브리스트 1탄 - 크롤링

Aromahyang·2023년 1월 9일

개인프로젝트

목록 보기
1/5
post-thumbnail

글 시작에 앞서 결과물은 tekken7.movelist.xyz에서 볼 수 있다!

🎉 프로젝트의 시작

보기 편하고 한국어를 지원하는 철권7 무브리스트를 만들자!

오락실 세대 사람이거나 종종 오락실을 가본 사람이라면 철권이란 게임을 알 것이다.
내가 플레이해본 게임이 열 손가락 안에 들 정도로 게임 문외한(게임 경력 2-3년)이지만, 철권의 존재를 알고 있고 오락실에서 아무 버튼을 두들겨본 정도가 전부다.
그런데 스팀(Steam)에 철권7이 등록되어 있어 접근성이 좋아졌고, 내가 누른 대로 동작하고 컨트롤러를 조작하는 재미가 있어 철권에 빠져들었다.
그렇게 2022년 초에 철권7을 시작하며 게임 시스템에 대해 공부해가던 중 여러 프레임표를 접했는데 불편함이 있었다.

먼저 한글로 된 프레임표 사이트가 없다.
한국에서 철권을 하는 사람들이 꽤 있을 텐데, 기술명, 히트(혹은 카운터히트) 후 상황(상대 다운, 공중 등), 기술 특징(호밍기, 월 바운드 등) 등이 영어로 표기되어 있다.
특히 기술명은 국가마다 다르게 부르기 때문에 다국어를 지원해주지 않는 프레임표 사이트에서 한국어 기술명을 검색할 수가 없다.
그래서 한국어도 지원되는 다국어 지원 프레임표 사이트가 있으면 좋겠다고 생각했다.

두 번째로 커맨드가 일반 텍스트로 표기되어 있어 보기 어렵다.
가뭄 속 단비처럼 한국어를 제공하는 철권 프레임표 안드로이드 앱이 있지만, 이 앱을 포함한 여러 프레임표 사이트는 커맨드를 일반 텍스트로 보여주고 있다.
그런데 동양권과 서양권은 커맨드를 표시하는 방법도 다르다. 😱
동양권에서는 방향을 숫자 키패드로 표현하고 손과 발은 영어 문자로 표현하는 반면, 서양권에서는 방향을 영어 문자로, 손과 발은 숫자로 표현한다.
예를 들어, 아래 그림을 동양권에서는 6 AP로, 서양권에서는 f 1+2,3로 표현한다.

뉴비가 여러 프레임표 사이트를 보며 기술의 정보를 접하기 위해 사전에 알아야 할 정보가 너무 많았다고 느꼈다.
그리고 일반 텍스트보다 이미지로 커맨드를 표시하는 게 직관적이고 국가 상관없이 보기 편하다고 생각했다.
그래서 이번 프레임표에는 커맨드를 이미지로 표시하고자 하였다.

📦 데이터를 모으자(feat. FRAMEDATA for TK7FR)

APK 파일 다운로드 → Dex2Jar과 JD-GUI로 Java 코드 추출 → Java 코드에서 정보 추출해 JSON 형태로 저장

유일하게 한국어로 프레임 정보를 모아둔 안드로이드 앱, FRAMEDATA for TK7FR에서 데이터를 크롤링하였다.

먼저 안드로이드 앱에서 정보를 얻어야 하므로 apk(Android Application Package) 파일이 필요하다.
apk 파일을 다운받을 수 있는 사이트를 사용하여 apk 파일을 다운받을 수 있다.
다음으로 apk를 java 코드로 복원하는 과정을 거쳐야 가공할 수 있는 데이터를 얻을 수 있다.
이때 Dex2Jar와 JD-GUI를 이용하였다.
기본적으로 윈도우에서 실행하는 방법에 대해 작성했고 맥에서는 사용법이 조금 다르기 때문에 맥 사용자라면 🍏이 붙은 문장을 참고하면 되겠다.

Dex2Jar.dex 파일을 .jar로 변환해주는 툴이다.
dex는 안드로이드 런타임에서 궁극적으로 실행되는 코드(기계어)가 포함되어 있다.
jar(Java Archive)은 java 클래스 파일과 클래스 관련 리소스가 하나의 파일로 모아서 앱을 빌드하기 위해 만들어진 파일 포맷이다.
Dex2Jar을 이용하려면 먼저 Java가 설치되어 있어야 한다.
Java가 설치되어 있다면 Dex2Jar의 Github 코드를 다운받아서 dex2jar Usage 부분을 따라 하면 jar 파일을 얻을 수 있다.
⚠ 만약 윈도우에서 실행한다면 unzipped directory(dex-tools/build/distributions/dex-tools-X.X-SNAPSHOT)에 apk 파일을 두고 Usage의 4번째 단계를 실행하는 것을 추천한다.
unzipped directory(dex-tools/build/distributions/dex-tools-X.X-SNAPSHOT)와 다른 위치에 있는 apk 파일을 커맨드에 입력할 경우 command not found 에러가 날 수 있기 때문이다.
⚠ 그리고 윈도우에서 sh d2j-dex2jar.sh -f ~/path/to/apk_to_decompile.apk를 실행하면 Could not find or load main class com.googlecode.dex2jar.tools.Dex2jarCmd 에러가 나므로 ./d2j-dex2jar.bat ~/path/to/apk_to_decompile.apk를 실행해야 한다.(참고)
🍏 맥에서 실행하려면 dex2jar Usage를 따라하지 말고, Release에서 최신 버전을 다운 받고 sh d2j-dex2jar.sh [디컴파일 할 APK파일명]를 실행하면 된다.

JD-GUI는 디컴파일한 자바 클래스 파일을 소스 코드로 보도록 도와준다.
jd-gui-[버전].jar을 다운받으면 되는데, 맥에서는 직접 프로그램을 실행하면 경고창이 뜨면서 쓸 수 없다.
🍏 따라서 JD-GUI를 설치한 경로에서 java -jar jd-gui-[버전].jar cli을 실행해야만 JD-GUI가 정상적으로 실행된다.
위 과정에서 생성된 jar 파일을 JD-GUI 툴을 통해 내부 내용을 확인할 수 있다.

필요한 정보가 어디에 있는지 확인했다면 JD-GUI에서 fileSave All Sources를 통해 java 파일들을 zip 형식으로 다운받으면 된다.
⚠ 만약 Save All Sources를 할 때 80% 정도에서 더 이상 진행되지 않는다면 JD-GUI의 버전을 낮춰서 해보면 다운될 수도 있다.
나는 윈도우에서 1.4.2 버전으로 했을 때 문제 없이 다운되었다.

앱의 Java 코드를 보니 규칙적인 패턴으로 작성되어 있어 정규식을 이용하여 필요한 정보를 가져왔다.

📹 게임에서 모션 녹화

아두이노를 이용하여 일정 시간 동안 모션 녹화

기술별 모션도 프레임과 함께 볼 수 있으면 좋겠어서 게임 속 모션을 녹화하였다.
먼저 철권7 게임에서 캐릭터별 커맨드 목록을 제공한다.

그래서 단순히 G매크로와 같은 매크로 프로그램을 써서 화면 녹화 기능 ON → 특정 시간 동안 녹화 → 화면 녹화 기능 OFF → 다음 커맨드로 이동 과정을 설정하면 되겠다고 생각했다.
그런데 G-매크로를 포함한 다른 매크로 프로그램이 동작하지 않았다.. 😶
겜알못(게임을 알지도 못하는 사람)이라 100% 확신은 없지만, 매크로를 통한 키 입력을 철권7이 입력값으로 인식하지 않는 것 같았다.
콘솔 게임이어서 그런지 직접 입력된 값에만 게임이 반응하는 것 같다..
그래서 아두이노를 이용하여 반복 과정을 설정하였다.
구매한 아두이노는 레오나르도 R3 호환보드다.
녹화 단축키(윈도우 기준 Window + Alt + r)와 녹화 개수를 지정하여 일정 시간 동안 녹화하였다.

void record() {
  // 윈도우 녹화 단축키
  Keyboard.press(KEY_LEFT_GUI);
  delay(100);
  Keyboard.press(KEY_LEFT_ALT);
  delay(100);
  Keyboard.press('r');
  delay(100);
  Keyboard.releaseAll();
}

void next() {
  // 다음 커맨드로 이동
  Keyboard.press(arrowDownKey);
  delay(100);
  Keyboard.releaseAll();
}

🎯 영상과 기술 커맨드 매치

OpenCV와 Tesseract를 이용하여 기술과 모션 비디오 대응

모션 녹화한 영상에서 특정 프레임을 따와서 기술명을 추출하였는데, OpenCVTesseract를 사용하여 OCR을 진행하였다.

import cv2
import numpy as np
import pytesseract as tes

vidcap = cv2.VideoCapture("비디오_파일")
frame_count = 0
target_frame = 2 # 2프레임을 타겟으로 하였다.
while vidcap.isOpened():
	success, image = vidcap.read()
	if not success:
		break
	if frame_count == target_frame:
    	# 기술명의 위치가 체력바 아래에 고정되어 있기 때문에 해당 위치의 이미지만 가져왔다.
		cropped_image = image[149:196, 290:1300]
        gray_cropped_image = cv2.cvtColor(cropped_image, cv2.COLOR_BGR2GRAY)
        sharpen_kernel = np.array([[-1, -1, -1], [-1, 9, -1], [-1, -1, -1]])
        sharpen = cv2.filter2D(gray_cropped_image, -1, sharpen_kernel)
        thr = cv2.threshold(sharpen, 0, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU)[1]
        # 가공한 이미지에서 기술명 추출
        result = tes.image_to_string(thr)[:-1]
	elif frame_count > target_frame:
    	frame_count = 0
        break
    frame_count += 1
vidcap.release()

크롭한 이미지에서 영문 텍스트를 추출하기 위해 색상을 그레이 스케일로 변환하고(cv2.cvtColor, cv2.threshold), 그레이 스케일이 적용된 이미지를 조금 더 선명하게 변환(cv2.filter2D)하였다.
각 함수에 대해 간단하게 설명하면 아래와 같다.

  • cv2.cvtColor(src, code[, dst[, dstCn]]): 이미지의 색상 공간을 변환하는 함수
    • src: 색상 공간을 변환시킬 이미지
    • code: 바꿀 색상 공간 코드
    • dst: src와 동일한 사이즈와 깊이를 가진 결과 이미지
    • dstCn: 결과 이미지의 채널 수
  • cv2.filter2D(src, ddepth, kernel[, dst[, anchor[, delta[, borderType]]]]): 필터를 적용하여 이미지를 변환하는 함수
    • src: 필터를 적용할 이미지
    • ddepth: 결과 이미지의 깊이(depth) 타입
    • kernerl: 필터 마스크 행렬(컨볼루션 커널)
    • dst: src와 동일한 크기와 채널 수를 가진 결과 이미지
    • anchor: kernel의 고정점
    • delta: 결과 이미지에 저장되기 전에 필터링된 픽셀에 더할 값
    • borderType: 가장자리 픽셀 확장 방식
  • cv2.threshold(src, thresh, maxval, type[, dst]): 이미지의 배열 요소에 기준을 적용하여 흑백 이미지를 출력
    • src: 입력 이미지
    • thresh: 임계값
    • maxval: 임계값을 넘었을 때 적용할 값
    • type: 임계값 타입
    • dst: 입력 이미지와 동일한 사이즈와 깊이를 가지는 결과 배열

영상 관련 변환은 OpenCV 문서를 참고하는 것을 추천한다.
나는 영상 처리를 전문으로 하려는 게 아니기 때문에 OpenCV 함수들을 활용하는 선에서 마무리하였다.
위 코드에서 추출한 텍스트와 크롤링한 데이터의 영문 기술명을 비교하여 일치하는 기술에는 모션 비디오를 보여주도록 처리하였다.

마무리

APK 디컴파일링과 정규식, OCR, 아두이노 등을 이용하여 데이터를 크롤링하고 데이터에 모션 영상이 합쳐진 새로운 데이터를 얻을 수 있었다.
철권7 덕분에 처음으로 게임을 진지하게 파보고, 게임 속 정보를 이용하여 웹 서비스를 만들어보며 다양한 기술을 접할 수 있었다.
꾸준히 사용해보며 더 개선할 부분이 있는지 혹은 사용자의 의견을 들어보며 유지보수해나갈 계획이다.

참고

크롤링 대상 앱: FRAMEDATA for TK7FR
[Android] 안드로이드 앱 Decompile (Reverse Engineering)
Could not find or load main class com.googlecode.dex2jar.tools.Dex2jarCmd
pytesseract

profile
보여주기 식이 아닌 경험하고 배운 것을 소소하게 기록하였습니다.

0개의 댓글