본문 바로가기
공부

[매크로] 게임 매크로 만들어보기

by 꾸돼지 2025. 6. 20.
320x100

예전부터 한 번 만들어보고 싶었다.

 

모바일 게임 중 PC에서 돌아가는 게임들이 많다.

나는 Java의 Robot과 UI를 이용해서 간단한 매크로를 만들어보았다.

 

 

대충 만든 기능 흐름도

큰 덩어리는 두 개다.

 

1. 설정

 - Swing을 이용해서 간단한 UI를 만들었다.

 - 클릭해야 하는 게임 내의 버튼을 overlay 한 java ui 위의 마우스 클릭을 통해 영역을 선택하게 만들었다.

 - 관리자 권한으로 실행해야 한다.

 

2. 매크로

 - 설정 화면에서 반복할 횟수를 정한다.

 

 - 현재 화면을 캡처한다.

 - if 특정 이미지가 캡처에 존재하면 해당 이미지를 클릭한다.

 - 화면을 드래그하여 남은 영역을 출력한다.

 - 현재 화면을 캡처한다.

 - if 특정 이미지가 캡처에 존재하면 해당 이미지를 클릭한다.

 

 - 정해진 반복 횟수만큼 위의 내용을 반복한다.

 

 


 

 

일단은 설정하는 화면과 마우스 이벤트를 처리하는 개략 클래스 다이어그램

 

 

Swing으로 UI 만드는 건 학원 다닐 때 이후로 처음인 것 같다.

하나씩 클릭해서 모든 영역이 설정되면 매크로 시작 버튼이 활성화된다.


매크로는 위의 절차대로 단순 하드코딩을 진행했다.

이 프로젝트는 더 발전시킬 생각은 없기 때문에... 사용도 안 할 거고... 이 이상 필요는 없을 듯하다.

이미지 매칭은 java의 라이브러리인 SikuliX를 사용했다.

public class ImageMatcher {

    private final Logger log = LoggerFactory.getLogger(this.getClass());
    private final Map<String, BufferedImage> templates;
    private final Rectangle area;

    public ImageMatcher(Rectangle area) throws IOException {
        this.templates = new HashMap<>();
        this.area = area;
        templates.put("1.png", loadImageFromResources("1.png"));
//        templates.put("2.png", loadImageFromResources("2.png"));
    }

    public BufferedImage loadImageFromResources(String imageName) throws IOException {
        ClassLoader loader = Thread.currentThread().getContextClassLoader();
        InputStream inputStream = loader.getResourceAsStream(imageName);

        if (inputStream == null) {
            throw new IOException("리소스를 찾을 수 없습니다: " + imageName);
        }

        BufferedImage image = ImageIO.read(inputStream);
        inputStream.close();
        return image;
    }

    public Point match(BufferedImage capture) {
        Finder finder = new Finder(capture);

        for (Map.Entry<String, BufferedImage> entry : templates.entrySet()) {
            String imageName = entry.getKey();
            BufferedImage templateImage = entry.getValue();
            Pattern pattern = new Pattern(templateImage).similar(0.7);

            finder.find(pattern);

            if (finder.hasNext()) {
                Match match = finder.next();
                log.info("성공: {} 이미지를 캡처된 영역 내에서 찾았습니다!", imageName);

                // match에서 반환되는 xy 좌표는 패턴이 겹치는 시작지점 즉, 상대좌표이다. 따라서 전체 영역의 x,y 좌표를 더해야 한다.
                int absoluteX = area.x + match.getX();
                int absoluteY = area.y + match.getY();

                return new Point(absoluteX, absoluteY);
            } else {
                log.info("실패: 이미지를 캡처된 영역에서 찾지 못했습니다.");
            }
        }
        return null;
    }
}

 

resource 폴더에서 불러온 이미지 파일!

 

이 파일과 탐색 영역의 유사도(0.7)에 따라 검색한다. 

이렇게 작성하고 실행하면...

SLF4J(W): Class path contains multiple SLF4J providers.
SLF4J(W): Found provider [org.slf4j.nop.NOPServiceProvider@52d455b8]
SLF4J(W): Found provider [ch.qos.logback.classic.spi.LogbackServiceProvider@4f4a7090]
SLF4J(W): See https://www.slf4j.org/codes.html#multiple_bindings for an explanation.
SLF4J(I): Actual provider is of type [org.slf4j.nop.NOPServiceProvider@52d455b8]
Exception in thread "main" java.lang.IllegalStateException: LoggerFactory is not a Logback LoggerContext but Logback is on the classpath. Either remove Logback or the competing implementation (class org.slf4j.helpers.NOPLoggerFactory loaded from file:/C:/Users/%ea%b5%ac%ed%98%84%ec%9a%b0/.gradle/caches/modules-2/files-2.1/org.slf4j/slf4j-api/2.0.17/d9e58ac9c7779ba3bf8142aff6c830617a7fe60f/slf4j-api-2.0.17.jar). If you are using WebLogic you will need to add 'org.slf4j' to prefer-application-packages in WEB-INF/weblogic.xml
	at org.springframework.util.Assert.state(Assert.java:101)
...

 

오래간만에 보는 의존성 충돌...

maven에서는 많이 제거해 봤는데, gradle에서는 처음 해보는 것 같다.

나는 Spring을 기반으로 프로젝트를 생성해서 Logback을 기본으로 사용하고 있다.

그런데 새로 추가한 라이브러리에서도 NOPServiceProvider라는 로깅 시스템을 사용하고 있었나 보다.

스프링은 충돌하는 두 개의 Slf4j 구현체를 인지하고, 에러를 발생시켰다.

 

아래와 같이 제거한다고 한다.

	implementation('com.sikulix:sikulixapi:2.0.5') {
		exclude group: 'org.slf4j', module: 'slf4j-nop'
	}

 

다시 실행!

 

 

 

 

320x100

'공부' 카테고리의 다른 글

[이미지변환] HEIC To JPG  (1) 2025.06.20
[CI/CD] Gitea + Act_Runner  (1) 2025.06.13
[GCP] Nginx SSL 자동갱신  (1) 2025.06.13
[REST] API 요청, 응답  (2) 2025.06.12
[BE] SSE 재연결과 관련된 시간  (0) 2025.06.12