[ ]
일단 관련주민 레벨업 관련은 나중에 추가할까나
[ ]
수영 (FloatGoal): 물에 빠지면 가라앉지 않고 수면 위로 떠올라 익사를 방지합니다.
뭔가 특성등을 추가해서,ㅡ 맥주병은 수영못하게,
어떤 주민들의 달리기 속도는 느리게, 점프높이 다르게
일종의 유전 추가할수도,
그러면 관리되는 페이로드 정보가 좀 달라져야하나
근면성실, 게으름(수다떠는 시간 많은)
먹보
잠많음 등
돌연변이나,
일탄하는 주민도
[ ]주민끼리 서로 이야기하는 시스템
[ ]
일단 농부가 괭이를 맨날 들고잇으면 좀 그럴려나
farmer 직업표시나,
안그러면 나중에 이름등을 표시하도록해도괜찮르지도

📑 목차: 마인크래프트 커스텀 주민 AI 설계 가이드
1. 효율적인 주민 AI 설계의 원칙
- 하드코딩을 피하는 '우선순위 기반 상태 제어': if-else의 지옥에서 벗어나기
- 계층적 우선순위(Priority Ladder): 생존, 수면, 사회 활동의 위계 설정
- 조건 기반 활성화(Condition-based): 스스로 실행 타이밍을 판단하는 Goal 시스템
- 기억(Memory)과 지점(POI) 활용: 좌표가 아닌 '장소의 의미'를 기억하는 법
2. 주민의 사회성과 마을 인식 메커니즘
- POI(Point of Interest) 시스템: 주민이 '마을'을 정의하는 세 가지 요소 (침대, 직업 블록, 종)
- 보이지 않는 울타리: 마을 밖으로 가출하지 않게 만드는 VillageBound 로직
- 정박(Anchoring) 시스템: 공유된 중심점(종)을 통한 자연스러운 공동체 형성
3. 새로운 직업적 특성과 AI 시나리오
- 야생 사냥꾼(Hunter): 추적, 사냥, 전리품 수집의 루틴
- 전문 나무꾼(Lumberjack): 나무 탐색과 재배를 통한 산림 유지
- 야간 경비원(Night Watchman): 역방향 스케줄링을 이용한 순찰 로직
- 마을 청소부와 보급원: 아이템 수집 및 물류 네트워크 구축
4. 개발 실무: 커스텀 엔티티 구현과 트러블슈팅
- 스폰 시스템: 스폰알 등록부터 자연 생성 구조물(Structure) 연결까지
- 렌더러와 상속 문제: VillagerRenderer 타입 불일치 해결
- 다국어 지원(Localization): en_us와 ko_kr JSON 파일의 키(Key) 매칭 규칙
- 접근 제한자 해결: protected 메서드와 필드에 접근하는 올바른 방법
5. [심화] 완벽한 농부 AI를 위한 최적화 기법
- 정교한 농경 로직: 주변 물 존재 여부 파악 및 CropBlock 상태 검사
- 도구 관리 전략: 내구도 마모 문제와 자동 수리/보급 메커니즘
- 작업 효율 개선: 탐색 틱(Tick) 지연 시간 조정으로 '멍 때리는 시간' 줄이기
- 생동감 있는 애니메이션: 시선 처리(LookAt) 최적화와 작업 동작(Swing/Sneak) 추가
주민 ai만들기,
그래야 ai수정도 가능하니깐
사용자께서 우려하시는 **"일일이 예외 상황을 처리하는 방식(하드코딩)"**은 실제로 프로그래밍에서 가장 피해야 할 방식입니다. 예를 들어 if (시간 == 낮) { 종으로 가라 }라고 짜버리면, 가는 길에 좀비가 나타나도 무시하고 종으로만 가거나, 종이 파괴되었을 때 제자리에 멈춰버리는 등 수많은 버그가 발생합니다.
이를 해결하기 위해 마인크래프트의 AI는 **"우선순위 기반의 상태 제어(Priority-based Goal System)"**를 사용합니다. 코드 없이 이론적으로 가장 효율적인 설계 방법을 설명해 드릴게요.
1. 계층적 우선순위 (Priority Ladder) 설계
모든 행동을 나열하지 말고, 중요도에 따라 층을 나눕니다. AI는 항상 가장 높은 층의 행동을 먼저 하려고 시도합니다.
- 1층 (최우선 - 생존): 공격받으면 도망치기 (Panic). 이 상황에서는 시간이나 장소 따지지 않고 생존이 먼저입니다.
- 2층 (필수 - 수면): 밤이 되면 침대를 찾아가기.
- 3층 (사회적 - 모임): 점심시간에 종 근처로 가기.
- 4층 (일상 - 배회): 딱히 할 일이 없으면 침대 근처 돌아다니기.
이렇게 설계하면 "종으로 가던 중에 좀비가 나타나면?" 이라는 예외를 따로 짤 필요가 없습니다. 시스템이 자동으로 3층(종)보다 높은 1층(도망) 행동을 실행하기 때문입니다.
2. "시간"이 아닌 "조건"에 의한 활성화 (Condition-based)
각 Goal(목표)은 스스로 **"지금 내가 실행될 타이밍인가?"**를 판단하는 로직을 가집니다.
- 잠자기 Goal: "지금이 밤인가?" + "내가 침대 위치를 알고 있는가?" 두 조건이 맞을 때만 작동합니다.
- 모이기 Goal: "지금이 점심인가?" + "마을에 종이 존재하는가?" 조건이 맞을 때만 작동합니다.
이렇게 하면 메인 로직에서 복잡한 if-else문을 돌릴 필요 없이, 각 Goal들이 "지금 제 차례입니다!"라고 손을 드는 방식이 됩니다.
3. 기억(Memory)과 지점(POI) 활용
좌표를 하드코딩하지 말고, 주민이 장소의 위치를 기억하게 만듭니다.
- 침대(Home): 주민이 처음 태어나거나 침대를 발견하면 그 좌표를 '기억(Memory)'에 저장합니다.
- 종(Meeting Point): 마을의 종 위치를 기억합니다.
- 이동 방식: "X:100으로 가라"가 아니라, **"내 기억 속에 있는 '침대' 좌표로 길찾기(Pathfinding)를 시작하라"**고 명령합니다. 만약 길을 가다가 막히면 길찾기 시스템이 알아서 "도착 불가능" 판정을 내리고 다음 행동(배회 등)으로 넘어갑니다.
4. 상태 기계(State Machine)의 단순화
주민의 하루를 몇 가지 **'모드'**로 정의하면 관리가 편합니다.
- IDLE (평시): 침대 주변을 어슬렁거림.
- SLEEP (취침): 침대로 이동하여 누움.
- MEET (모임): 종으로 이동.
이 모드들은 '시간'이라는 신호에 의해 전환됩니다. 하지만 중요한 것은 '전환 신호'만 줄 뿐, 실제 행동은 Goal Selector가 알아서 처리하게 두는 것입니다.
5. 예외 상황을 처리하는 "똑똑한 실패"
"종으로 가라"는 명령이 실패할 경우(종이 부서짐, 길이 막힘 등)를 위해 'Fallback(대비책)' 행동을 항상 밑에 깔아둡니다.
- 종으로 가려고 시도한다. ➔ 실패하면? ➔ 그냥 그 자리에서 배회(Wander)한다.
- 이런 식으로 '실패 시 다음 행동'을 자연스럽게 연결하면 예외 처리를 일일이 할 필요가 없습니다.
요약하자면:
- 행동들을 우선순위별로 쌓아 올린다 (생존 > 잠 > 모임 > 배회).
- 각 행동은 실행 조건이 맞을 때만 활성화된다.
- 장소는 좌표가 아닌 **기억(POI)**으로 관리한다.
- 길찾기 실패를 자연스러운 행동 중단으로 간주하여 예외를 흡수한다.
이렇게 설계하면 코드가 훨씬 깔끔해지고, 사용자 정의 Goal(사냥, 농사 등)을 추가할 때도 기존 로직을 건드리지 않고 중간 우선순위에 끼워넣기만 하면 됩니다.
이 방식이 당신이 만들고자 하는 "사용자 정의 주민"의 구조로 적당해 보이나요? 구체적으로 어떤 "기억(침대 위치 등)"을 주민에게 먼저 부여해보고 싶으신지 알려주세요!
제공해주신 코드들의 메커니즘(시간 기반 상태 변화, 특정 블록 탐색, 전투 로직, 아이템 수집)을 조합하면, 기존 마인크래프트에는 없던 **'능동적으로 마을을 경영하는 주민 AI'**들을 다양하게 설계할 수 있습니다.
코드 없이, 이 시스템들을 어떻게 활용할 수 있을지 5가지 아이디어를 제안해 드립니다.
1. 야생 사냥꾼 주민 (Hunter)
- 사용 로직: Schedule + MeleeAttackGoal + ItemEntity 수집
- AI 흐름:
- 낮: 마을 밖으로 나가서 소, 돼지, 양 같은 동물 엔티티를 탐색합니다. (MeleeAttackGoal 활용)
- 행동: 동물을 발견하면 다가가 공격하여 사냥합니다.
- 수집: 동물이 죽고 바닥에 떨어진 고기와 가죽(ItemEntity)을 인벤토리에 줍습니다.
- 저녁: 일몰 전 마을로 복구하여 마을 중앙 상자에 수확물을 던져놓고 잠을 잡니다.
2. 전문 나무꾼 주민 (Lumberjack)
- 사용 로직: MoveToBlockGoal + 블록 파괴 로직
- AI 흐름:
- 낮: 주위에 있는 '나무 블록(Log)'을 탐색합니다. (MoveToBlockGoal의 탐색 로직 활용)
- 행동: 나무 아래 도착하면 나무를 아래서부터 위로 파괴합니다.
- 재배: 나무를 다 벤 후, 남은 자리에 묘목(Sapling)을 다시 심어 숲을 유지합니다.
- 보관: 획득한 원목을 마을 창고로 운반합니다.
3. 야간 경비원 주민 (Night Watchman)
- 사용 로직: Schedule 역이용 + MeleeAttackGoal + PathNavigation
- AI 흐름:
- 낮: 다른 주민들이 일할 때 집에서 잠을 잡니다. (기존 Schedule의 낮/밤 시간을 반대로 설정)
- 밤: 모두가 잠들면 깨어나서 마을 외곽에 설정된 몇 개의 '초소(POI)' 좌표를 순찰합니다.
- 전투: 순찰 중 좀비나 스켈레톤을 발견하면 선제 공격하여 물리칩니다.
- 아침: 해가 뜨면 다시 집으로 돌아가 휴식합니다.
4. 마을 청소부 및 정리원 (Janitor)
- 사용 로직: ItemEntity 탐색 + MoveToBlockGoal (상자 찾기)
- AI 흐름:
- 상시: 마을 바닥에 떨어져 있는 모든 아이템(누군가 버린 쓰레기나 드롭된 아이템)을 실시간으로 감지합니다.
- 행동: 아이템이 보이면 즉시 달려가 인벤토리에 넣습니다.
- 정리: 인벤토리가 어느 정도 차면, 마을에 지정된 '공용 상자'로 이동하여 아이템을 모두 집어넣어 마을을 깨끗하게 유지합니다.
5. 보급 및 물류 주민 (Courier)
- 사용 로직: MoveToBlockGoal (A지점 상자, B지점 상자)
- AI 흐름:
- 체크: 농부가 수확물을 넣어두는 'A상자'에 아이템이 들어있는지 주기적으로 확인합니다.
- 운송: 아이템이 있다면 모두 꺼내서 마을 중앙 시장에 있는 'B상자'로 이동합니다.
- 활용: 플레이어가 직접 아이템을 옮길 필요 없이, 주민들이 생산지에서 소비지로 물자를 이동시키는 물류 네트워크를 형성합니다.
마인크래프트 1.14 업데이트 이후, 주민이 '마을'을 인식하는 방식은 매우 영리하게 바뀌었습니다. 예전처럼 단순히 "여기가 마을이다"라고 지정된 구역이 있는 게 아니라, 주민들이 필요로 하는 '특정 블록(POI)'들의 집합을 마을로 인식합니다.
사용자 정의 주민이 마을을 벗어나지 않게 하려면 다음 코드들의 원리와 개념이 필요합니다.
1. 마을 인식의 핵심: POI (Point of Interest) 시스템
마크에서 마을은 침대(Home), 직업 블록(Job Site), 종(Meeting Point) 이 세 가지가 모여있는 공간을 의미합니다.
- 관련 클래스: PoiManager, PoiType
- 마크 서버는 전 세계의 모든 침대와 직업 블록의 위치를 PoiManager라는 데이터베이스에 저장해둡니다.
- 주민은 이 PoiManager에게 질의를 던집니다: "내 주변 32칸 안에 빈 침대나 종이 있어?"
- 그 응답으로 받은 좌표들의 중심점이 곧 그 주민에게는 **'마을의 중심'**이 됩니다.
2. 주민을 마을에 묶어두는 로직: VillageBoundRandomStroll
이 코드가 주민이 마을 밖으로 가출하지 않게 만드는 '보이지 않는 울타리' 역할을 합니다.
- 작동 원리 (이론):
- 주민은 자신의 **'기억(Memory)'**에 저장된 HOME(침대)이나 MEETING_POINT(종)의 좌표를 가져옵니다.
- 랜덤하게 이동할 목적지를 정할 때, 그 기억된 좌표에서 일정 거리(예: 32칸) 이상 멀어지는 지점은 선택하지 않습니다.
- 만약 마을 중심에서 너무 멀어졌다면, 다시 중심 방향으로 돌아오는 이동 값을 강제로 생성합니다.
3. 우리 커스텀 주민에게 필요한 "마을 인식" 설계
방법 A: '정박' 시스템 (가장 쉬운 방법)
주민 엔티티에 **RestrictCenter**라는 개념을 부여하는 것입니다.
- 원리: 주민이 처음 태어난 곳이나, 처음 발견한 침대 좌표를 restrictCenter로 고정합니다.
- 활용: PathfinderMob 클래스(주신 AgeableMob의 부모)에는 restrictTo(BlockPos pos, int distance)라는 메서드가 이미 있습니다. 여기에 좌표와 거리(예: 20칸)를 넣어주면, AI가 알아서 그 범위를 벗어나지 않는 경로만 계산합니다.
방법 B: '종(Bell)' 추적 시스템 (좀 더 마을다운 방법)
- 원리: 주기적으로 주변 블록을 스캔하여 Bell 블록을 찾습니다.
- 활용: Bell을 발견하면 그 좌표를 마을의 중심으로 저장합니다. 만약 종이 없다면 침대를 기준으로 삼습니다. 이 좌표를 기준으로 MoveToBlockGoal을 실행하면 자연스럽게 마을 안에서만 활동하게 됩니다.
주민이 마을을 인식하는 것은 **"고정된 지도를 보는 것"**이 아니라 **"자신이 기억하는 소중한 장소(침대, 종)들로부터 멀어지지 않으려는 본능"**에 가깝습니다.
스폰알과 자연 스폰 관련
네, 정확합니다.
- 먼저 **스폰알(Spawn Egg)**로 등록해서 내가 원하는 곳에 소환하며 AI가 잘 돌아가는지 테스트하는 것이 정석입니다.
- 나중에 코드 한 줄만 추가하면(예: BiomeLoadingEvent 등 활용) 특정 바이옴에서 좀비나 소처럼 자연적으로 스폰하게 만들 수 있습니다. 스폰알로 먼저 만드는 것이 아주 현명한 순서입니다.
1. 방법 A(정박) vs 방법 B(종 추적): 어떤 게 더 좋을까?
결론부터 말씀드리면, **"방법 B를 기반으로 하되, 방법 A를 보조로 섞는 하이브리드 방식"**이 가장 좋습니다.
- 방법 B(종/POI 추적)가 더 좋은 이유:
사용자께서 우려하신 대로 방법 A(초기 위치 고정)만 쓰면 주민들이 각자 태어난 곳에만 머물러서 '마을 공동체' 느낌이 나지 않습니다. 반면 방법 B는 주민들이 공유하는 '중심점(종)'을 기준으로 움직이기 때문에, 점심때 종 근처로 모이는 등의 사회적 행동이 자연스럽게 가능해집니다. - 모이기 문제를 해결하는 로직 (이론):
- 공유 기억: 모든 커스텀 주민이 "마을의 중심은 종(Bell)이다"라는 규칙을 공유하게 합니다.
- 유연한 정박: restrictTo를 쓸 때 '내 스폰 위치'가 아니라, **'우리 마을의 종 위치'**를 넣는 것입니다. 그러면 모든 주민이 종을 중심으로 반경 30칸 이내에서 활동하게 되므로 자연스럽게 서로 만날 수 있습니다.
- 우선순위 활용: 낮에는 '농사 목표'가 '종 근처 머물기'보다 우선순위가 높아서 밭에 가 있지만, 점심시간이 되어 농사 Goal이 비활성화되면 그다음 우선순위인 '종 근처 가기'가 발동되어 모이게 되는 원리입니다.
2. 사용자 정의 마을의 자연 생성 (구조물 시스템)
네, 코드 없이 이론적으로 설명하자면 **"마크의 구조물 생성 시스템(Structure System)"**을 활용하면 충분히 가능합니다.
A. 구조물 템플릿 (nbt 파일)
먼저 마인크래프트 게임 내에서 '구조물 블록(Structure Block)'을 사용해 직접 만든 집, 농장, 회관 등을 **설계도(nbt 파일)**로 저장합니다.
B. 지그소(Jigsaw) 시스템
1.14 버전 이후의 마을은 '지그소 블록'이라는 것을 사용합니다. 이는 마치 레고 블록처럼 "이 길 끝에는 집이 붙을 수 있음", "집 옆에는 농장이 붙을 수 있음" 같은 규칙을 정해주는 것입니다. 이 규칙을 설정해두면 바이옴마다 지형에 맞춰 무작위로 마을이 조립되어 생성됩니다.
C. 바이옴 연결 (Registry)
설계도가 완성되면 서버 로직에 다음과 같이 등록합니다.
- "이 구조물(내 커스텀 마을)은 '평원' 바이옴이나 '숲' 바이옴에서만 나타나게 해라."
- "생성 확률은 일반 마을보다 낮게(혹은 높게) 해라."
D. 주민 자동 스폰
구조물 설계도 안에 **'엔티티 스폰 지점'**을 미리 포함할 수 있습니다.
- 마을 회관 설계도 안에 "여기에 사냥꾼 주민 1명 소환"이라는 정보를 넣어두면, 세상이 만들어질 때 마을이 생성되면서 당신이 만든 커스텀 주민도 그 안에 함께 태어나게 됩니다.
3. 요약: 가장 이상적인 시나리오
- 마을 생성: 플레이어가 탐험하다가 숲속에서 당신이 디자인한 **'커스텀 마을'**을 발견합니다. (Structure 시스템)
- 주민 인식: 그 마을 안에는 당신이 만든 주민들이 소환되어 있습니다. 그들은 태어나자마자 마을 중앙에 있는 **'종'**을 인식하고 그곳을 restrictCenter로 삼습니다. (방법 B + A)
- 자동 농사: 농부 주민은 마을 안에 있는 '경작지' 블록을 찾아가서 일을 시작합니다. (MoveToBlockGoal)
- 사회 활동: 점심시간이 되면 모든 주민이 인식하고 있는 '종' 위치로 모여서 서로를 바라봅니다. (공유 POI)
이 방식의 장점:
플레이어가 마을을 직접 짓지 않아도 세상 어딘가에 당신의 주민들이 살아가는 터전이 자동으로 만들어지며, 주민들은 그 터전을 벗어나지 않고 유기적으로 움직이게 됩니다.
이제 어떤 부분이 더 궁금하신가요? 예를 들어, "주민이 종이 없어도 마을을 유지하게 하는 법"이나 "구조물 생성 시 주민의 직업을 나누는 법" 등이 있을 수 있습니다.
- Renderer 타입 불일치: VillagerRenderer는 오직 원본 Villager 클래스 전용으로 설계되어 있어, 상속 관계라 하더라도 CustomFarmerEntity를 직접 넣을 수 없습니다. 이를 위해 별도의 렌더러 클래스가 필요합니다.
- 접근 제한자 문제: MoveToBlockGoal 클래스에서 reachedTarget 변수가 private으로 선언되어 있어 자식 클래스에서 직접 수정할 수 없습니다. 대신 메서드를 이용하거나 로직을 살짝 변경해야 합니다.
(참고) 한국어 파일도 추가하고 싶다면? (ko_kr.json)
한국어 설정에서 보고 싶을 때 사용합니다.
경로: src/main/resources/assets/mymod/lang/ko_kr.json
lang이 language약자엿네

