스타크래프트2 봇

스타크래프트 2 봇 개발 일지-2(Ocraft)

오잎 클로버 2022. 1. 3. 03:30
728x90

지난 번에는 SCV 건설, 그리고 보급고 건설을 하였습니다.

 

이번에는 건설 직후, SCV가 노는 경우, 다시 작업에 착수하기 위한 코드를 작성할 것입니다.

 

일꾼 관리

스타크래프트2 같은 경우, 게임시작과 동시에 미네랄로 자동으로 보내주지만

그이후부터는 플레이어가 알아서 조절을 해야합니다.

그렇기때문에 건물 건설 등과 같은 미네랄 채취 외에 작업이 주어진 후에는 수동으로 다시 채취하도록 해야합니다.

보통 건물이 다 지어지면 자동으로 shift + 미네랄 우클릭을 해서 건물이 다 지어진 후 즉시 미네랄 채취 명령합니다.

 

이와 같이 개발하기 위해 제일 가까운 미네랄 하나를 찾아서 반환하여 놀고 있는 SCV에게 다시 미네랄을 채취하도록 합니다.

private Optional<Unit> findNearestMineralPatch(Point2d start) {
    List<UnitInPool> units = observation().getUnits(Alliance.NEUTRAL);
    double distance = Double.MAX_VALUE;
    Unit target = null;
    for (UnitInPool unitInPool : units) {
        Unit unit = unitInPool.unit();
        if (unit.getType().equals(Units.NEUTRAL_MINERAL_FIELD)) {
            double d = unit.getPosition().toPoint2d().distance(start);
            if (d < distance) {
                distance = d;
                target = unit;
            }
        }
    }
    return Optional.ofNullable(target);
}

위 코드는 제 글의 90%를 차지했던 스타크래프트 글들을 보신 분들이라면 다들 어느정도 이해가 가는 코드입니다.

단지 null 값을 담지 않기위해 Optional.ofNullable()로 반환을 한 것이지요.

 

그후 onUnitIdle 메소드에 SCV를 추가합니다.

case TERRAN_SCV:

그후 findNearestMineralPatch 메소드를 사용해서 미네랄을 채취해줄겁니다.

@Override
public void onUnitIdle(UnitInPool unitInPool) {
    Unit unit = unitInPool.unit(); // 아직도 이해 못함 원리를
    switch ((Units) unit.getType()) {
        case TERRAN_COMMAND_CENTER:
            actions().unitCommand(unit, Abilities.TRAIN_SCV, false);
            break;
        case TERRAN_SCV:
            findNearestMineralPatch(unit.getPosition().toPoint2d())
                    .ifPresent(mineralPath -> {
                        actions().unitCommand(unit, Abilities.SMART, mineralPath, false);
                    });
            break;
        default:
            break;
    }
}

코드에 굳이 SMART를 써야하나라는 생각이 잠깐들어 확인을 해보니,

Abilities 중에는 HARVEST_GATHER_SCV이 따로 존재하긴 하나 그냥 SMART를 쓰시면 더 편리합니다.

(SMART는 우클릭 정도라고 생각하시면 됩니다.)

 

시도해볼만한 것

그리고 튜토리얼 문서에 있는 것들을 조금 해보고자 합니다.

  1. 여러개의 보급고를 SCV 하나로 여러개를 건설
  2. 2개의 가스 추출장 건설 후, 각각 추출장에 3기의 SCV들에게 가스 채취 명령

 

하나하나 천천히 해봅시다.

변수로 지정을 한 뒤, 이를 계속 보급고 건설을 하도록 해봤습니다..

private Optional<Unit> supplyBuilder = Optional.empty();
private boolean tryBuildSupplyDepot() {
//            if (observation().getFoodUsed() <= observation().getFoodCap() - 2) {
//                return false;
//            }

            if (observation().getFoodCap() - observation().getFoodUsed() > 3) {
                return false;
            }

            return tryBuildStructure(Abilities.BUILD_SUPPLY_DEPOT, Units.TERRAN_SCV);
        }
private boolean tryBuildStructure(Abilities abilities, UnitType type) {
            if (!observation().getUnits(Alliance.SELF, doesBuildWith(abilities)).isEmpty()) {
                return false;
            }

//            Optional<UnitInPool> unitInPool = getRandomUnit(type);
            List<UnitInPool> units = observation().getUnits(Alliance.SELF, UnitInPool.isUnit(type));
            Optional<UnitInPool> unitInPool = Optional.ofNullable(units.get(0));
            if (unitInPool.isPresent()) {

                if (supplyBuilder == null) {
                    supplyBuilder = unitInPool.get().unit();
                }

                Unit unit = supplyBuilder;
                actions().unitCommand(
                        unit,
                        abilities,
                        unit.getPosition().toPoint2d().add(Point2d.of(getRandomScalar(), getRandomScalar()).mul(15f)),
                        false
                );
                return true;
            }
            else {
                return false;
            }
        }

만, 그렇지만 썩 좋지는 않은 것 같아 그냥 다시 되돌렸습니다.

(차라리 기존 코드가 더 좋은 것 같아 되돌렸습니다.)

 

2번 같은 경우에는 findNearestMineralPatch 메소드를 응용하면 될 것 같습니다.

...라고 생각해서 거의 3시간동안 삽질을 해봤지만 건설이 되지않아 일단 넘기기로 했습니다.

 

이상입니다.