모딩/마인크래프트 모드 개발 일지

마크모딩) 스스로 발전하는 마을주민 만들기 #2

kimchangmin02 2025. 12. 23. 12:22

 

[  ]최적화문제 해결해야할지도 

일단 이건 기능적으로 완성된 다음 고민해야할 문제일수도 

 

 

 

 

[  ]

-씨악 소모

-지금 씨앗을 줍고는 잇나(현재 인벤토리에 씨앗 얼마나잇는지 출력로그) [  ]

-나중엔 상자에 담는것도

-결국 돌아오기 구현하려면, 상자 추가해야하네

-근데 상자를 어떻게 추가할건지(농부랑 같이 생성 ?)

 

-소문 관련-이 메커니즘이 어떻게 돌아가는지, 그리고, 마치 대화하는것처럼, rendering (한글되나 ) 

-씨앗나눠주기 

 

 

-농부좀 꾸면줄수도, (바이옴에 맞는 농부옷 자동으로 커스텀 되나 )

-혹은 더 나중에는 3d모델로 커스텀 옷으로 꾸며줄수도 (나중에)

 

 

[  ]

  1. 무한 씨앗: 현재 performAction 메서드에서 농부의 인벤토리에 씨앗이 있는지 검사하지 않고 바로 Blocks.WHEAT를 설치합니다. 만약 생존 모드 밸런스를 고려한다면 인벤토리에서 씨앗 아이템을 소모하는 로직을 추가하는 것이 좋습니다.
  2. 아이템 습득: 현재 농부는 수확만 하고 바닥에 떨어진 밀과 씨앗을 줍지 않습니다. getInventory()는 만들어져 있지만, 아이템을 줍는 PickUpItemGoal 같은 AI가 추가되어야 진정한 자동 농부가 됩니다.

 

[  ]

  1. 가계 계승 (심화 아이디어):
    • 만약 농부가 죽는 게 너무 아쉽다면, 나중에 "농부 주민 두 명에게 음식을 주면 새로운 아기 농부가 태어나 그 집을 물려받는다"는 로직을 넣을 수 있습니다. 이건 마법처럼 스폰되는 게 아니라 **'번식'**이라는 자연스러운 과정이 됩니다.

 

 

[  ]

근데 집의 구조

주민 마을은 집구조가 랜덤하게 나오도록 되어잇잖아 

 

 

 

 

  • 수확(CropBlock 체크): 코드에서 작물인지 확인할 때 instanceof CropBlock을 사용합니다. 밀, 당근, 감자, 비트루트는 CropBlock 계열이라 수확이 가능하지만, **수박과 호박은 일반 블록(Block)**으로 취급되어 현재 AI가 작물로 인식하지 못하고 그냥 지나칩니다.
  • 심기(강제 지정): 현재 코드의 심기 로직은 Blocks.WHEAT.defaultBlockState() 밀 전용으로 고정되어 있습니다. 즉, 어떤 밭을 발견하든 농부는 오직 밀만 심습니다.

[   ]이건 씨앗을 소모하게 함으로써 그 이후에 바꿔야할것인것같네 

 

 

 

[  ]

이걸로, 일종의 던전만들수도, 

대신 드롭템 만들어서, 유저가 사용하거나 

아니면 경비병 만들수도 

구분 JSON 스폰 (시스템 중심) NBT 포함 (생존/서사 중심)
느낌 공장형 몹 스포너 느낌 실제 주민이 사는 느낌
자연스러움 죽어도 계속 나와서 이상함 죽으면 사라짐 (진짜 생존)
관리 시스템이 알아서 관리함 플레이어가 돌봐줘야 함
추천 컨셉 몬스터 사냥터, 던전 자급자득 마을, 생존 모드

 

 

[  ]관리자 처단검

주민에게도 먹히도록 


 

 

📑 목차

1. 기초 AI 버그 수정 및 인식 로직 개선

  • 수면 AI 최적화: 침대 도달 거리(Distance 0) 판정 오류 수정 및 강제 취침 로직 검토.
  • 농부의 레이더(Perception): 탐색 범위(16x9x16) 설정 이유 및 높이 값(±4)이 계산되는 방식.
  • 길찾기 알고리즘의 한계: 수직 지형(계단식 논)에서 농부가 목표를 놓치지 않게 하는 안전장치.

2. 마인크래프트 엔진의 이해 (성능과 구조)

  • BlockState 시스템의 비밀: 왜 수조 개의 블록을 객체로 만들지 않고 '상태 값'으로 관리하는가? (메모리 최적화 원리)
  • 참조 변수와 데이터 처리: 농부가 밭의 상태(Age)를 빠르게 읽어오는 메커니즘.
  • 우선순위 로직: '수확'과 '파종' 중 무엇을 먼저 할 것인가? (isValidTarget의 논리 구조)

3. 농부의 고립 방지 및 사회성 설계 (이론)

  • 표류(Straying) 문제: 한 방향으로 전진하다 길을 잃는 농부를 위한 '고무줄(Home Anchor)' 원리.
  • 자생적 마을 시스템: 상자 없이도 농부끼리 정보(비옥한 땅 좌표)와 자원(씨앗)을 공유하는 '선물 경제' 개념.
  • 저녁의 본능: 밤이 되면 특정 지점(집결지)으로 모이는 사회적 AI 설계.

4. 건축물 모딩과 구조물 생성 (WorldGen)

  • 설계도(NBT) 시스템: 구조물 블록을 이용해 게임 내 건축물을 파일로 추출하고 보존하는 방법.
  • 1.18.2 데이터 기반 생성: JSON 파일을 이용한 지형별 건축물 자동 배치 (Configured Structure Feature).
  • 엔티티 포함 저장: 설계도 안에 농부를 미리 배치하여 집과 함께 스폰시키는 기술.

5. 보급망 및 창고 관리 시스템 (Supply Chain)

  • 고향 상자(Home Chest) 등록: 농부가 태어난 집의 상자를 자신의 전용 창고로 인식하는 로직.
  • 보급 및 정리 사이클: 씨앗 부족 시 창고 방문, 수확물 입고 및 보충(Withdraw/Deposit) 자동화.
  • 예외 상황 대응: 상자가 부서지거나 밭이 망가졌을 때 '야생 모드'로 전환되는 심리적 임계값 설정.

6. 성능 최적화 및 시스템 안정화

  • 연산 부하 분석: 블록 스캔(n³) vs 길찾기(Pathfinding) vs 아이템 감지의 CPU 점유율 비교.
  • 게으른 탐색(Lazy Scanning): 매 틱마다 주변을 훑지 않고 쿨타임을 부여하는 최적화 기법.
  • 현재 해결 과제: 아이템 습득 권한(canPickUpLoot)과 커스텀 인벤토리 간의 동기화 문제 해결.

 

 


또한 농부가 모든 청크의 밭을 다 관리하나, 특정 몇개의 밭만 관리하나

그 범위가 어떻게 되지

<몇개만 관리하는데

그건 정해져잇나,ㄴㄴ 가변적, 

위치에 따라 갱신

대신 범위는 고정

