메운디 아시려나
혹은 plant vs zombiesms?
모딩하다가, 이 마크로 이런 디펜스겜 구현할순없을까라는 생각이 들었고
이떄까지 만든 몹들을 하나씩, 고정된, 타워로 만들려고한다
처음은 궁수타워이다

(좀비가 벽을 공격하게 해야하는데)
그럴려면, 좀비 엔티티의 goal도 바꿔야할것같아서
1부: 최종 구현 원리 (그래서 어떻게 만들었는가?)
결론부터 말하면, 지금의 '궁수 타워'는 **독립적인 하나의 몹(Entity)**임.
겉보기엔 흙 두 칸 위에 주민이 서 있는 것처럼 보이지만, 사실 블록도, 일반 주민도 아님. 모든 기능이 이 하나의 엔티티 안에 담겨 있음.
- 핵심 설계: 모든 것은 하나의 엔티티
- 처음엔 '설치용 블록 + 제어용 데이터 + 궁수 몹'이라는 복잡한 구조를 생각했으나, 수많은 문제 끝에 포기함.
- 최종적으로는 '궁수 타워' 자체를 하나의 엔티티로 만들어, 스폰 알로 소환하는 방식을 채택함. 이것이 모든 문제를 해결한 핵심 열쇠였음.
- 기본 뼈대: PathfinderMob 상속
- Villager(주민)를 상속하지 않았음. PathfinderMob이라는, AI를 가진 몹의 가장 기본적인 클래스를 상속받음.
- 이유: 주민 클래스는 너무 똑똑해서 우리가 원치 않는 행동(직업 찾기, 잠자기, 돌아다니기)을 하는 '뇌(Brain)' 시스템이 내장되어 있음. 우리는 완벽한 통제가 필요했기에, 백지상태인 PathfinderMob에서 시작함.
- 겉모습: 렌더러 빌려오기
- 뼈대는 기본 몹이지만, 겉모습은 주민처럼 보여야 함.
- 이를 위해 엔티티의 렌더링(화면에 그려주는 작업)만 바닐라의 VillagerRenderer를 사용하도록 지정함.
- 결과적으로 우리 엔티티는 주민의 모델과 텍스처를 '빌려와서' 화면에 표시됨. 로직과 겉모습을 분리한 것임.
- AI 설계 (행동 방식)
- 제자리 고정: 움직임, 넉백, 다른 몹에 의해 밀리는 모든 물리 효과를 코드 레벨에서 0으로 만들거나 무시하도록 하여 완벽한 '고정 포대'로 만듦.
- 적 탐지: NearestAttackableTargetGoal이라는 AI 목표를 사용해, 주변의 모든 Monster(좀비, 스켈레톤 등)를 적으로 인식함.
- 공격: RangedAttackGoal이라는 AI 목표를 사용해, 탐지한 적에게 활을 쏨.
- 레벨업과 연사
- 데이터 저장: 엔티티 내부에 SynchedEntityData라는 기본 시스템을 사용해 레벨과 킬 수를 저장함. 이 시스템은 서버에서 값이 바뀌면 자동으로 클라이언트에 알려주는 매우 편리한 기능임.
- 킬 카운트: LivingDeathEvent라는 '몹 사망 사건'을 감지함. 몹이 죽었을 때, 공격자가 우리 '아처 가디언'이라면 가디언의 notifyKill() 메서드를 호출하여 킬 수를 1 올림.
- 레벨업 조건: 킬 수가 현재 레벨과 같아지면 레벨업함. (Lv.1 -> 1킬, Lv.2 -> 2킬...)
- 연사: 활을 쏘는 로직 안에 for 반복문을 넣어, '현재 레벨'만큼 화살을 발사함. 이때 두 번째 화살부터는 약간의 부정확도를 줘서 자연스럽게 퍼져나가도록 함.
- 좀비가 타워를 공격하게 만들기
- 가장 까다로운 문제였음. 바닐라 좀비는 PathfinderMob을 공격하지 않음.
- EntityJoinWorldEvent라는 '몹 스폰 사건'을 감지함.
- 세상에 좀비가 스폰될 때마다, 그 좀비의 AI 목록에 "저기 있는 ArcherGuardianEntity도 공격해!" 라는 새로운 공격 목표를 강제로 주입해 줌.
- 덕분에 우리 타워는 좀비에게 매력적인 공격 대상이 됨.
- HUD (체력바와 레벨 표시)
- 엔티티 렌더러(ArcherGuardianRenderer)가 매 프레임마다 엔티티의 데이터를 읽어 옴.
- 위에서 설명한 SynchedEntityData 덕분에, 서버에서 변경된 체력, 레벨, 킬 수가 클라이언트로 자동 동기화됨.
- 렌더러는 이 최신 정보를 바탕으로 엔티티의 머리 위에 텍스트와 체력바를 그려줌.
2부: 좌충우돌 개발 일지 (오류 해결의 역사)
이 간단한 타워 하나를 만드는 데 상상 이상의 오류들이 있었음. 그야말로 총체적 난국이었음.

