1주민이 빵보면 이동하도록
2또한 교배하도록
1번이 되면 2번이 안되고
2번이 되면 1번이 안됫음(빵이 화면에 안보였음)
1차 삽질: AI가 일을 안 함
코드를 다 짜고 '사랑의 빵'을 던져줌. 주민이 멀뚱멀뚱 쳐다만 봄. 로그를 찍어보니 AI 관련 로그가 단 한 줄도 안 찍힘. 여기서부터 디버깅의 늪이 시작됨.
"우리만의 쇼핑 리스트를 만들면 되는거 아닌가? private final static 필드를 수정를 고수하면 사용자 주민을 사용하는 이유가 없잖아"
처음엔 바닐라 주민이 음식을 인식하는 Villager.FOOD_POINTS라는 static 맵을 건드려볼까 생각했음. 하지만 이건 모든 주민에게 영향을 주는 방식이라, 우리 커스텀 주민만의 고유한 행동을 만들고 싶었음. 그래서 바닐라 시스템을 이용하지 않고, 독립적인 AI를 만들기로 결정함. 이게 첫 번째 판단 미스였을 줄은...
2차 삽질: private 메서드의 벽과 Mixin의 유혹
뇌 설정을 커스터마이징하려고 바닐라 코드를 뜯어봄. Villager 클래스에 registerBrainGoals라는 아주 맛있는 이름의 메서드가 있었음. "이거다!" 싶어서 바로 오버라이드함.
에러: @Override protected void registerBrainGoals(Brain<Villager> pBrain) / 메서드는 상위 클래스의 메서드를 재정의하지 않습니다
그렇다. 이 메서드는 private 이었음. 자바 규칙상 private은 자식 클래스에서 절대 건드릴 수 없음. 여기서 모딩의 첫 번째 벽과 마주함. "아, 이래서 사람들이 Mixin, Mixin 하는구나." Mixin을 사용하면 private 메서드에도 코드를 주입할 수 있다는 사실을 떠올림. 하지만 Mixin은 설정을 복잡하게 만들고 싶지 않아서 일단 보류함. 다른 방법이 있을 거라 생각했음.
3차 삽질: 컴파일러와의 기 싸움
어찌어찌 다른 방법으로 AI를 등록하려고 하니, 온갖 기괴한 컴파일 오류들이 반겨줌.
R 타입 변수의 인스턴스가 없으므로 Stream<R>이(가) Optional<Villager>을(를) 준수합니다
이건 자바 컴파일러가 "네 코드가 너무 복잡해서 타입을 못 정하겠다"고 비명을 지르는 소리였음. return entities.get().find(...).map(...) 처럼 한 줄에 너무 많은 제네릭 타입 변환을 때려 넣으니 컴파일러가 길을 잃은 거임.
해결책: 복잡한 스트림 코드를 for-each 반복문으로 단순하게 풀어서 작성함.
에러: Foreach는 타입 'net.minecraft.world.entity.ai.memory.NearestVisibleLivingEntities'에 적용할 수 없습니다
NearestVisibleLivingEntities는 Iterable이 아니었던 거임. 결국 정답은 .find(predicate).findFirst() 라는, 스트림을 한번 더 호출하는 방식이었음. 이렇게 컴파일 오류와 싸우며 시간을 다 보냄.
4차 삽질: ImmutableMap이라는 통곡의 벽
결국 바닐라 AI를 이용하는 방식으로 다시 돌아옴. FOOD_POINTS 맵에 우리 빵을 등록하기로 함.
에러: java.lang.UnsupportedOperationException at ImmutableMap.put()
Villager.FOOD_POINTS는 이름 그대로 수정 불가능한(Immutable) 맵이었음. 게임 로딩이 끝나면 절대 내용을 바꿀 수 없는, 봉인된 데이터였던 거임. 여기서 "아, 바닐라 시스템을 건드리는 건 정말 어렵구나"를 깨달음.
5차 삽질: IDLE vs CORE의 함정
"좋아, 그럼 우리만의 AI로 돌아가자. 이제 모든 준비는 끝났다!"
FindLoveBread와 모든 커스텀 행동을 주민의 IDLE(빈둥거리기) 활동에 최우선으로 등록함.
결과: 또 안 움직임.
"IDLE 활동에 세 가지 행동을 모두 추가하면 walk할떄 이 idle실행이 안된다면서 뭔 개소리하는거야 시발 진짜"
이때 멘탈이 터졌음. 주민은 '일과표'에 따라 움직이기 때문에, 한창 WORK(일하기) 중일 때는 IDLE 활동을 아예 쳐다보지도 않는다는 사실을 뒤늦게 깨달음. 즉, 우리 AI가 실행될 기회 자체가 없었던 것.
해결책: "그럼 어떤 활동 중에도 항상 실행되는 CORE 활동에 등록하면 되겠지!"
6차 삽질: makeBrain 오버라이드의 배신
makeBrain 메서드를 오버라이드해서 CORE 활동에 우리 AI를 덮어씌우는 코드를 작성함. 로그상으로는 완벽하게 등록되는 것처럼 보였음.
[BRAIN_LOG] [SUCCESS] Brain of ... fully re-configured with CORE behaviors.
하지만 주민은 움직이지 않았음. 로그는 성공이라는데, 현실은 실패. 여기서 알게 된 사실은 다음과 같음.
makeBrain과 registerBrainGoals의 관계
super.makeBrain()을 호출하는 순간, 우리가 건드릴 수 없는 private registerBrainGoals가 이미 모든 행동을 등록하고 뇌를 "잠가버림". 그 후에 addActivity로 무언가를 덮어쓰려는 시도는 그냥 무시되고 있었던 것.
진짜 진짜 진짜 최종 결론: 모든 문제의 근원
이 모든 삽질 끝에, 로그의 가장 첫 부분에서 놓치고 있던 것을 발견함.
<<<<< [SUCCESS] Registered attributes for CUSTOM_VILLAGER. >>>>>
이 로그가 찍히지 않고 있었다는 사실.
모든 문제의 원흉은, MyMod.java에서 EntityAttributeCreationEvent를 통해 커스텀 주민에게 기본 능력치(체력, 속도 등)를 부여하는 코드가 누락되었던 것. AI를 실행할 몸 자체가 미완성이었기 때문에, 뇌에 무슨 짓을 해도 소용이 없었던 거임.
사용자 정의 주민 패키지를 만들었는데도 주민의 뇌를 컨트롤하기 어렵네
다른 방식으로
어쩌면 우리가 새로운 CORE를 구성해야하나
기능하나 만드는데 3일째 헤메고있네
살짝 포기마렵네
빵을 던지지만 빵을 주으러가지않는 모습
'모딩 > 마인크래프트 모드 개발 일지' 카테고리의 다른 글
| [마인크래프트 모딩]#11 좀비힐러 (7) | 2025.06.28 |
|---|---|
| [마인크래프트 모딩]#10 모든 살아있는 생명을 공격하는 좀비 (4) | 2025.06.28 |
| [마크 모딩 삽질기] #8 주민아, 연애 좀 해! (feat. 뇌 제어 실패기) (11) | 2025.06.27 |
| [마인크래프트 모딩] #7 침대 없이 강제 번식시키는 마법의 빵 만들기 (Feat. 무한 버그) (12) | 2025.06.26 |
| [마인크래프트 모딩] #6 "분명 데미지는 들어가는데..." 칼 안 휘두르는 주민, 범인은 의외의 곳에 있었다 (14) | 2025.06.25 |