스타봇/스타봇 강의 및 개발

스타크래프트 봇 강의 튜토리얼 2편

오잎 클로버 2021. 12. 20. 14:00
728x90

지난 번에 갑자기 마무리가 되어 이어서 설명 후, 튜토리얼 코드 테스트 해보는 방향으로 글을 마무리하겠습니다.

 

먼저 

위 부분은 한 번이라도 게임을 진행하면서 탐험, 즉 시야를 얻은 적이 있는 지 여부를 확인하는 메소드입니다.

game에 isExplored(int x, int y)

 

그리고 이 메소드는 현재 시야 내에 들어오는 지 확인하는 메소드입니다.

hasPowered는 해당 위치가 파워드(powered) 즉, 전력이 공급되고 있는 지 파악하는 메소드입니다.

프로토스 건물과 관련된 메소드입니다.

 

isValidTile 메소드는 이미 game에 있는 메소드이긴 하나 따로 만든다고 하여 문제되지않고,

그냥 어떤 방식으로 메소드가 작동하는 지 파악하고자 작성한 메소드입니다.'

 

isBuildable 메소드는 해당 위치에 건물이 지어지는 공간인지 파악하는 메소드입니다.

그밖에도 printMap, isDepotBuildableTile, isWalkable, width, height, drawTile, canWalk, draw 

메소드들을 정의하였지만 내용 분량차 짧게 설명드리자면

width와 height는 넓이와 높이를 반환하는 메소드이고

drawTile, draw는 스타크래프트 및 JBwapi에서는 어떤 방식으로 위치를 기억 및 처리하는 지 파악하고자 정의한 메소드이고

canWalk, isWalkable은 해당 위치가 유닛이 걸을 수 있는 위치인지 파악합니다.

(어떤 유닛이라도 이동 가능한 위치 라면 true, 그렇지않다면 false)

printMap은 이동가능한 영역을 콘솔에 띄우는 메소드이고

isDepotBuildableTile은 건물 건설 가능 여부를 알려주는 메소드입니다.

 

Tools와 MapTools 클래스들이 준비가 완료되었으니

이제 Main보다 더 방대한 양들을 처리해줄 클래스를 하나 더 만듭니다.

저 같은 경우에는 TutorialBot으로 하였습니다.

TutorialBot 클래스는 Tools, MapTools를 활용하여 처리한 영역들을 Main에서 호출하는 방식입니다.

 

MapTools 전역변수를 선언합니다.

public static MapTools m_mapTools = new MapTools();

onStart 메소드를 정의하여 게임의 속도, 그리고 Flag를 설정 및 MapTools 에 있는 onStart 메소드를 호출합니다.

public void onStart() {
    Main.game.setLocalSpeed(10);
    Main.game.setFrameSkip(0);

    Main.game.enableFlag(Flag.UserInput);

    m_mapTools.onStart();
}

그런 후, onEnd 메소드를 정의하여 게임 결과를 콘솔에 출력시키도록 합니다.

public void onEnd(boolean isWinner) {
    System.out.println("We " + (isWinner ? "Won!" : "lost!"));
}

그리고 앞서 말했던 기능들 중 미네랄 채취 기능을 수행하는 메소드를 정의합니다.

public void sendIdleWorkersToMinerals() {
    final List<Unit> myUnits = Main.game.self().getUnits();

    for (Unit unit : myUnits) {
        if (unit.getType().isWorker() && unit.isIdle()) {
            Unit closestMineral = Tools.getClosestUnitTo(unit, Main.game.getMinerals());

            if (closestMineral != null) {
                unit.rightClick(closestMineral);
            }
        }
    }
}

rightClick, gather 둘 다 사용가능하긴 합니다만

gather 같은 경우에는 중도 cancel이 되지않아 다른 기능을 수행하기 위해서는 rightClick 혹은 stop 을 사용해야합니다

rightClick같은 경우에는 중도 cancel이 가능합니다.

 

그리고 일꾼을 생산하기 위한 메소드 역시 정의합니다.

public void trainAdditionalWorkers() {
    final UnitType workerType = Main.game.self().getRace().getWorker();
    final int workersWanted = 20;
    final int workersOwned = Tools.countUnitsOfType(workerType, Main.game.self().getUnits());

    if (workersOwned < workersWanted) {
        final Unit myDepot = Tools.getDepot();

        if (myDepot != null && !myDepot.isTraining()) {
            myDepot.train(workerType);
        }
    }
}

위 방식으로 작성시, 저그 종족에서는 적용되지않기 때문에