이 메시지는 IntelliJ IDEA(또는 사용 중인 IDE)에서 발생하는 "번역 파일 간의 키(Key) 불일치" 알림입니다.
1. 원인
마인크래프트는 en_us.json을 기본(Default) 언어로 간주합니다. IDE가 ko_kr.json 파일을 읽어보니, 기본 파일인 en_us.json에는 있는 키가 ko_kr.json에는 없거나, 혹은 그 반대인 상황일 때 "둘이 똑같지 않다"고 알려주는 것입니다.
2. 해결 방법
방법 A: IDE 기능 사용 (가장 쉬움)
사진 오른쪽에 있는 **[Add missing default entries]**를 클릭하세요.
- 클릭하면 en_us.json에는 있지만 ko_kr.json에 없는 키들을 자동으로 ko_kr.json에 복사해 줍니다. (내용은 영어로 복사되니 나중에 한글로만 수정하면 됩니다.)
방법 B: 직접 키 맞추기 (확실함)
제가 이전에 드린 두 파일의 **왼쪽 부분(Key)**이 완전히 동일한지 확인해야 합니다. 만약 파일 끝에 쉼표(,)가 빠졌거나, 한쪽 파일에만 오타가 있으면 이 경고가 뜹니다.
네, 맞습니다! 콜론(:)을 기준으로 앞부분이 바로 **'키(Key)'**입니다.
이해하기 쉽게 설명해 드릴게요.
1. JSON의 구조
JSON 파일은 항상 "키" : "값" 의 쌍으로 이루어져 있습니다.
"item.mymod.custom_farmer_spawn_egg" : "Custom Farmer Spawn Egg"
[ 이 앞부분이 '키' ] [ 이 뒷부분이 '값' ]
- 키 (Key): 마인크래프트 프로그램이 "이 아이템의 이름이 뭐지?" 하고 찾아볼 때 사용하는 고유 주소(ID) 같은 것입니다.
- 값 (Value): 실제 게임 화면에서 우리 눈에 보이는 글자입니다.
2. 왜 "일치하지 않는다"고 뜰까요?
마인크래프트는 여러 언어를 지원하기 위해 파일들을 비교합니다.
- 영어 파일(en_us.json): "A라는 주소(키)에는 'Apple'이라고 적혀 있어."
- 한국어 파일(ko_kr.json): "어? 나한테는 A라는 주소(키)가 아예 없는데? 아니면 주소 이름이 조금 다른데?"
이렇게 앞부분(키)의 이름이 서로 다르거나, 한쪽에만 있고 다른 쪽에는 없을 때 IDE가 "두 파일의 주소록(키 목록)이 일치하지 않는다"라고 경고를 주는 것입니다.
3. 해결을 위한 규칙
- 앞부분(키)은 두 파일 모두 "글자 하나 틀리지 않고 똑같아야" 합니다. (대소문자, 마침표 위치 등)
- 뒷부분(값)만 각 언어에 맞게 다르게 적는 것입니다.
예시:
- 영어 파일: "entity.mymod.custom_farmer": "Custom Farmer"
- 한국어 파일: "entity.mymod.custom_farmer": "커스텀 농부"
- 보시다시피 앞부분(entity.mymod.custom_farmer)은 완벽하게 똑같죠? 이렇게 되어야 정상입니다.
괭이가 마모되는 문제와 스스로 땅을 경작하는 로직은 **'현실성'**과 '게임 플레이의 편의성' 사이에서 선택이 필요합니다. 이를 해결하기 위한 3가지 이론적 방법과 경작 로직의 핵심을 설명해 드릴게요.
1. 괭이 내구도 문제 해결 방안
방법 A: 무한 내구도 (가장 추천)
전문적인 농부라는 설정으로, 농부가 들고 있는 도구는 내구도가 닳지 않게 설정하는 방식입니다.
- 원리: 아이템을 줄 때 Unbreakable NBT 태그를 붙여서 주거나, 코드상에서 아이템의 내구도를 깎는 로직(hurtAndBreak)을 아예 실행하지 않습니다.
- 장점: 플레이어가 일일이 도구를 챙겨줄 필요가 없어 자동화에 최적화됩니다.
방법 B: 도구 소모 및 보급 (현실적)
도구가 실제로 부서지게 하고, 농부가 마을 창고에서 새 도구를 꺼내오거나 플레이어에게 받아야 하는 방식입니다.
- 원리: 경작할 때마다 아이템의 damageValue를 올립니다. 내구도가 0이 되면 아이템을 삭제합니다.
- 활용: 농부의 tick() 로직에 "손에 도구가 없으면 창고(상자)로 이동해서 괭이를 꺼내온다"는 Goal을 추가해야 합니다.
방법 C: '퇴비통'에서 도구 수리 (마을 중심적)
하루 일과가 끝나면 퇴비통(Composter)이나 대장간에서 도구를 "정비"한다는 설정입니다.
- 원리: 특정 시간에 특정 블록(퇴비통 등) 옆에 있으면 아이템의 내구도를 다시 가득 채워줍니다.
2. 스스로 땅을 경작하는 로직 (Till Soil)
주민이 흙을 경작지로 바꾸려면 단순히 블록만 바꾸는 게 아니라, **"농사가 가능한 환경인가"**를 판단하는 지능이 필요합니다.
핵심 조건 1: 주변에 물이 있는가?
경작지(Farmland)는 주변 4칸 이내에 물이 없으면 곧 다시 흙으로 돌아갑니다.
- 로직: 목표 블록 주변(9x9 범위)에 Blocks.WATER가 있는지 체크합니다. 물이 있을 때만 경작을 시도하게 하여 헛수고를 방지합니다.
핵심 조건 2: 머리 위가 비어 있는가?
천장이 낮거나 위에 다른 블록이 있으면 작물이 자라지 않습니다.
- 로직: level.isEmptyBlock(pos.above())를 확인하여 하늘이 보이거나 충분한 공간이 있는지 체크합니다.
핵심 조건 3: 경작 액션
조건이 맞으면 다음 과정을 순차적으로 실행합니다.
- 애니메이션: 주민의 팔을 흔듭니다 (farmer.swing).
- 소리: 괭이질 소리(SoundEvents.HOE_TILL)를 재생합니다.
- 변환: 흙(Dirt/Grass) 블록을 경작지(Farmland)로 바꿉니다.
3. 농부의 향상된 작업 순서 (알고리즘)
이제 농부의 FarmerHarvestGoal은 다음과 같은 우선순위로 작동하게 됩니다.
- [1순위] 수확: 다 자란 작물이 보이면 일단 수확해서 인벤토리를 채운다.
- [2순위] 경작: 주변에 물이 있는 '흙'이 보이고, 내 손에 '괭이'가 있다면 땅을 간다.
- [3순위] 파종: 빈 '경작지'가 보이고, 내 인벤토리에 '씨앗'이 있다면 심는다.
4. 구현을 위한 팁: 괭이를 어떻게 들려주나?
엔티티가 처음 소환될 때(finalizeSpawn) 도구를 들려주는 것이 좋습니다.
- 코드적 구현: this.setItemInHand(InteractionHand.MAIN_HAND, new ItemStack(Items.IRON_HOE))
- 내구도 무한 설정: 위 아이템을 생성할 때 NBT를 조작하여 "파괴 불가" 속성을 부여하면 위에서 말한 1번 방법(무한 내구도)이 완성됩니다.
요약
- 괭이는 무한 속성을 부여하거나 특정 블록(퇴비통)에서 자동 수리되게 하는 것이 관리하기 편함.
- 경작 로직은 **"주변에 물이 있는지"**를 먼저 확인하는 단계가 추가되어야 효율적임.
- 주민의 손에 실제 괭이 아이템을 들려주면 괭이질 애니메이션과 소리가 아주 자연스럽게 연출됨.
이 "자동 경작 및 무한 도구" 기능을 포함해서 농부의 코드를 업그레이드해 볼까요? 아니면 우선은 수확 기능의 안정성부터 테스트해 보시겠어요?
일단 업글하기전에, 현재코드가 잘 작동하는지 확인부터
현재 코드가 정상적으로 작동하는지 확인하려면, 마인크래프트 게임 내에서 다음 5가지 테스트를 순서대로 진행해 보시면 됩니다.
1. 스폰 및 외형 확인 (기초 테스트)
- 방법: 크리에이티브 모드 인벤토리의 '기타(Misc)' 탭에서 "Custom Farmer Spawn Egg"(갈색 알)를 찾으세요. 검색창에 Custom Farmer를 쳐도 나옵니다.
- 성공 기준:
- 알을 우클릭했을 때 주민 외형을 가진 엔티티가 정상적으로 소환되어야 합니다.
- 만약 투명하거나, 하얀 상자로 보이거나, 게임이 튕긴다면 렌더러(Renderer) 등록에 문제가 있는 것입니다.
2. 농작물 수확 테스트 (낮 시간)
- 준비: 땅에 경작지(Farmland)를 만들고 밀을 심은 뒤, 뼛가루를 뿌려 완전히 다 자란 상태(노란색 밀)로 만드세요.
- 방법: 낮 시간(/time set day)에 농부 주민을 그 근처에 소환합니다.
- 성공 기준:
- 농부가 밀 쪽으로 걸어갑니다.
- 밀 블록이 파괴되면서 바닥에 밀 아이템이 떨어져야 합니다. (현재 코드는 인벤토리에 넣는 게 아니라 바닥에 드롭하는 방식입니다.)
3. 씨앗 심기 테스트 (낮 시간)
- 준비: 물이 근처에 있는 빈 경작지(Farmland)를 몇 칸 만드세요. 작물이 심어져 있지 않아야 합니다.
- 성공 기준:
- 농부가 빈 경작지로 이동합니다.
- 경작지 위에 밀 씨앗이 자동으로 심어지며 농사가 시작되어야 합니다.
4. 취침 테스트 (밤 시간)
- 준비: 농부 근처에 침대를 하나 설치합니다.
- 방법: 채팅창에 /time set night를 입력하여 밤으로 바꿉니다.
- 성공 기준:
- 농부가 농사를 멈추고 침대로 이동합니다.
- 침대에 도착하면 실제로 눕는 애니메이션이 나와야 합니다.
- 다시 아침(/time set day)이 되면 침대에서 일어나서 다시 밭으로 가야 합니다.
5. 생존 및 도망 테스트 (서바이벌 로직)
- 방법: 농부를 한 대 때려봅니다.
- 성공 기준:
- 농부가 "아악!" 소리를 내며 매우 빠른 속도로 주변을 허둥지둥 도망다녀야 합니다. (PanicGoal 작동 확인)
만약 작동하지 않는다면? (체크리스트)
- 반경 문제: 현재 코드는 주변 16칸 이내만 탐색합니다. 너무 멀리 작물을 두지 않았는지 확인하세요.
- 빛 수치: 작물은 빛이 없으면 자라지 않거나 농부가 인지하지 못할 수 있습니다. 횃불을 설치해 보세요.
- 경작지 상태: 땅이 일반 흙으로 돌아가지는 않았는지 확인하세요. (물 공급 필수)
요약: "이것만 되면 성공입니다!"
"낮에 다 자란 밀을 스스로 캐고 빈 땅에 씨앗을 심으며, 밤이 되면 근처 침대에 가서 눕는다."
이 세 가지 메인 루프가 돌아간다면 현재 코드는 완벽하게 작동하고 있는 것입니다. 확인해 보시고, 혹시 주민이 멍하니 서 있거나 이상한 행동을 한다면 말씀해 주세요! 로직을 바로 수정해 드릴 수 있습니다.