위치는 가변적(새로운 위치에 가면 범위에 따라 새로운 위치가 탐색범위에 포함 

[ ok ]

 

 

-근데 농부가 지금 수확을 하고잇나 [ ok  ]< 확인해봐야 

 

 

[ ok ]

근데 우선순위 부여할수잇게하면 좋겟는데 

이거 함수를 분리시켜서, 

만약 둘다 가능하면, 수확먼저 

 

 

 


[ok  ]근데 농부 업데이트할때마다 이 ntb를 다시 수정해줘야겟지 ?ㄴㄴ

설계도(NBT) 준비 (코드 수정 전 작업)

  1. 모델하우스로 가서 농부를 두 명 소환합니다.
  2. 구조물 블록에서 **[개체 포함: 켜짐]**을 확인하고 다시 **[저장]**합니다.
  3. 새로 생성된 .nbt 파일을 다시 모드 폴더로 복사합니다.

 

[ ok ]문열기 로직은 아직 없지않나 


 

 

[  ]경비원 주민도 만들수도

[  ]더 다양한 주민만들기전에, 공통적인 인터페이스 만들수도 

<일단 이건 커스텀 주민의 ai완성하고 나서 

 

 

 

 

 

 


[  ]

주민이 할수있는 행동모음 뭐잇는지 살펴보는것도 

관련 클래스 


 

 

 

 


 

아 팔휘두르는게, 머리끄덕이는 저거인가

이거 행동 추가나 다른 행동으로 변경은 나중에 하기로하고 

 

 

 

 

일단 잠을 안자는 문제 

관련 로그 

 

 

 

 

 

침대발견은 하는데 잠을 안잠

 

 

 

로그에서 **[수면 AI] 침대로 이동 중... 남은 거리: 0**이라는 메시지가 계속 반복되고 있습니다. 이는 농부가 침대 좌표(72, 63, 123)와 정확히 같은 칸에 서 있지만, 내부적으로 **"도착했다(isReachedTarget)"**라고 판단하지 못해 startSleeping() 코드가 실행되지 않고 있는 상태입니다.

마인크래프트의 MoveToBlockGoal은 기본적으로 대상 블록의 중심에 아주 가깝게 가야 도착으로 인정하는데, 침대는 높이가 낮고 판정이 특이해서 이런 현상이 자주 발생합니다.

 

 

 

 

 

 

좀 자는 위치가 이상한디

 

 

좀 완전히 맞지는 않긴하지만 이정도면 넘어가도 될지도

침대부서져도 잘 이동하네 다시 

 

 

 

 

 

 

 

 

농부의 인식 범위는 자기 중심 반경 16칸(가로 33 x 세로 9 x 높이 33) 영역이며, 농부와 함께 이동합니다.

즉, 새로 이동하면, 그 위치로 부터 새로운 범위로 갱신 

 

 

 

 

 

 

높이 9인 이유

1. "손이 닿는 거리"가 아니라 "눈에 보이는 범위"입니다.

MoveToBlockGoal에서 설정하는 범위는 농부가 서 있는 자리에서 바로 작업을 하는 범위가 아니라, "저기 일거리가 있네? 가야겠다!"라고 결심하는 범위입니다.

  • 만약 높이 범위를 1로 설정하면: 농부보다 딱 1칸 위에 있는(머리 높이) 밭조차 인식하지 못합니다. 농부는 자기 발 밑에 있는 밭만 일거리가 있다고 생각하게 됩니다.
  • 높이 범위를 4로 설정하면: 농부가 낮은 곳에 서 있더라도, 언덕 위(4칸 높이)에 있는 밭을 "발견"할 수 있습니다. 일단 발견을 해야 인공지능이 **"점프해서 올라가거나 길을 돌아서 그곳까지 가야지"**라고 길찾기를 시작합니다.

2. 계단식 논(Terraced Farm) 대응

마인크래프트에서 많은 유저들이나 마을은 지형을 따라 계단식 밭을 만듭니다.

  • 농부가 1층 밭에서 작업을 끝냈을 때, 바로 윗단(2~3칸 위)에 있는 밭을 인식하지 못하면 농부는 "이제 주변에 밭이 없네?"라고 판단하고 농사를 멈추거나 엉뚱한 곳으로 가버립니다.
  • 높이를 4칸 정도로 여유 있게 두면, 농부가 아래층에서 일하면서도 위층의 빈 땅을 보고 계단을 타고 올라가서 연속적으로 일을 할 수 있게 됩니다.

3. 이동 알고리즘의 한계 보완

농부가 이동하다 보면 밭보다 낮은 **도랑(물 칸)**에 빠지거나, 밭 옆의 반 블록(Slab) 위에 서 있게 되는 경우가 많습니다.

  • 이때 농부의 발 위치(Y좌표)가 미세하게 밭보다 낮아지면, 높이 범위가 너무 좁을 경우 바로 눈앞에 있는 밭도 "높이가 안 맞아서" 인식 대상에서 제외되는 버그가 발생할 수 있습니다.
  • **9칸(±4)**이라는 범위는 지형이 조금 험난해도 농부가 "밭 근처에만 있으면 일단 목표를 놓치지 않게" 만드는 안전장치 같은 것입니다.

설정값(코드에 적는 숫자)은 4이고, 그 결과로 탐색하는 총 칸수가 9칸**인 것입니다.

 

 

 

 

 

왜 4를 적으면 9칸이 되나요?

마인크래프트 AI에서 높이 범위(Vertical Range)는 **"중심(농부의 발)에서부터 위로 몇 칸, 아래로 몇 칸"**을 볼지를 정하는 반지름 개념이기 때문입니다.

계산식은 이렇습니다:

  • 위로: 4칸
  • 아래로: 4칸
  • 농부가 서 있는 기준층: 1칸
  • 합계: 
  • 4(위)+4(아래)+1(기준)=9

그래서 코딩할 때 **4**라고 적으면, 농부는 자기 발 위치를 기준으로 총 9칸 높이의 범위를 레이더로 스캔하게 됩니다.

 

 

 

 

 

현재 코드의 목표 탐색 로직(isValidTarget)은 수확할 목표와 씨를 심을 목표 둘 다를 포함하고 있습니다.

 

 

 

 

 

 

우선순위를 부여하려면 AI가 목표를 **탐색하는 단계(isValidTarget)**에서부터 "지금 이 땅이 빈 땅이라도, 혹시 주변에 수확할 수 있는 다 자란 작물이 있다면 빈 땅은 일단 무시해라"라는 논리가 들어가야 합니다.

 

 

 

BlockState 객체는 멤버 변수로 직접 값을 가지고 있지 않고, getValue(Property)라는 메소드를 통해 정보를 가져옵니다.

이 땅이 수확할 땅인지, 아닌지 판단 어케함

이 블록이 대충 정보 가지고잇는듯한데 

 

 

 

 

 

  1. 멤버 변수 형태(pos.isSeed)**로는 존재하지 않습니다.
  2. 대신 **state.getValue(속성키)**를 사용하여 블록 내부에 저장된 모든 정보를 꺼내올 수 있습니다.
  3. 이 방식 덕분에 농부가 "거의 다 자란 것만 골라 캐기"나 "물기가 충분한 땅에만 심기" 같은 정교한 지능을 가질 수 있게 됩니다.

근데 1,2차이가 뭐지 

 

 

 

 

 

네, 정확히 보셨습니다! 사용자님의 **"아, 땅 자체가 객체가 아니라?"**라는 깨달음이 마인크래프트 엔진의 핵심 설계 원칙입니다.

조금 더 구체적으로, 왜 마인크래프트가 수조 개의 블록을 감당할 수 있는지 객체 지향 프로그래밍의 관점에서 쉽게 설명해 드릴게요.


1. 일반적인 생각 (비효율적): "모든 칸이 객체다"

만약 마인크래프트가 월드의 모든 칸(X, Y, Z)마다 new DirtBlock() 같은 객체를 생성한다면 어떻게 될까요?

  • 청크 하나(16x256x16)에만 약 65,536개의 객체가 필요합니다.
  • 플레이어가 조금만 돌아다녀도 수백만 개의 객체가 생기고, 컴퓨터 메모리(RAM)는 바로 터져버릴 것입니다.

2. 마인크래프트의 방식 (효율적): "세상은 숫자로 된 지도다"

마인크래프트 월드(저장 장치나 메모리)는 거대한 3D 숫자 배열에 가깝습니다.

  • 블록 객체 (Block): 게임 전체에 DirtBlock이라는 객체는 단 하나만 존재합니다. (싱글톤)
  • 월드 데이터: 특정 좌표 (10, 64, 10)에는 DirtBlock 객체가 들어있는 게 아니라, **"여기는 흙 블록 상태 번호 15번이야"**라는 **숫자(ID)**만 적혀 있습니다.

3. 그럼 BlockState는 무엇인가?

사용자님이 말씀하신 "플레이어가 심으면 그때그때 바뀌는 정보"를 처리하기 위해 마인크래프트는 미리 정의된 상태들을 만들어 둡니다.

예를 들어 밀(Wheat) 블록을 생각해 봅시다.

  • 밀은 나이(age)가 0부터 7까지 있습니다.
  • 마인크래프트는 게임이 켜질 때, 밀에 대한 8가지 BlockState 객체를 미리 딱 만들어 둡니다.
    • State 0: 밀 [age=0]
    • State 1: 밀 [age=1] ... State 7: 밀 [age=7]
  • 전 세계에 밀이 100만 개가 심어져 있어도, 그 100만 개는 모두 미리 만들어진 8개의 객체 중 하나를 가리키고(참조하고) 있을 뿐입니다.

4. "맵이 바뀌면 어떻게 되나요?"

플레이어가 밀 씨앗을 심어서 age가 0에서 1로 변하는 상황:

  1. (X, Y, Z) 좌표의 데이터를 직접 수정하는 게 아닙니다.
  2. 월드라는 지도에서 해당 좌표에 적힌 **"참조 값(ID)"**을 State 0에서 State 1교체하는 것입니다.
  3. 농부 AI가 getValue(age)를 호출하면, 월드는 그 좌표에 연결된 BlockState 객체에게 "너 나이가 몇이니?"라고 물어보고 결과를 알려주는 것입니다.

5. 요약하자면 (중요!)

  • 땅(좌표)은 객체가 아니다: 그냥 지도의 좌표일 뿐입니다.
  • 블록은 공유된다: 전 세계의 모든 '돌'은 단 하나의 '돌 객체'를 공유합니다.
  • 상태(BlockState)는 미리 정해진 조합이다: 나이 0인 밀, 나이 1인 밀... 이런 조합들은 이미 메모리에 준비되어 있고, 월드는 그저 좌표마다 "여기는 몇 번 조합!"이라고 표시만 해둡니다.

※ 예외 (BlockEntity):
하지만 상자(Chest)나 화로(Furnace)처럼 안에 아이템이 들어있어서 칸마다 정보가 다 달라야 하는 블록들은 어쩔 수 없이 별도의 BlockEntity 객체를 만듭니다. 그래서 상자를 수천 개 깔면 게임이 렉 걸리는 이유가 바로 여기에 있습니다. (흙이나 밀은 100만 개가 있어도 렉이 거의 없죠!)

이 시스템 덕분에 마인크래프트는 무한에 가까운 세상을 아주 적은 메모리로도 돌릴 수 있는 것입니다. 농부 코드가 BlockState를 계속 가져와서 쓰는 이유도 바로 이 공유된 상태 도서관에서 정보를 빌려오기 위해서입니다.

 

오,. 참조변수(자바)에서 배운게

이거 이해할때 도움이 되기는 하네 

 

 

 

if (cropState.getBlock() instanceof CropBlock crops) {
    if (crops.isMaxAge(cropState)) return true;
}
  • 여기서 cropState는 전 세계에 단 하나뿐인(혹은 아주 소수만 존재하는) **"다 자란 밀 상태 객체"**를 가리키는 참조 변수입니다.
  • 수백만 개의 밀을 검사해도, 결국 농부는 똑같은 몇 개의 객체 주소값만 비교하는 셈이라 속도가 엄청나게 빠릅니다.

 

 

오 밀 파괴를 먼저하는모습

 

 

 

 

 

1. 현재 농부의 상태 점검 (Q&A)

  • 씨앗을 줍고 있나?
    • 아니요. 현재 코드에는 아이템을 감지하거나 줍는 AI(Goal)가 전혀 없습니다. 바닥에 씨앗이 떨어져 있어도 농부는 그냥 지나칩니다.
  • 현재 메커니즘은?
    • **파괴(수확) → 안 줍고(아이템 방치) → 자동 심기(무한)**가 맞습니다.
    • performAction에서 인벤토리를 확인하지 않고 level.setBlock을 바로 실행하기 때문에 씨앗이 없어도 무한정 심을 수 있습니다.

2. 주민의 메모리 구조 (Minecraft AI 이론)

마인크래프트 1.14 이후 주민은 Brain (두뇌) 시스템을 사용하여 기억을 관리합니다.

  1. MemoryModule (기억 장치):
    • 주민은 HOME (침대 위치), JOB_SITE (작업대 위치), MEETING_POINT (종 위치) 등을 좌표(GlobalPos) 형태로 기억합니다.
    • 이 기억은 특정 시간이 지나면 잊혀지거나(Expiry), 다른 기억으로 덮어씌워집니다.
  2. 하지만 사용자님의 코드는?
    • 현재 GoalSelector 방식을 사용 중인데, 이는 구버전 AI 방식입니다. 이 방식에서 "기억"을 하게 하려면 **엔티티 클래스 내부에 변수(List, Queue 등)**를 만들어 직접 저장해야 합니다.

 

 

 

  • 기억(자료구조): "어디에 심었지?"를 기억하기 위해 List<BlockPos> 같은 자료구조를 직접 만들 수도 있지만, 이는 추천하지 않습니다. 왜냐하면 블록이 불에 타거나, 플레이어가 캐버리면 그 기억은 '틀린 정보'가 되기 때문입니다.

근데 사실 마크의 맵 정보로 그 장소가 다 자랏는지 아닌지 알수잇긴한데

사실 현실적으로는 가보지않고는 확인 못하는게 사실 더 진짜같긴한데 

 

 

 

 

 

 

 

내가 우려하는건, 이런 상황임, 가령, 경작지는 충분히 잇는데, (쉽게 생각하기 위해, 1차원으로 생각하면 0에서 시작 <16에 밀잇음<이동, 17에심음, 17+16자리에 밀잇음< 17+16+1자리에 심음, 근데 주변 아무것도없음<이떄 아무것도 못해버리잖아, 되돌아가는거 안하면

 

 

표류(Straying) 문제"**라고 합니다.

농부가 일감을 찾아 한 방향으로 계속 전진하다 보면, 정작 뒤쪽(처음 시작점)에서 작물이 다 자라도 자신의 '인식 주머니(16칸 범위)' 밖에 있게 되어 고립되는 현상입니다.

 

 

 

 

1. "고무줄" 원리 (Home Anchor / Base Point)

가장 확실한 방법은 농부에게 **'집(기준점)'**을 만들어주는 것입니다.

  • 설계: 농부가 스폰될 때나 처음 일을 시작할 때의 좌표를 homePos라는 변수에 저장합니다.
  • 작동: isValidTarget에서 주변 16칸을 뒤졌는데 할 일이 하나도 없다면, 농부는 **"어? 할 일이 없네? 일단 집으로 돌아가자"**라고 판단하고 homePos 근처로 이동하게 합니다.
  • 효과: 농부가 멀리 갔다가도 일감이 없으면 다시 중심으로 돌아오기 때문에, 마치 고무줄처럼 밭 안에서만 머물게 됩니다.

2. "정찰" 원리 (Idle Wandering)

사용자님의 코드에 이미 들어있는 WaterAvoidingRandomStrollGoal이 사실 이 역할을 미세하게 수행하고 있습니다.

  • 설계: 할 일이 없을 때 가만히 서 있는 게 아니라, 아무 방향이나 무작위로 몇 걸음씩 걷게 합니다.
  • 작동: 농부가 34번 지점(일감이 없는 곳)에서 멍하니 있다가 무작위로 왼쪽(뒤쪽)으로 몇 걸음 이동합니다. 그러다 운 좋게 16칸 레이더 안에 17번 지점의 밭이 들어오면 다시 일을 시작합니다.
  • 한계: 무작위이기 때문에 운이 나쁘면 아예 농장 밖으로 멀리 떠나버릴 수도 있습니다.

3. "마을의 중심" 시스템 (Vanilla 방식)

마인크래프트의 실제 주민들이 사용하는 방식입니다.

  • 설계: 주민은 특정 블록(침대, 종, 작업대)을 **'내 구역의 중심'**으로 인식합니다.
  • 작동: 주민의 AI에는 "할 일이 없으면 내가 등록한 침대(또는 작업대) 근처로 이동하라"는 우선순위가 항상 들어있습니다.
  • 적용: 우리 커스텀 농부에게도 "일감이 없으면 주변의 가장 가까운 경작지(Farmland) 뭉치나 침대로 가라"는 목표를 추가하면 해결됩니다.

 

 

 

 

 

 

"기억"보다 "본능"

농부가 "내가 어디에 심었지?"라고 모든 좌표를 기억하는 것은 메모리 낭비지만, **"내 집(중심점)이 어디지?"**라는 좌표 하나를 기억하는 것은 매우 가볍고 효과적입니다.

 

결국, 게임에서 성능을 무시할수없나보네

gta 망가진 차량 계속 그대로 잇으면 좋겟건만 

 

 

 

 

  • 블록 스캔 (비효율): 반경 32칸을 뒤지는 건 수만 개의 블록을 하나하나 체크하는  계산입니다.n**3
  • 상자 이동 (효율): 상자의 위치는 한 번만 찾거나 미리 저장해두면 끝입니다. 농부에게 "저 좌표(상자)로 가!"라고 명령하는 것은 단순한 좌표 이동일 뿐이며, 길찾기 알고리즘은 농부가 이동을 시작할 때 딱 한 번만 계산하면 됩니다.

 

 

 

 

 

 

 

1. "공유된 기억" (The Shared Hotspot)

농부들이 각자 자기 갈 길만 가는 '표류'를 막고 한데 어우러지게 하려면, **물리적인 상자 대신 '개념적인 마을 중심'**이 필요합니다.

  • 포인트 방식: 농부들이 각자 "내가 오늘 가장 기분 좋게 일했던 장소(수확을 많이 한 곳)"의 좌표를 머릿속에 저장합니다.
  • 소문 퍼뜨리기: 농부들이 근처에서 마주칠 때(예: 3칸 이내), 서로의 '꿀좌표' 정보를 교환합니다.
  • 결과: A 농부가 발견한 비옥한 땅 정보가 B에게 전달되고, 결국 할 일이 없어진 농부들은 서로가 알려준 '검증된 작업지' 근처로 모이게 됩니다. 이게 자연스럽게 **'유동적인 마을 구역'**이 됩니다.

2. "선물 경제" (The Gift Economy - 상호 부조)

자업자득형 농부들이 마을을 이루는 가장 큰 동기는 **'부족한 자원의 공유'**입니다.

  • 씨앗 던져주기: A 농부는 씨앗이 64개로 넘치고, B 농부는 0개라면? A가 B 옆을 지나갈 때 씨앗 일부를 바닥에 던져주거나 전달하는 AI를 넣습니다.
  • 도와주기 본능: 내 주변 16칸은 깨끗한데, 바로 옆 농부의 구역에 수확할 게 산더미라면? "내 일이 아니야"라고 무시하는 게 아니라 '도와주기' 상태가 되어 옆 농부의 밭을 대신 수확해줍니다.
  • 결과: 농부들이 서로 아이템을 주고받고 일을 도와주면서, 플레이어가 상자를 놓아주지 않아도 그들 스스로 **'이동식 물류 네트워크'**를 형성하게 됩니다.

3. "저녁 모임" (The Social Anchor - 저녁의 본능)

낮에는 각자 흩어져 개척하더라도, 밤이 되기 직전에는 한곳으로 모이는 본능을 부여합니다.

  • 집결지 설정: 농부들이 낮에 일하다가 발견한 **'물가'**나 **'가장 많은 경작지가 모여 있는 곳'**을 그날의 임시 캠프로 정합니다.
  • 사회적 배회: 해가 질 무렵(Sunset), 농사 AI를 끄고 "동료 농부 근처로 가기" AI를 켭니다.
  • 결과: 밤이 되면 농부들이 한자리에 옹기종기 모여 있게 됩니다. 여기에 나중에 '전사 주민'이나 '치료사 주민'이 추가되면, 그들도 이 농부들의 모임 장소로 찾아오게 설계하면 됩니다. 이것이 곧 **'자생적 마을'**이 됩니다.

4. 이 방향성의 장점 (성능과 게임성)

  1. 상자가 필요 없음: 농부 자체가 아이템 저장소이자 전달자가 되므로, 맵에 인위적인 구조물을 강제할 필요가 없습니다.
  2. 유연한 확장: 플레이어가 어디에 농장을 만들든 농부들은 그 지형에 맞춰 '오늘의 마을'을 형성합니다.
  3. 성능 효율: 24시간 내내 마을을 계산하는 게 아니라, **'마주칠 때'**나 **'해가 질 때'**만 사회적 로직이 돌아가므로 성능 부담이 적습니다.

5. 요약하자면

사용자님의 주민들이 "따로 똑똑하고 같이 풍요로운" 존재가 되게 하려면, 다음 세 가지만 기억하면 됩니다.

  • 정보 공유: "저기 캘 거 많더라" (좌표 교환)
  • 자원 공유: "너 씨앗 없지? 여기 있어" (아이템 나눔)
  • 시간 공유: "밤에는 같이 모여 있자" (집결 본능)

 

 

 

 

 

 

 

자원,정보, 시간 공유하는건좋은데,

일단 만나야지 뭘 공유할수잇잖아

 

 

<건축물 모딩으로 넘어감

농부관련기능 더 구현하기 위해서는 

 

 

 

 

 

 

1. 건축물 설계하기 (Blueprint)

직접 코드로 "여기 돌 설치해라, 저기 나무 설치해라"라고 명령하는 방식은 예전 방식입니다. 지금은 게임 내에서 직접 짓고 저장하는 방식을 씁니다.

  • 구조물 블록(Structure Block): 게임 내에서 /give @s structure_block 명령어로 블록을 얻은 뒤, 내가 지은 집을 영역 지정해서 .nbt 파일로 저장합니다.
  • 파일 저장: 저장된 .nbt 파일은 모드의 resources/data/모드아이디/structures 폴더에 넣습니다. 이 파일이 건축물의 '설계도'가 됩니다.

2. 건축물을 세상에 자연적으로 생성시키기 (Data-Driven Generation)

1.18.2 버전부터 건축물 생성은 자바 코드보다는 **JSON 파일(데이터 팩 방식)**을 더 많이 사용합니다.

  • Structure JSON: "내 설계도 파일을 어떤 바이옴(평원, 숲 등)에 스폰시킬 것인가?", "얼마나 자주 나타나게 할 것인가(희귀도)?" 등을 설정합니다.
  • 지그소(Jigsaw) 시스템: 마인크래프트 마을처럼 집, 도로, 울타리가 무작위로 조합되어 하나의 큰 마을을 이루게 하고 싶을 때 사용하는 기술입니다. 단순히 집 한 채가 아니라 '농장 구역' 전체를 무작위로 생성하게 할 수 있습니다.

3. 건축물 근처에 농부가 살게 하는 방법 (연결 로직)

건축물이 생성되었다면, 농부가 그곳을 '고향'으로 인식하게 만드는 방법은 두 가지가 있습니다.

방법 A: 생성 시 농부도 같이 스폰하기 (추천)

  • 건축물 설계도를 저장할 때, 그 안에 **'데이터 마커'**나 '엔티티' 정보를 포함할 수 있습니다.
  • 건축물이 세상에 생성되는 그 순간, 집 안에 우리가 만든 커스텀 농부가 자동으로 한 마리 생겨나게 합니다.
  • 이때 농부의 변수에 **"지금 네가 태어난 이 좌표가 네 집이야"**라고 저장해버리면 됩니다.

방법 B: POI(Point of Interest) 시스템 활용

  • 마인크래프트 주민들이 '직업 블록'을 찾는 방식입니다.
  • 건축물 내부에 특정 블록(예: 커스텀 농부 작업대, 혹은 특정 깃발 등)을 배치합니다.
  • 농부는 주변을 돌아다니다가 이 특수 블록을 발견하면 "아, 여기가 내 관리 구역이구나!"라고 인식하고 그 주변을 벗어나지 않게 됩니다.

4. 야생 생존 농부를 위한 최적의 시나리오

  1. 방치된 농가(Abandoned Farm) 생성: 플레이어가 없는 야생 곳곳에 아주 작은 오두막과 밀밭이 포함된 구조물이 희귀하게 생성되도록 설정합니다.
  2. 농부의 배치: 그 구조물 생성 시 농부가 한 마리 같이 스폰됩니다.
  3. 중심점 고정: 농부의 AI는 자신이 스폰된 구조물의 중심 좌표를 절대 잊지 않습니다.
  4. 행동: 낮에는 그 구조물 주변 밭을 갈고, 밤에는 그 오두막으로 들어가서 쉼으로써 플레이어가 아무것도 안 해줘도 스스로 마을을 이루고 사는 모습을 연출할 수 있습니다.

요약: 개발 방향

  • 건축물: 게임 내에서 직접 지어서 .nbt 파일로 추출하세요.
  • 생성: JSON 설정을 통해 특정 바이옴에 나타나게 만드세요.
  • AI: 농부 엔티티가 생성될 때의 좌표를 HomePos로 저장하여, 그 반경을 벗어나지 않게 하는 Goal을 추가하세요.

 

 

 

 

 

이해합니다! 모딩에서 건축물(구조물)을 자연스럽게 만드는 건 복잡해 보일 수 있지만, 사실 **"붕어빵 찍어내기"**라고 생각하면 훨씬 쉽습니다.

아주 쉬운 3단계로 설명해 드릴게요.


1단계: 내 집을 "설계도"로 만들기 (게임 안에서)

코딩으로 집을 짓는 게 아니라, 마인크래프트 게임 안에서 직접 집을 짓고 파일로 저장하는 방식입니다.

  1. 마크 게임 안에서 예쁜 농가와 밭을 하나 짓습니다.
  2. **'구조물 블록(Structure Block)'**이라는 특수 블록을 사용합니다. (/give @s structure_block)
  3. 이 블록으로 내가 지은 집을 상자로 감싸듯 영역을 지정하고 [저장] 버튼을 누릅니다.
  4. 그러면 내 컴퓨터에 **.nbt**라는 확장자의 파일이 생깁니다. 이게 바로 건축 설계도입니다.

2단계: "이 설계도를 맵에 뿌려줘"라고 말하기 (설정 파일)

이제 설계도가 생겼으니, 마인크래프트한테 "야생 돌아다니다 보면 가끔 이 집을 지어줘"라고 시켜야 합니다.

  • 이건 복잡한 자바 코드를 쓰는 게 아니라, 메모장 같은 JSON 파일 몇 개만 만들면 됩니다.
  • "평지에만 지어줘", "100번 이동할 때 1번꼴로 나오게 해줘" 같은 내용을 적어서 모드 폴더에 넣기만 하면 됩니다.
  • 그러면 마인크래프트가 맵을 만들 때 알아서 그 집들을 야생에 툭툭 던져놓습니다.

3단계: 농부를 그 집에 "배달"하기

집이 생길 때 농부도 같이 태어나게 해야겠죠?

  1. 위에서 만든 설계도 파일(.nbt) 안에 "여기에 농부 한 마리 스폰해줘"라는 표시를 미리 해둘 수 있습니다.
  2. 그러면 집이 야생에 생길 때, 농부도 그 안에서 뿅! 하고 나타납니다.
  3. 농부한테는 딱 한 줄의 코드만 있으면 됩니다. "너는 태어난 자리가 집이니까, 거기서 20칸 이상 멀어지지 마!"

요약: 딱 이것만 하시면 됩니다!

  1. 게임에서 집 짓기 (코딩 아님)
  2. 구조물 블록으로 저장하기 (파일 추출)
  3. 설정 파일(JSON) 몇 개 만들기 (맵 생성 규칙 정하기)
  4. 농부 코드에 "태어난 곳 = 집"이라고 저장하기 (본능 심어주기)

이게 왜 더 쉽나요?

  • 시각적입니다: 집을 코드로 일일이 좌표 계산해서 짓지 않아도 됩니다. 그냥 마크 하듯이 집을 지으면 끝입니다.
  • 공유가 쉽습니다: 설계도 파일 하나만 있으면 다른 모드에도 그 집을 똑같이 쓸 수 있습니다.
  • 농부가 똑똑해집니다: 농부는 자기가 태어난 예쁜 집 주변에서만 농사를 지으므로, 길을 잃을 걱정이 없습니다.

이 중에서 **"1단계: 게임에서 집을 지어서 파일로 만드는 법"**부터 차근차근 해보시는 건 어떨까요? 이게 건축 모딩의 시작입니다!

 

 

 

 

 

 

 

 

 

네, 맞습니다! 가장 먼저 해야 할 일은 마인크래프트 게임 안에서 "표본이 될 예쁜 집과 밭"을 짓는 것입니다.

구조물 블록은 내가 지은 건축물을 '복사(Copy)'해서 '파일'로 저장해주는 도구라고 생각하면 됩니다. 밭까지 포함해서 저장하는 방법을 아주 쉽게 설명해 드릴게요.


1단계: 모델하우스와 밭 짓기

먼저 빈 평지에 농부가 살 집과 관리할 밭을 만듭니다.

  • 집: 농부가 잠을 자거나 쉴 공간을 만듭니다.
  • 밭: 집 바로 옆에 **경작지(Farmland)**를 만들고, 물을 채우고, 씨앗까지 심어두세요.
  • 중요: 나중에 "이 파일"을 그대로 찍어낼 것이기 때문에, 지금 밭에 씨앗을 심어두면 나중에 야생에서 생성될 때도 씨앗이 심어진 채로 나타납니다.

2단계: 구조물 블록 얻고 배치하기

  1. 채팅창에 /give @s minecraft:structure_block을 쳐서 블록을 받습니다.
  2. 내가 지은 건물과 밭의 한쪽 모서리 땅바닥에 이 블록을 설치합니다.

3단계: 영역 지정하기 (가장 중요!)

설치한 구조물 블록을 우클릭하면 복잡한 화면이 나오는데, 당황하지 말고 아래 순서대로 하세요.

  1. 모드 변경: 왼쪽 아래 버튼을 눌러 [저장(Save)] 모드로 바꿉니다.
  2. 영역 크기 설정(Size):
    • 집과 밭이 모두 들어가도록 숫자를 입력합니다.
    • 예: 가로(X): 15, 높이(Y): 10, 세로(Z): 15 (숫자를 키우면 하얀 선 박스가 커집니다.)
  3. 위치 조정(Offset): 하얀 선 박스가 내가 지은 집과 밭을 정확히 감싸도록 숫자를 조절합니다.
    • 꿀팁: Y(높이)의 Offset을 -1로 설정하면 땅바닥(경작지와 물)까지 포함해서 저장할 수 있습니다.
  4. 엔티티 포함(Include Entities): 이 옵션을 **[켜짐(ON)]**으로 하면, 집 안에 소환해둔 농부까지 통째로 저장할 수 있습니다!

4단계: 파일로 저장하기

  1. 구조물 이름: 상단 칸에 이름을 적습니다. (예: mymod:farmer_home)
  2. [저장] 버튼 클릭: "구조물이 저장되었습니다"라는 메시지가 뜨면 성공입니다.

5단계: 저장된 파일 찾기 (내 컴퓨터에서)

이제 게임 밖으로 나와서 저장된 파일을 모드 폴더로 옮겨야 합니다.

  1. 마크가 설치된 폴더의 saves -> 내가 플레이하던 월드 이름 -> generated 폴더로 들어갑니다.
  2. 그 안에 있는 .nbt 파일을 복사합니다.
  3. 내 모드 프로젝트 폴더의 src/main/resources/data/모드아이디/structures/ 안에 붙여넣습니다.

밭을 포함할 때의 핵심 포인트!

  • 물 칸도 같이 저장: 구조물 블록 영역 안에 물(Water) 블록도 반드시 포함되게 하세요. 그래야 야생에서 생길 때 밭이 마르지 않습니다.
  • 농부도 같이 저장: 아예 농부를 집 안에 스폰시켜둔 상태에서 저장해 보세요. 그러면 나중에 맵 어딘가에 이 집이 생길 때, 농부가 이미 집 안에서 플레이어를 기다리고 있는 모습을 볼 수 있습니다.

결론:

  1. 지금 바로 마크에서 예쁜 집과 밭을 지으세요!
  2. 영역을 넉넉하게 잡아서 **"집 + 밭 + 농부 + 물"**을 한꺼번에 하얀 박스 안에 넣고 저장 버튼만 누르면 설계도 준비 끝입니다!

다 지으시고 영역 지정하는 단계에서 막히면 다시 물어봐 주세요! 하얀 선 박스 맞추는 게 처음엔 조금 헷갈릴 수 있습니다.

 

 

 

 

 

 

 

 

일단 농부집

 

 

 

 

 

 

 

 

 

 

 

경계표시 어케함 

 

 

 

 

 

저장 버튼을 누른 후 할 일:

"구조물이 'farmerhouse_'로 저장되었습니다"라는 메시지가 채팅창에 뜨면 성공입니다!

이제 이 파일을 찾으러 가야 합니다.

  1. 윈도우 탐색기를 엽니다.
  2. 주소창에 %appdata%를 입력하고 엔터.
  3. .minecraft -> saves -> 현재 플레이 중인 월드 폴더 -> generated -> minecraft -> structures 폴더로 이동합니다.
  4. 거기에 있는 farmerhouse_.nbt 파일을 복사해서 모드 개발 폴더로 옮기시면 됩니다!

 

 

 

 

개발 환경(IntelliJ나 Eclipse 같은 IDE)에서 runClient로 실행 중이라면 %appdata% 폴더가 아니라, 현재 작업 중인 모드 프로젝트 폴더 안에 데이터가 저장됩니다.

다음 경로를 따라가 보세요.

1. 설계도 파일(.nbt)이 저장된 위치 (찾아야 할 곳)

보통 개발 환경의 기본 실행 경로는 프로젝트 루트의 run 폴더입니다.

  • 경로: [내 프로젝트 폴더] / run / saves / [월드 이름] / generated / [네임스페이스(기본은 minecraft)] / structures /
  • 여기에 방금 저장한 farmerhouse_.nbt 파일이 들어있을 겁니다.

2. 이 파일을 모드 소스 코드로 옮기기 (배포용)

찾은 .nbt 파일을 복사해서, 실제로 모드에 포함될 수 있도록 아래 폴더로 옮겨주어야 합니다. (직접 폴더를 만들어야 할 수도 있습니다.)

  • 옮길 목적지: [내 프로젝트 폴더] / src / main / resources / data / [내모드ID] / structures /

주의사항:

  • generated/minecraft/structures에 생겼다고 해서 그대로 두면 나중에 모드를 빌드(배포)할 때 포함되지 않습니다. 반드시 src/main/resources 아래의 지정된 경로로 복사해 넣어야 합니다.
  • 내모드ID 부분은 본인이 설정한 모드 ID(영문 소문자)로 폴더를 만드세요.

 

 

 

 

src/main/resources 폴더 아래에 assets 폴더만 있고 data 폴더가 없는데, 설계도 파일(.nbt)은 data 폴더 시스템에 넣어야 합니다.

 

 

방법 1: IntelliJ 프로젝트 창에서 바로 하기 (가장 추천)

  1. 원본 파일 찾기:
    • 왼쪽 프로젝트 트리에서 run -> saves -> 월드이름 -> generated -> minecraft -> structures 폴더를 엽니다.
    • farmerhouse_.nbt 파일을 마우스 오른쪽 클릭하고 [Copy] (또는 단축키 Ctrl + C)를 누릅니다.
  2. 대상 폴더에 붙여넣기:
    • 아까 새로 만든 src/main/resources/data/mymod/structures 폴더를 마우스 오른쪽 클릭합니다.
    • [Paste] (또는 단축키 Ctrl + V)를 누릅니다.
  3. 이름 확인 창:
    • 붙여넣기를 누르면 이름을 바꿀 거냐고 묻는 작은 창이 뜹니다.
    • 그대로 두셔도 되고, 깔끔하게 farmerhouse.nbt로 이름을 고쳐서 **[OK]**를 누르셔도 됩니다.

 

 

 

 

 

 

이제 가장 중요한 '마인크래프트가 이 설계도를 어디에, 얼마나 자주 지을지' 규칙을 정해줄 차례입니다. 1.18.2 버전부터는 자바 코드 대신 JSON이라는 설정 파일을 사용합니다.

조금 복잡해 보일 수 있지만, 제가 드리는 내용을 복사해서 파일로 만들기만 하면 됩니다.

3개의 폴더3개의 파일을 만들 것입니다.


1. 폴더 구조 만들기

먼저 src/main/resources/data/mymod/ 폴더 아래에 **worldgen**이라는 폴더를 만들고, 그 안에 아래 3개의 폴더를 더 만듭니다.

최종 경로는 이래야 합니다:

  1. data/mymod/worldgen/template_pool/ (설계도 묶음)
  2. data/mymod/worldgen/structure/ (구조물 설정)
  3. data/mymod/worldgen/structure_set/ (생성 빈도 설정)

2. 첫 번째 파일: Template Pool (설계도 연결)

마인크래프트에게 "어떤 .nbt 파일을 쓸 거야?"라고 알려주는 파일입니다.

  • 파일명: farmer_house_pool.json
  • 경로: template_pool 폴더 안
    {
  "name": "mymod:farmer_house_pool",
  "fallback": "minecraft:empty",
  "elements": [
    {
      "weight": 1,
      "element": {
        "location": "mymod:farmerhouse",
        "processors": "minecraft:empty",
        "projection": "rigid",
        "element_type": "minecraft:single_pool_element"
      }
    }
  ]
}
  

(주의: mymod:farmerhouse 부분은 아까 넣은 .nbt 파일 이름과 같아야 합니다.)


3. 두 번째 파일: Structure (생성 규칙)

"이 집을 어느 지형에, 어떤 높이로 지을 거야?"를 정합니다.

  • 파일명: farmer_house.json
  • 경로: structure 폴더 안
    {
  "type": "minecraft:village",
  "start_pool": "mymod:farmer_house_pool",
  "size": 1,
  "max_distance_from_center": 80,
  "biomes": "#minecraft:has_structure/village_plains",
  "step": "surface_structures",
  "terrain_adaptation": "beard_thin",
  "start_height": {
    "absolute": 0
  },
  "project_start_to_heightmap": "WORLD_SURFACE_WG",
  "use_expansion_hack": false,
  "spawn_overrides": {}
}
  

(설명: biomesvillage_plains이므로 주로 평원에서 나타납니다.)


4. 세 번째 파일: Structure Set (빈도 조절)

"얼마나 자주 이 집이 나타나게 할 거야?"를 정합니다.

  • 파일명: farmer_houses.json
  • 경로: structure_set 폴더 안
    {
  "structures": [
    {
      "structure": "mymod:farmer_house",
      "weight": 1
    }
  ],
  "placement": {
    "type": "minecraft:random_spread",
    "spacing": 20,
    "separation": 8,
    "salt": 123456789
  }
}
  

(설명: spacing: 20은 약 20청크마다 하나씩 시도한다는 뜻입니다. 숫자를 줄이면 더 자주 나옵니다.)


5. 확인 및 실행!

  1. 모든 파일을 저장했으면 프로젝트를 다시 실행(runClient)하세요.
  2. 새 월드를 만듭니다. (기존 월드에는 이미 만들어진 땅이라 안 나올 수 있습니다.)
  3. 게임에 접속해서 다음 명령어를 쳐보세요:
    /locate structure mymod:farmer_house
  4. 만약 좌표가 뜬다면 성공입니다! 그 좌표로 텔레포트(tp) 해보세요.
  5. 야생 한가운데에 내가 지은 집과 밭, 그리고 농부가 서 있을 것입니다.

 

 

 

 

 

 

 

 

 

  • JSON 파일명 (farmer_house_pool.json): 이건 이 '목록(Pool)'의 이름입니다. (예: "농부네 집 세트 목록")
  • JSON 내부의 "location" (mymod:farmerhouse): 이게 진짜 설계도(.nbt) 파일의 이름입니다. (예: "이 목록 안에는 'farmerhouse'라는 설계도를 써라")

 

 

 

 

 

 

mymod:는 파일 이름이 아니라 "폴더 주소"입니다.

마인크래프트는 파일 이름을 찾을 때 네임스페이스(폴더명) : 파일명 형식을 사용합니다.

  • 실제 파일 경로: src/main/resources/data/ mymod /structures/ farmerhouse.nbt
  • 코드 상의 주소: mymod : farmerhouse

즉, mymod: 부분은 "그 파일이 data/mymod/ 폴더 안에 들어있다"는 것을 알려주는 암호 같은 것입니다. 만약 파일 이름 자체를 mymod:farmerhouse.nbt라고 지으면 윈도우에서 인식하지 못해 오류가 납니다.

 

 

 

 

 

 

 

 

 

 

java.lang.IllegalStateException: Unbound values in registry ... [mymod:farmer_house]

이 말은 **"너가 structure_set에서 mymod:farmer_house를 쓰겠다고 등록해놨는데, 정작 그 farmer_house가 어디 있는지(설정 파일) 못 찾겠다!"**는 뜻입니다.

 

 

 

 

 

 

  • 잘못된 경로 (1.19+ 방식): data/mymod/worldgen/structure/
  • 올바른 경로 (1.18.2 방식): data/mymod/worldgen/configured_structure_feature/

 

 

이번 에러 메시지인 **No key config**는 마인크래프트 1.18.2 버전에서 가장 까다로운 부분 중 하나를 건드렸기 때문에 발생했습니다.

원인: 1.18.2 버전의 configured_structure_feature JSON 파일은 모든 내용을 한곳에 적으면 안 되고, 핵심 설정값을 반드시 config라는 괄호 안에 한 번 더 감싸서 적어줘야 합니다. 현재 사용자님의 코드는 1.19 이상의 최신 방식(평면 구조)으로 되어 있어서 마인크래프트가 "설정(config) 묶음이 어디 있느냐"라고 묻는 것입니다.

 

 

 

 

 

 

 

 

 

/locate structure mymod:farmer_house<이거 복붙안되나 마크 채팅창

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

근데 경작이 안된 상태로 집이 만들어지네 

 

 

 

 

 

왜 경작지가 흙으로 변했을까요? (이론적 이유)

마인크래프트의 경작지 블록은 아주 예민합니다. 설계도에서 경작지로 저장했더라도, 새로 생성될 때 다음 조건이 맞지 않으면 순식간에 흙으로 되돌아갑니다.

  • 수분 부족: 주변 4칸 이내에 **물(Water)**이 없으면 경작지는 곧바로 흙으로 변합니다.
  • 업데이트 체크: 구조물이 설치되는 순간 마인크래프트는 "이 블록이 여기 있어도 되나?"를 체크합니다. 이때 물이 감지되지 않으면 즉시 흙이 됩니다.

 

 

 

/locate mymod:farmer_house

 

 

 

 

 

y좌표를 더 아래까지 해서 물까지 포함되게 하니깐, 경작지가 되어버린 모습

 

 

 

 

 

 

설계도(.nbt)와 코드(Java)의 역할 차이

  • 설계도(.nbt): "여기에 mymod:custom_farmer라는 이름의 주민을 소환해라"라는 명령만 저장하고 있습니다.
  • 코드(.java): 그 주민이 소환된 후 **"어떻게 움직일지(AI)"**를 결정합니다.

즉, 설계도는 **"소환 버튼"**일 뿐이고, 그 버튼을 눌렀을 때 나타나는 농부가 어떤 지능을 가질지는 항상 최신 자바 코드를 따라갑니다.

 

 

 

 

 

다시 수정(저장)해야 하는 경우 (YES)

만약 아래와 같은 변화가 있다면 설계도를 다시 만들어서 프로젝트에 넣어야 합니다.

  1. 농부의 수나 위치를 바꿀 때: "한 집에 두 명이었는데 세 명으로 늘리고 싶다"거나 "농부 위치를 거실에서 마당으로 옮기고 싶다"면 다시 저장해야 합니다.
  2. 엔티티의 이름(Registry ID)이 바뀔 때: 코드에서 custom_farmer라는 이름을 super_farmer로 바꿨다면, 설계도는 예전 이름인 custom_farmer를 찾으려고 하다가 실패하게 됩니다.
  3. 초기 아이템을 바꿔줄 때: "농부가 태어날 때부터 손에 괭이를 들고 있게 하고 싶다"면, 게임 내에서 괭이를 들려준 상태로 다시 저장해야 합니다.

 

 

3. 다시 수정할 필요 없는 경우 (NO)

대부분의 업데이트는 여기에 해당합니다. 그냥 코드만 고치고 다시 실행하면 됩니다.

  • AI 로직 수정: 농사를 더 빨리 짓게 하거나, 집으로 돌아오는 거리를 조절하는 등 registerGoals 내부를 고치는 경우.
  • 능력치 수정: 체력을 20에서 40으로 늘리거나 이동 속도를 바꾸는 경우.
  • 외형 수정: 렌더러(Renderer) 코드를 고쳐서 농부의 옷 색깔을 바꾸는 경우.

 

 

 



 

 

 

문 열기 로직 설명 (이론)

  1. nav.setCanOpenDoors(true):
    • 이 코드가 없으면 농부는 문을 "벽"으로 인식합니다. 즉, 문 너머에 침대가 있어도 길을 찾지 못합니다.
    • 이 설정을 켜줘야 문을 "통과 가능한 장애물"로 보고 경로를 계산합니다.
  2. new OpenDoorGoal(this, true):
    • 농부가 경로상에 문이 있다는 것을 발견하면, 문 앞에서 멈추는 대신 문을 여는 동작을 실행합니다.
    • 두 번째 인자인 **true**는 **"문을 통과한 후 다시 닫아라"**라는 뜻입니다. (야생 생존 컨셉에서 좀비가 들어오지 못하게 문을 닫는 아주 중요한 매너입니다!)
  3. 우선순위 (1번):
    • 문 열기는 매우 긴급한 행동입니다. 농사를 짓거나 산책하는 것보다 **"일단 길을 막고 있는 문을 여는 것"**이 먼저여야 하므로 우선순위를 높게 설정했습니다.

 

 

 

 

 

 

 

오 문도 잘열고 닫네

문소리도 들림

 

 

 

 

 

 

 

 

 

 

 

 

 

 

1. "수많은 상자 중 어떤 게 내 상자인가?" (상자 식별 전략)

야생에 버려진 상자나 플레이어의 상자와 섞이지 않게 하는 가장 영리한 방법은 **'스폰 시점의 거리'**를 이용하는 것입니다.

  • 최초 스캔: 농부가 NBT 설계도에 의해 집 안에서 처음 태어날 때, 주변 5~10칸 이내를 딱 한 번 훑습니다.
  • 고향 상자(HomeChest) 등록: 이때 발견된 가장 가까운 상자 하나를 자신의 **'전용 창고'**로 머릿속에 저장합니다.
  • 고정 주소: 한 번 등록된 상자 주소는 농부가 죽기 전까지 변하지 않습니다. 이 방식이면 농부가 멀리 농사를 지으러 나갔다가 상자를 발견해도 "이건 내 거 아냐"라며 무시하고, 자기가 처음 등록한 집 안의 상자로만 돌아오게 됩니다.

2. 농부의 하루 일과 (아이템 흐름)

상자를 중심으로 농부의 행동은 3가지 상태를 순환하게 됩니다.

  1. 작업 상태 (Working):
    • 인벤토리에 씨앗이 있다면 주변을 돌며 심고 수확합니다.
    • 씨앗 소모: 심을 때마다 인벤토리에서 씨앗 1개를 제거하고 로그를 출력합니다. ([로그] 씨앗 소모! 남은 개수: 5)
    • 수확: 수확한 밀과 씨앗은 농부의 가방(인벤토리)에 담깁니다.
  2. 보급/정리 상태 (Restock/Deposit):
    • 복귀 조건: 씨앗이 0개가 되거나, 가방에 밀이 너무 많아지면(예: 20개 이상) 농사는 중단하고 '전용 창고' 좌표로 걸어갑니다.
    • 이동할 때 MoveBackToHomeGoal 로직을 사용하여 자연스럽게 집으로 돌아옵니다.
  3. 상자 상호작용 (Interaction):
    • 상자에 도착하면 팔을 휘두르며 상자를 여는 모션을 취합니다.
    • 넣기: 가방에 든 '밀'을 전부 상자로 옮깁니다.
    • 빼기: 상자 안에 '씨앗'이 있다면 일정량(예: 1세트)만큼 자기 가방으로 옮깁니다.

3. NBT 설계도에 상자 포함하기

따로 코드로 상자를 소환할 필요 없이, 모델하우스(설계도)를 만들 때 미리 상자를 배치해두면 됩니다.

  • 장점: 설계도가 야생에 생성될 때 상자도 블록으로서 그 자리에 생깁니다.
  • 자동 연결: 농부가 그 집 안에서 태어나자마자 바로 옆에 있는 상자를 보고 "아, 이게 내 상자구나!"라고 바로 연결할 수 있어 매우 자연스럽습니다.

4. 성능과 안정성

  • 성능: 매 틱 상자를 찾는 게 아니라, 태어날 때 딱 한 번 찾아서 좌표만 기억하는 것이므로 성능 부하가 거의 없습니다.
  • 안정성: 만약 상자가 파괴된다면? 농부는 "상자가 없네?"라고 판단하고 다시 야생 농부 모드로 돌아가거나, 주변의 새로운 상자를 찾도록 예외 처리를 할 수 있습니다.

5. 요약된 구현 방향

  1. NBT: 집 안에 상자를 하나 배치하고 농부 2명과 함께 저장한다.
  2. 농부 코드: 태어날 때 주변 상자 위치를 targetChestPos로 저장한다.
  3. 심기 로직: 인벤토리 아이템을 체크해서 씨앗이 있을 때만 심고 숫자를 뺀다.
  4. 복귀 로직: 씨앗이 없으면 targetChestPos로 돌아가 아이템을 주고받는다.

 

 

 

 

 

 

 

 

1. 상자가 파괴되었을 때의 예외 처리 (Chest Validation

농부가 상자로 돌아가려고 할 때마다 **"거기에 진짜 상자가 아직 있나?"**를 확인하는 절차를 넣습니다.

  • 확인 루틴: 상자 좌표로 이동하기 직전, 해당 좌표의 블록 상태(BlockState)를 체크합니다.
  • 판단 1 (상자가 있음): 평소대로 가서 아이템을 넣고 뺍니다.
  • 판단 2 (상자가 없음/파괴됨):
    1. 머릿속에 저장된 targetChestPos를 비웁니다(null).
    2. 재탐색: 주변 10칸 이내에 플레이어가 새로 놓아준 다른 상자가 있는지 훑습니다.
    3. 최종 실패 (야생 모드): 주변에 상자가 하나도 없다면, 농부는 "창고가 없는 야생 농부" 상태로 즉시 전환됩니다. 이때부터는 수확한 밀을 창고에 넣지 못하고 자기 인벤토리에 계속 쌓아두게 됩니다.

2. 주변에 경작지가 없을 때의 예외 처리 (No Work)

이건 마인크래프트 AI의 '우선순위(Goal Selector)' 시스템이 자동으로 해결해 줍니다.

  • 로직: 농사 AI(FarmerHarvestGoal)는 매번 "주변에 경작지가 있나?"를 묻습니다.
  • 상황: 만약 밭이 다 망가졌거나 주변에 경작지가 하나도 없다면, 농사 AI는 **"할 수 있는 일이 없음"**이라고 판단하고 작동을 멈춥니다.
  • 자연스러운 전환: 농사 AI가 멈추면, 그다음 순위인 **'배회(Wandering)'**나 '집 근처 머물기' AI가 실행됩니다.
    • 플레이어 눈에는 농부가 "할 일이 없어서 집 마당에서 서성거리거나 동료랑 수다 떠는" 모습으로 보이게 됩니다.

3. 농부의 "멘탈 관리" (종합 행동 순위)

예외 상황을 포함하여 농부가 어떤 순서로 생각할지 정해두면 안정성이 높아집니다.

  1. [1순위] 살고 보자 (Panic): 공격받으면 무조건 도망간다.
  2. [2순위] 집은 지키자 (HomePos): 태어난 곳(집)에서 20칸 이상 멀어졌으면 무조건 돌아온다. (이게 있어야 상자가 없어도 안 도망갑니다.)
  3. [3순위] 보급이 우선 (Chest): 상자가 있고, 가방이 꽉 찼거나 씨앗이 없으면 상자로 간다. (단, 상자가 부서졌으면 이 단계를 건너뛴다.)
  4. [4순위] 일하자 (Farm): 낮이고 주변에 밭이 있으면 일을 한다.
  5. [5순위] 쉬자 (Wander): 위 상황이 모두 아니면(상자도 없고 밭도 없으면) 그냥 집 근처를 산책한다.

4. 야생 생존 농부의 최종 모습

이렇게 예외 처리를 하면 농부는 다음과 같이 행동합니다.

  • 밭과 상자가 있는 평화로운 상태: 집-상자-밭을 오가며 무한 농사를 짓습니다.
  • 상자가 부서진 상태: 수확한 밀을 가방에 꽉 채울 때까지는 농사를 짓다가, 가방이 꽉 차면 더 이상 수확하지 못하고 밭 근처에서 서성거립니다. (플레이어가 상자를 다시 놓아주길 기다리는 것처럼 보입니다.)
  • 밭까지 다 망가진 상태: 그냥 자신의 고향(homePos)인 오두막 근처를 배회하며 '야생인'처럼 살아갑니다.

 

 

 

 

 

1. 핵심 논리: 임계값(Threshold) 설정

농부에게 **"심리적 마지노선"**을 정해줍니다.

  • 설계: 가방 안의 씨앗 개수를 실시간으로 체크합니다.
  • 작동:
    • 씨앗이 10개보다 많으면: 상자는 잊어버리고 오직 농사(수확/심기)에만 집중합니다.
    • 씨앗이 10개 이하가 되는 순간: "아, 씨앗이 떨어져 가네. 상자에 다녀와야겠다"라고 판단하고 보급 모드로 전환합니다.
  • 장점: 상자와 밭 사이를 불필요하게 왕복하지 않아 농사 효율이 극대화됩니다.

2. "보급 모드"에서의 똑똑한 행동 (상호작용)

상자에 도착했을 때, 단순히 씨앗만 가져오는 게 아니라 **'정리'**도 같이 하게 만듭니다.

  1. 비우기 (Deposit): 가방에 가득 찬 '밀'을 상자에 다 털어넣습니다. (가방 공간 확보)
  2. 채우기 (Withdraw): 상자 안에 씨앗이 있다면, 내 가방의 씨앗이 **64개(한 세트)**가 될 때까지 보충합니다.
  3. 복귀: 이제 다시 씨앗이 든든해졌으므로(> 10), 즉시 밭으로 돌아가 농사를 재개합니다.

3. 상자가 없는 "진짜 야생"에서의 예외 처리

만약 상자가 파괴되었거나 애초에 없다면 농부는 어떻게 될까요?

  • 상황: 씨앗이 10개 이하가 되어 상자로 가려고 했는데, 상자가 없습니다.
  • 판단: "보급처가 사라졌군. 이제부터는 내가 직접 자원을 구해야 한다."
  • 행동 변화 (수집가 모드):
    • 더 이상 상자로 가려고 시도하지 않습니다.
    • 씨앗이 아예 0개가 되면 **'심기'**를 멈춥니다.
    • 대신 **'수확'**은 계속합니다. 왜냐하면 밀을 수확하면 씨앗이 바닥에 떨어지기 때문입니다.
    • 바닥에 떨어진 씨앗을 주워서 다시 10개 이상이 되면, 누가 시키지 않아도 다시 **'심기'**를 시작합니다.
    • 이것이 바로 진정한 자업자득(자급자족) 생존입니다.

4. 로그로 확인하는 농부의 상태

사용자님이 요청하신 로그를 이런 식으로 출력하게 설계할 수 있습니다.

  • 심을 때: [작업] 씨앗 1개 소모 (가방에 45개 남음)
  • 임계값 도달: [경고] 씨앗 부족(10개 이하)! 상자로 보급하러 이동합니다.
  • 보급 완료: [보급] 상자에서 씨앗 보충 완료. 현재 64개.
  • 야생 모드 전환: [예외] 등록된 상자를 찾을 수 없음. 직접 수급 모드로 전환합니다.

 

 

 

 

 

1. 아이템 습득: "엔진의 기능을 빌려 쓰기" (canPickUpLoot)

우리가 직접 "주변 아이템을 찾아라!"라는 복잡한 검색 알고리즘을 짠 것이 아닙니다.

  • 원리: 마인크래프트 엔진 내부에는 이미 **'주변에 떨어진 아이템을 감지하는 시스템'**이 내장되어 있습니다. 우리는 단지 농부에게 **"너도 그 시스템을 사용할 권한을 줄게"**라고 스위치(canPickUpLoot)만 켠 것입니다.
  • 성능 이점: 엔진은 이미 월드에 있는 아이템 리스트를 관리하고 있습니다. 농부가 따로 블록을 뒤지는 게 아니라, 엔진이 아이템 리스트를 훑을 때 농부의 위치와 가까운 아이템이 있으면 **"어, 여기 너가 주울 수 있는 거 있다!"**라고 신호를 보내주는 방식입니다. 즉, 농부 한 명 추가된다고 연산량이 폭발적으로 늘어나지 않습니다.

2. 씨앗 체크: "주머니 뒤지기" (isValidTarget)

농부가 밭을 찾기 전에 씨앗이 있는지 확인하는 로직입니다.

  • 원리: 농부가 16칸 반경을 훑을 때, "여기에 심어야지"라고 결정하기 직전에 자신의 가방(27칸)에 씨앗이 있는지 한 번 슥 보는 것입니다.
  • 성능 이점: 수만 개의 블록을 뒤지는 게 아니라, 자기 인벤토리(고작 27개 슬롯)만 확인하는 것입니다. 컴퓨터 입장에서 27개의 숫자를 읽는 것은 0.000001초도 안 걸리는 아주 가벼운 작업입니다.
  • 필터링 효과: 오히려 씨앗이 없을 때는 "빈 땅"이라는 목표를 아예 무시해버리기 때문에, 길찾기 연산(Pathfinding)을 시작조차 하지 않습니다. 덕분에 불필요한 이동 계산을 줄여주는 성능 최적화 효과도 있습니다.

3. 씨앗 소모: "단순한 숫자 빼기" (consumeSeed)

  • 원리: 농부가 밭에 도착해서 실제로 심는 순간, 가방에서 씨앗 뭉치 하나를 찾아 숫자를 -1 해주는 것입니다.
  • 성능 이점: 이건 탐색도 알고리즘도 아닙니다. 그냥 엑셀 시트에서 숫자 하나 고치는 수준의 아주 단순한 연산입니다.

 

 

 

 

 

 

1. 성능 부하 원인 분석 (내림차순)

[1순위] 블록 탐색 알고리즘 (Block Scanning)

  • 이유: 가장 큰 부하 주범입니다. MoveToBlockGoal은 설정된 범위(16x16x9) 안의 모든 블록 좌표(약 2,300개 이상)를 하나하나 열어서 상태를 확인합니다. 농부가 걷고 있거나 할 일이 없을 때 이 스캔을 반복하는데, 여러 명의 농부가 동시에 스캔을 돌리면 서버 연산량이 기하급수적으로 늘어납니다.
  • 특이점: 특히 '유효한 타겟'이 없을 때, "어디 없나?" 하며 계속해서 전 범위를 훑는 과정이 가장 무겁습니다.

[2순위] 길찾기 계산 (Pathfinding)

  • 이유: 목표 블록을 찾은 뒤, 그곳까지 가는 경로를 계산하는 작업입니다. 단순한 직선거리가 아니라 장애물, 문, 고저차를 고려해야 하므로 복잡한 수학 연산이 필요합니다.
  • 부하 상황: 목표가 너무 멀거나, 경로가 복잡하게 막혀 있을 때 "갈 수 있는 길"을 찾으려고 반복 계산(Retry)을 시도하며 CPU를 많이 사용합니다.

[3순위] AI 목표 결정 주기 (Goal Tick)

  • 이유: "지금 농사를 지을까? 잠을 잘까? 집으로 갈까?"를 결정하는 판단 로직입니다. 현재는 매 틱(0.05초)마다 이 우선순위를 검토하도록 설계되어 있습니다.
  • 부하 상황: 판단 자체는 가볍지만, 그 판단의 결과로 '블록 스캔'이나 '길찾기'가 호출되기 때문에 빈도가 높을수록 부하가 누적됩니다.

[4순위] 아이템 엔티티 감지 (Item Pickup)

  • 이유: 주변에 떨어진 씨앗이나 밀 아이템이 있는지 리스트를 훑는 작업입니다.
  • 부하 상황: 블록 탐색보다는 훨씬 가볍지만, 주변에 드롭된 아이템이 수백 개씩 쌓여 있는 상황에서는 체크해야 할 리스트가 길어져 부하가 발생할 수 있습니다.

2. 더 나은 성능을 위한 개선 아이디어 (이론적 설명)

① "게으른 탐색" 도입 (Search Cooldown)

  • 설명: 매 틱마다 주변을 훑지 않게 만드는 것입니다.
  • 방법: 농부가 목표를 찾지 못했을 때, 즉시 다시 찾는 게 아니라 1~2초 동안 "휴식 상태"를 강제로 부여합니다.
  • 효과: 블록 스캔 횟수를 1/20 이하로 줄일 수 있어 가장 확실한 성능 최적화가 가능합니다.

② "단계별 탐색" (Tiered Scanning)

  • 설명: 처음부터 16칸 전체를 다 뒤지지 않는 전략입니다.
  • 방법: 먼저 자기 발밑 4칸을 보고, 없으면 8칸, 그래도 없으면 16칸으로 범위를 넓혀가며 찾습니다.
  • 효과: 가까운 곳에 일감이 있을 경우 전체 범위를 스캔할 필요가 없어 평균 연산량이 대폭 감소합니다.

③ "블랙리스트" 기억 (Failed Target Memory)

  • 설명: 갈 수 없는 곳에 대한 정보를 짧게 기억하게 합니다.
  • 방법: 어떤 블록을 목표로 찍고 길찾기를 시도했는데 실패했다면, 그 좌표를 10초 동안 "무시 목록"에 넣습니다.
  • 효과: 막혀 있는 경로에 대해 반복적으로 무의미한 길찾기 연산을 수행하는 것을 방지합니다.

④ "상태 기반 활성화" (Conditional Trigger)

  • 설명: 특정 조건이 맞을 때만 AI를 깨우는 것입니다.
  • 방법: 예를 들어 인벤토리에 씨앗이 없고, 주변에 다 자란 작물도 감지되지 않는다면 아예 '농사 스캔' 로직 자체를 잠시 꺼버리고 '배회' AI만 작동하게 합니다.
  • 효과: 불필요한 상황에서 무거운 스캔 로직이 돌아가는 것을 원천 봉쇄합니다.

⑤ "중심점 기준 탐색" (Anchor-based Search)

  • 설명: 농부의 현재 위치가 아닌, '집'이나 '상자'를 기준으로 한 번만 스캔 정보를 생성하는 것입니다.
  • 방법: 농부가 움직일 때마다 새로 스캔하는 게 아니라, 마을의 고정된 구역 정보를 공유하거나 캐싱(저장)해서 돌려씁니다.
  • 효과: 중복된 블록 체크를 막아 서버 전체의 최적화에 기여합니다.

 

 

 

 

 

 

 

뭐야 근데 갑자기 농사일을 안하는데 

 

일단 지금 인벤토리에 씨앗이 없나 ?

 

 

 

[  ]

근데 씨앗이 없으면 상자에서 씨앗 꺼내도록 되어잇나 

 

 

 

 

씨앗 주울수는 잇는데 

인벤토리랑 갱신이 안되나 

 

 

[  ]

농부가 아이템을 주웠는데 인벤토리가 비어있다고 뜨는 이유는, 마인크래프트 기본 주민 시스템이 우리가 만든 customInventory가 아닌, 자기들의 기본 인벤토리(8칸짜리)에 아이템을 집어넣고 있기 때문일 확률이 매우 높습니다.