87 lines
5.5 KiB
Markdown
87 lines
5.5 KiB
Markdown
---
|
||
title: DevLog @ 2026.02.16
|
||
category: DevLog
|
||
date: 2026-02-16
|
||
excerpt: |
|
||
LemonNeko 가 Dome Keeper 방향에서 최근 이룬 진전을 나눕니다.
|
||
---
|
||
|
||
설날 전야 잘 보내고 계신가요! [@LemonNekoGH](https://github.com/LemonNekoGH) 입니다. 춘절 전 마지막 DevLog 를 제가 씁니다.
|
||
|
||
## 돌아보기
|
||
|
||
작년 [DevLog](../DevLog-2025.08.26/) 에서는 `airi-factorio` 의 순수 비전 방향 진전을 나눴습니다. 오늘은 Dome Keeper 방향에서 무엇을 해 왔는지 이야기하려 합니다.
|
||
|
||
잠깐, LemonNeko? `airi-factorio` 를 계속 안 하고요?
|
||
|
||
솔직히 겁이 났습니다. Factorio 는 너무 열려 있고 복잡해서 제가 통제할 수 없었거든요. 그래서 비교적 단순한 게임인 [Dome Keeper](https://store.steampowered.com/app/1637320/Dome_Keeper/) 로 옮겼습니다.
|
||
|
||

|
||
|
||
그래서 지금까지 무엇을 했을까요?
|
||
|
||
1. 데이터 수집용 모드를 작성했습니다. 모드를 설치하면 일시정지 메뉴에 `Start YOLO Data Collection` 버튼이 보이고, 클릭하면 수집이 시작됩니다.
|
||
|
||

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

|
||
|
||
아직 많지는 않지만, 기록해 둘 만한 함정과 세부 사항을 벌써 여러 개 만났습니다. 그래서 이 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` 가 부족합니다.
|
||
|
||
다음 업데이트를 기대해 주세요. 아, 모드 코드는 이미 오픈소스로 공개했으니 편하게 [써 보세요](https://github.com/proj-airi/game-playing-ai-dome-keeper)!
|
||
|
||
설날 전야 잘 보내세요!
|