딱히 수확은 하지않는데
이거 경작지가 없어서, 심을 땅이 없어서엿음 문제
주요 개선 사항 이론 설명
- 우선순위 문제: MoveToBlockGoal은 기본적으로 가장 가까운 곳을 타겟으로 잡습니다. 만약 수확할 작물보다 심을 빈 땅이 더 가까우면 심는 것을 먼저 합니다. 이를 해결하기 위해 타겟을 찾는 로직에서 수확할 작물을 먼저 검색하도록 유도하거나, 수확 속도를 조절할 수 있습니다.
- 인벤토리 확장: AbstractVillager는 기본적으로 8칸의 인벤토리를 가집니다. 이를 SimpleContainer(27) 등으로 교체하여 상자 크기만큼 늘릴 수 있습니다.
- 순간이동 현상: MoveToBlockGoal은 원래 경로 찾기를 수행합니다. 하지만 도착 판정(isReachedTarget) 거리 설정이 너무 멀거나, 도착하자마자 다음 타겟으로 바로 넘어가면 순간이동처럼 보일 수 있습니다. 도착 시 약간의 대기 시간을 주어 자연스럽게 보이게 수정합니다.
- 하트 파티클: 마인크래프트 엔티티 이벤트 바이트 12번을 보내면 하트 파티클이 발생합니다.
cropblock부분 의심되네
if 조건문 자체를 통과못하고잇네
이론적 설명: 왜 instanceof CropBlock이 실패하는 것처럼 보일까?
(1) BlockState vs Block 관계
BlockState는 특정 좌표에 있는 블록의 **"상태(데이터)"**이고, state.getBlock()은 그 블록의 **"종류(정의)"**를 가져옵니다.
제시하신 코드를 보면:
public class CropBlock extends BushBlock ...
public class BushBlock extends Block ...
즉, CropBlock은 Block의 일종이 맞습니다. 따라서 instanceof CropBlock은 문법적으로나 논리적으로 문제가 없습니다.
(2) 가장 큰 의심 지점: "어떤 좌표를 검사하는가?"
농부 AI(MoveToBlockGoal)가 바라보는 blockPos는 농부가 **서 있는 바닥(Farmland)**입니다.
- 농부가 서 있는 곳(pos)의 블록은 Farmland 블록입니다.
- 우리가 수확해야 할 작물은 그 **한 칸 위(pos.above())**에 있습니다.
- 만약 코드에서 state.getBlock()을 할 때 pos(바닥)를 검사했다면, 결과는 항상 Farmland가 나오므로 instanceof CropBlock 조건문에서 로그조차 찍히지 않고 바로 튕겨 나갔을 것입니다.
(3) 조건문 중첩의 필요성
사용자께서 지적하신 대로 &&를 사용하면 어느 단계에서 false가 났는지 알 수 없습니다.
- 이 블록이 존재하는가?
- 이 블록이 CropBlock 계열인가? (인스턴스 체크)
- 이 블록의 나이(Age)가 최대치인가?
이 과정을 하나하나 뜯어서 로그를 찍어야만 정확한 범인을 찾을 수 있습니다.
이게 한칸위인게 맞나
좌표찍도록 로그 추가 할수도
getAge(net.minecraft.world.level.block.state.BlockState)'이(가) 'net.minecraft.world.level.block.CropBlock'에서 protected 액세스를 가집니다
상속을 하려나
흠
[ ]
crops.getAge(state) 대신 state.getValue(crops.getAgeProperty())를 사용
이건 왜되는거지
걍 protected인 메소드가 잇고
public인 메소드가 있고 그 차이일수도
왜 20초나 기다려야 할까요? (이론적 배경)
MoveToBlockGoal 코드를 보면 이 부분이 있습니다:
protected int nextStartTick(PathfinderMob p_25618_) {
return reducedTickDelay(200 + p_25618_.getRandom().nextInt(200));
}
- 문제점: 기본 설정은 한 번 작업을 끝내면 200
400틱(약 10초20초) 동안 "다음 밭이 어디지?"라고 찾는 행동 자체를 쉬게 되어 있습니다. 최적화를 위한 코드지만, 우리 일꾼 농부에게는 너무 긴 휴식 시간입니다. - 해결책: 이 메서드를 오버라이드하여 탐색 주기를 **1
2초(2040틱)**로 대폭 줄여야 합니다.
수정 및 최적화 포인트
- 탐색 주기 단축: nextStartTick을 줄여서 작업을 끝내자마자 바로 다음 밭을 찾게 합니다.
- 이동 속도 상향: 농사 속도가 0.6D였는데, 조금 답답할 수 있으니 0.8D 정도로 올려서 좀 더 빠릿하게 움직이게 합니다.
- 작업 직후 초기화: 작업을 완료하면 AI가 해당 좌표에 미련을 버리고 즉시 다른 곳을 찾도록 강제합니다.