if (myDepot != null && !myDepot.isTraining()) {
    myDepot.train(workerType);
}

if (myDepot != null && myDepot.getLarva().size() > 0) {
    myDepot.morph(workerType);
}

으로 바꾸셔야합니다.

 

그리고 인구수가 막히지 않도록 일정 인구수밖에 남지 않다면 자동으로 건설하도록 합니다.

public void buildAdditionalSupply() {
    final int unUsedSupply = Tools.getTotalSupply(true) - Main.game.self().supplyUsed();

    if (unUsedSupply >= 2) {
        return;
    }

    final UnitType supplyProviderType = Main.game.self().getRace().getSupplyProvider();
    final boolean startedBuilding = Tools.buildBuilding(supplyProviderType);
    if (startedBuilding) {
        Main.game.printf("Started Building " + supplyProviderType.toString());
    }
}

저그 종족같은 경우에는 위같은 방식 말고 일꾼 생산하는 방식을 약간 수정하여 오버로드를 생산해주시면 됩니다.

 

마지막으로 디버깅, 즉 어떤 방식으로 작동 중인지 확인 차 화면에 draw를 하는 메소드를 정의합니다.

public void drawDebugInformation() {
    Main.game.drawTextScreen(new Position(10, 10), "Hello, World");
    Tools.drawUnitCommands();
    Tools.drawUnitBoundingBoxes();
}

그리고 MapTools에는 draw를 on/off 도 가능하기 때문에 이를 제어하는 것을 게임 내에서 특정 명령어를 입력 시 사용되도록 하였습니다.

public void onSendText(String text) {
    if (text.equals("/map")) {
        m_mapTools.toggleDraw();
    }
}

그리고 마지막으로 프레임마다 작동시키 위해 onFrame 메소드를 정의합니다.

public void onFrame() {
    m_mapTools.onFrame();

    sendIdleWorkersToMinerals();

    trainAdditionalWorkers();

    buildAdditionalSupply();

    Tools.drawUnitHealthBars();

    drawDebugInformation();
}

그리고 마지막으로 TutorialBot을 Main와 연결시키고자 

인스턴스화 시켜 각 필요한 메소드마다 호출시킵니다.

public static TutorialBot bot;
public static void main(String[] args) {
    Main main = new Main();
    main.client = new BWClient(main);
    bot = new TutorialBot();
    main.client.startGame();
}
@Override
public void onStart() {
    game = client.getGame();
    bot.onStart();
}
@Override
public void onEnd(boolean isWinner) {
    bot.onEnd(isWinner);
}
@Override
public void onFrame() {
    bot.onFrame();
}
@Override
public void onSendText(String text) {
    bot.onSendText(text);
}

이런 식으로 작성하셨다면

완벽합니다.

 

이제 ChaosLauncher를 실행하여 테스트를 합니다.

테스트 하는 방식은 아래와 같습니다.

더보기

※ 필자의 기준으로 테스트 방식을 작성하였습니다. ※

스타크래프트(1.16.1)가 설치된 디렉토리로 이동합니다.

저같은 경우에는 scbw_bwapi440 입니다.

 여기서 BWAPI 디렉토리로 이동합니다.

 에서 Starcraft > bwapi-data > bwapi.ini 에서

ai = 부분을 NULL로 바꿔주시고

ai_dbg = 부분 역시 NULL로 바꿔주세요.

그런다음 다시 BWAPI 디렉토리로 이동해주세요.

 해당 디렉토리에서 ChaosLauncher 디렉토리로 이동합니다.

Chaoslauncher - MultiInstance.exe 를 관리자 권한으로 실행해주세요.

 만약 Start를 누르지 못하신다면 스타크래프트 봇 강의 준비편 을 참고해주세요.

Start를 하신다면 아래와 같이 스타크래프트가 실행될 겁니다.

 Single Player - Expansion -> Ok -> Play Custom -> 맵 설정 

그리고 Main을 run합니다.

 초록색 ▶를 클릭하거나 Shift + F10 으로 run 할 수 있습니다.

실행한 후, 

 위와 같은 콘솔이 나오면 오류가 없다는 의미입니다.

그런 후 다시 스타크래프트로 돌아와서

 OK를 합니다.

(반드시 1대1로 세팅해야합니다.)

(스타봇은 1대1로 개발해야 수월합니다.)

(물론 다수를 상대할 수 있다면 제일 좋으나, 1대1 개발도 굉장히 어렵습니다.)

카오스런처를 사용하여 

게임을 시작하였을 때 아래 사진처럼 나온다면 성공입니다.

 

이상입니다.