DevLog @ 2026.02.16

설날 전야 잘 보내고 계신가요! @LemonNekoGH입니다. 춘절 전 마지막 DevLog를 제가 씁니다.

돌아보기

작년 DevLog 에서는 airi-factorio의 순수 비전 방향 진전을 나눴습니다. 오늘은 Dome Keeper 방향에서 무엇을 해 왔는지 이야기하려 합니다.

잠깐, LemonNeko? airi-factorio를 계속 안 하고요?

솔직히 겁이 났습니다. Factorio는 너무 열려 있고 복잡해서 제가 통제할 수 없었거든요. 그래서 비교적 단순한 게임인 Dome Keeper 로 옮겼습니다.

Dome Keeper

그래서 지금까지 무엇을 했을까요?

  1. 데이터 수집용 모드를 작성했습니다. 모드를 설치하면 일시정지 메뉴에 Start YOLO Data Collection 버튼이 보이고, 클릭하면 수집이 시작됩니다.

    add-button-to-menu

  2. 소량의 데이터를 수집했습니다.

    some-collected-data

아직 많지는 않지만, 기록해 둘 만한 함정과 세부 사항을 벌써 여러 개 만났습니다. 그래서 이 DevLog를 씁니다.

세부 사항

  • 저장소 구조.

    Dome Keeper 모드를 개발하려면 게임을 디컴파일해야 하는데, 그 소스 코드는 공개할 수 없습니다. 그래서 저장소 구조를 신중히 설계해야 했습니다. 디컴파일한 게임은 최상위 external/ 폴더에 두고 .gitignore에 넣었으며, 모드 코드는 게임 소스 디렉터리로 링크했습니다.

  • 샘플링 전략.

    처음 전략은 0.5초마다 한 프레임을 캡처하는 것이었는데, 프레임에 대상이 없는 경우가 많았습니다. "네거티브 샘플"이 너무 많이 생겨서 데이터셋 크기는 커지는데 유효 정보 밀도는 떨어졌습니다.

    이후 규칙을 바꿨습니다. enemy 또는 ore_*가 포함된 프레임만 "대상 프레임"으로 세고, 대상 프레임 5장당 대상 없는 프레임을 1장만 허용합니다. 배경을 약간 남기면서도 데이터셋이 희석되는 것을 막습니다.

  • UI 오버레이가 "잘못된 라벨"을 만드는 문제.

    일시정지 메뉴나 업그레이드 패널(TechTree)이 열려 있으면 UI가 장면을 덮는데도 라벨은 여전히 광석과 적을 표시합니다. 라벨 파일이 정상처럼 보이고 시각화할 때만 문제가 드러나서 알아채기 까다롭습니다.

    PauseMenu / TechTreePopup에 그룹을 태그하고, 그 그룹의 보이는 노드가 있으면 캡처를 건너뛰는 방식으로 해결했습니다.

  • 좌표 불일치로 인한 전역 오프셋.

    가장 고통스러운 버그였습니다. 모든 bbox가 같은 방향으로 밀려 있어서 이미지 전체가 잘못 스케일된 것처럼 보였습니다.

    근본 원인은 논리적 뷰 크기실제 텍스처 픽셀 크기의 불일치였습니다. bbox 계산에는 viewport.get_visible_rect().size를 썼는데 스크린샷은 텍스처에서 찍었던 것이죠. 해결책은 bbox를 먼저 뷰 공간에서 이미지 공간으로 스케일한 뒤 letterbox 스케일 + 오프셋을 적용하는 것이었습니다.

  • letterbox가 라벨에 영향을 주는 문제.

    출력을 640×640으로 정규화하면서 가운데 정렬 패딩(회색 114/255)을 넣습니다. 같은 변환을 bbox에도 적용하지 않으면 라벨이 틀어집니다.

    그래서 해법은 2단계 변환입니다. 스케일 + 오프셋을 적용한 뒤 640×640으로 정규화합니다.

  • 데이터셋 분할 전략.

    처음에는 세션 단위로 나누려 했지만, 한 세션에 여러 판이 들어갈 수 있고 길이도 꽤 깁니다. 그래서 시간 기반 분할로 바꿨습니다. 구간당 30초로 잘라 4/1/1로 순환시켜 train/val/test에 배분합니다. 3분이면 분할 한 주기가 완성되니 검증 비용이 훨씬 싸집니다.

  • 성능과 프레임 끊김.

    Image.resize()save_png()는 CPU/IO 부담이 큽니다. 너무 자주 캡처하면 끊김이 생깁니다. 곧바로 멀티스레딩으로 가기보다 대상 없는 프레임을 줄이는 쪽을 택했습니다.

요약

지금 시점에서 안정적이고 검증이 빠른 파이프라인을 갖췄습니다. 캡처 → 네거티브 필터링 → 자동 분할 → data.yaml 자동 생성 → 바로 학습.

학습 로그에 이미 진전이 보입니다. 광석 클래스(ore_*)는 괜찮은 mAP를 달성했는데, 파이프라인이 올바르다는 뜻입니다. dome / enemy / player는 아직 샘플이 부족해서 더 필요합니다.

다음 단계

airi-factorio 저장소의 순수 비전 Playground 기억하시나요? 그것을 Dome Keeper로 확장해서 proj-airi 조직 전체가 재사용할 수 있게 할 계획입니다. 샘플도 더 필요하고, 특히 dome, enemy, player가 부족합니다.

다음 업데이트를 기대해 주세요. 아, 모드 코드는 이미 오픈소스로 공개했으니 편하게 써 보세요!

설날 전야 잘 보내세요!