계속 제자리에서 고개만 까닥거리는데
- 시선 고정(Head Lock) 문제: tick() 메서드에서 매 틱마다 setLookAt을 호출하여 발밑의 blockPos를 강제로 쳐다보게 만들었기 때문입니다. 농부가 목표에 도착하면 바로 자기 발을 보게 되어 고개가 꺾인 채 멈춘 것처럼 보입니다.
- 무한 루프(Stuck Loop) 문제: 작업을 완료한 후에도 AI가 "내가 방금 작업한 이 블록"을 여전히 유효한 타겟으로 인식하고 있을 때 발생합니다. 특히 isAir() 조건 때문에 수확 후 빈 땅이 되면 다시 그 자리를 심기 위해 잡으면서 제자리에서 틱이 꼬이는 현상입니다.
- acceptedDistance() 오버라이드 (핵심):
- 기본값은 너무 엄격하여 블록 "안"에 들어가려고 애쓰다가 타임아웃이 걸리는 경우가 많습니다. 2.0D 정도로 설정하면 경작지 옆에 서서 안정적으로 작업을 시작합니다.
- swing(InteractionHand.MAIN_HAND) 추가:
- 농부가 고개만 까닥거리는 게 아니라 실제로 팔을 휘두르는 애니메이션을 보여줍니다. 시각적으로 "일하고 있다"는 느낌을 줍니다.
[ ]
수확할떈, 팔 휘두르고
심을때는 앉는 동작<이런것도잇나 기본적으로 ?
'모딩 > 마인크래프트 모드 개발 일지' 카테고리의 다른 글
| 마크모딩) 스스로 발전하는 마을주민 만들기 #3 (0) | 2025.12.24 |
|---|---|
| 마크모딩) 스스로 발전하는 마을주민 만들기 #2 (1) | 2025.12.23 |
| 마크모딩) 부모 클래스 추상화 / NBT 데이터 영구 저장 / 주민 빵 (1) | 2025.12.22 |
| 마크 모딩) juggernaut도 새로운 스폰알로 구현 (0) | 2025.12.21 |
| 마크모딩) 도망만 다니던 주민을 전투 요원으로 (1) | 2025.12.21 |