- 초기 설계의 함정: "블록으로 만들자!"
- 처음엔 "흙 블록 2칸을 쌓고 그 위에 주민을 두면 타워지!" 라고 단순하게 생각함.
- 하지만 이 구조는 최악이었음. 흙 블록의 체력과 주민의 체력을 어떻게 합칠지, 좀비는 왜 흙을 안 때리고 주민만 때리는지 등 문제가 산더미였음.
- 결정적으로 사용자(나 자신)가 "아 그냥, 흙 2칸 없애면 되는거 아닌가?" 라는 깨달음을 얻고, 복잡한 블록 구조를 버리고 '하나의 엔티티'로 만드는 현재의 설계로 전환하게 됨.
- 오류 1: 주민 상속의 배신
- "좀비가 타워를 공격하게 하려면, 그냥 주민을 상속받으면 되잖아?" 라는 생각으로 Villager를 상속함.
- 결과: 좀비가 공격은 하지만, 가디언이 주민의 '뇌(Brain)' AI 때문에 제자리에 있지 않고 마음대로 돌아다니기 시작함. 흙 블록에서 내려와 좀비와 육탄전을 벌이는 대참사가 발생.
- 해결: PathfinderMob을 상속하여 AI를 완벽하게 통제하는 것으로 방향을 바꿈.
- 오류 2: 보라색과 검은색의 저주
- 블록 아이템이 보라색/검은색 체커보드 모양으로 깨져서 나옴.
- 원인: 게임이 아이템의 텍스처를 어디서 찾아야 할지 모를 때 나오는 대표적인 오류. 리소스 파일(.json)의 경로, 이름, 내용 중 하나가 잘못된 것.
- 해결: models/item/ 폴더에 스폰 알의 모델을 정의하는 .json 파일을 추가하고, lang/en_us.json에 아이템 이름을 등록하여 해결함.
- 오류 3: "게임 창이 안 떠요!" (runClient vs runServer)
- 서버 실행용인 runServer를 켜놓고, 게임 화면이 뜨기를 하염없이 기다림.
- 원인: runServer는 원래 게임 창 없이 콘솔 로그만 띄우는 게 정상. 게임 화면을 보려면 runClient를 실행해야 함.
- 해결: IDE의 실행 구성을 runClient로 바꾸고 나서야 익숙한 마인크래프트 화면을 볼 수 있었음.
- 오류 4: 불친절한 EULA
- runServer 실행 시, "EULA에 동의해야 한다"는 메시지만 남기고 서버가 꺼짐.
- 원인: 마인크래프트 서버는 최초 실행 시 run 폴더에 eula.txt를 생성하고, 사용자가 직접 이 파일의 eula=false를 true로 바꿔주길 기다림.
- 해결: 프로젝트 폴더의 run/eula.txt 파일을 찾아 값을 true로 수정하여 해결.
- 오류 5: 보이지 않는 체력바, 오르지 않는 킬 수
- 가디언이 좀비를 죽여도 HUD의 킬 수가 전혀 오르지 않았음.
- 가설: 클라이언트-서버 데이터 동기화 문제라고 생각하고, 직접 네트워크 패킷(Clientbound...Packet)을 만들어야 하나 고민함.
- 진짜 원인: 서버에서 킬 카운트 자체가 안 되고 있었음. LivingDeathEvent에서 공격자를 찾는 로직에 결함이 있어, 가디언이 킬을 해도 시스템이 인지를 못 했던 것. 서버에서 데이터가 바뀌지 않으니 클라이언트로 보낼 업데이트도 없었고, 당연히 HUD도 갱신되지 않았음.
- 해결: DamageSource에서 공격자를 가져오는 방식을 source.getEntity()로 변경하여 서버 로직을 수정하자, SynchedEntityData 시스템이 알아서 동기화를 처리해주며 HUD 문제까지 한 번에 해결됨.
- 최종보스: 등록되지 않은 이벤트 핸들러
- 킬 카운트 로직을 완벽하게 수정하고 로그까지 심었는데도, 킬 수가 오르지 않고 로그도 전혀 찍히지 않음.
- 원인: TowerInteractionHandler라는 완벽한 설계도를 만들어놓고, 정작 Forge에게 "이 설계도 좀 봐주세요!" 라고 등록하는 것을 잊음.
- 해결: 모드 메인 클래스(MyMod.java)에 MinecraftForge.EVENT_BUS.register(new TowerInteractionHandler()); 단 한 줄을 추가. 이 코드는 Forge의 이벤트 시스템에 우리 핸들러를 등록하여, 게임 내 사건들을 감지할 수 있게 만드는 핵심적인 절차였음. 이 한 줄을 추가하자마자 모든 기능이 마법처럼 정상 작동하기 시작함.
'모딩 > 마인크래프트 모드 개발 일지' 카테고리의 다른 글
| 마크 모딩) 커스포지에 업로드하기전 사전준비들 (2) | 2025.12.20 |
|---|---|
| [마인크래프트 모딩]#20 전방에 충격파를 날리는 주민 (15) | 2025.07.06 |
| [마인크래프트 모딩]#18 오늘 산재해있던, 문제들에 대해서 (8) | 2025.07.02 |
| [마인크래프트 모딩]#17 나만의 소환사 주민 만들기 (9) | 2025.07.02 |
| [마인크래프트 모딩]#16 나만의 RPG 파티 만들기! (마법사 주민 편) (12) | 2025.07.01 |