ENFRKOantony langlois
작업

The Creepy Walker

방문기타JavaMinecraftForge

마인크래프트 공포 몹 · 어떤 공간에서든 서고 살금살금 걷고 기어서 당신을 쫓아오는 지형을 인식하는 괴물 · 월드당 하나만 존재하며 세이브 파일을 통해 강제됩니다 · Java · Forge

아키텍처

Forge client
Client · 렌더링
엔티티 렌더러
entity/client · MobRenderer
매 프레임 walker를 그립니다; 죽음 동안 몸 회전을 건너뛰어 시체가 쓰러진 자리에 정확히 남게 합니다
Blockbench
Client · 지오메트리
계층형 모델
entity/client · 18 bones
Blockbench에서 내보낸 스켈레톤; 머리의 yaw와 pitch가 제한되어 시선이 계속 플레이어를 추적합니다
Java keyframes
Client · 애니메이션
키프레임 클립
entity/animations
클립 14개: 걷기, 살금 걷기, 기기, 대기, 공격, 스폰, 죽음, 자세 전환
server tick
Core · 서버 두뇌
TheCreepyWalkerEntity
entity/custom · Monster subclass
매 틱 위와 앞의 블록을 탐지하고 자세를 선택합니다: stand, sneak 또는 crawl
자세 상태 기계히트박스 크기 조정근접 공격 목표피해 규칙스폰 검사
Minecraft
External · 게임 런타임
Forge + 바닐라 엔진
모드가 후킹하는 이벤트 버스, AI 목표 스케줄러, 자연 스폰 엔진
Forge events
Server · 스폰 게이트
스폰 게이트
event · SpawnPlacements
canSpawnHere: 지하, 단단한 땅 위, 위에 하늘이 없고, 살아 있는 다른 walker가 없을 것
SavedData
Storage · 월드 세이브
싱글턴 월드 데이터
worlddata · NBT
월드 세이브에 영속되는 불리언 exists 하나; 재시작과 청크 언로드에도 살아남습니다
JSON
Storage · 데이터팩
Biome modifier
data/forge/biome_modifier
모든 오버월드 바이옴의 스폰 풀에 가중치 100으로 walker를 추가합니다

바닐라 스폰 엔진biome modifier 스폰 풀에서 walker를 선택하고 스폰 게이트에 질의하며, 그 게이트의 canSpawnHere싱글턴 월드 데이터를 읽습니다: 세이브 안에 이미 walker가 어딘가 존재하면 스폰은 거부됩니다. 살아난 뒤에는 TheCreepyWalkerEntity가 서버 틱마다 지형을 탐지하고, 서기, 살금 걷기, 기기 사이를 전환하며, 히트박스 크기를 조정하고, 자세 플래그를 SynchedEntityData를 통해 클라이언트로 전송합니다. 엔티티 렌더러는 매 프레임 그 플래그를 읽고 계층형 모델은 일치하는 키프레임 클립을 샘플링하며, 여기에는 기기에서 서기로의 전환을 위한 2단계 안무도 포함됩니다.

배포: Mod jar → Forge 클라이언트 + 서버 · 스폰 규칙 → 데이터팩 JSON, 코드 리로드 불필요

성과

  • 자세 3종 · 히트박스가 2.7에서 1.7에서 0.7 블록으로 줄어듭니다
  • 월드당 1개 · 세이브 파일을 통해 싱글턴 강제
  • 클립 14개 · 손으로 키프레임을 잡은 Blockbench 애니메이션
  • 패킷 0개 · 커스텀 넷코드 · 모든 동기화가 바닐라 엔티티 데이터에 실림

보여준 역량: 서버-클라이언트 상태 동기화 · 유한 상태 기계 · 히트박스 + 충돌 처리 · 이벤트 기반 등록 · NBT 월드 영속성 · 게임 AI 목표 설계

문제와 해결책

하나의 제약이 모든 것을 이끕니다: 살아 있는 것처럼 느껴져야 하고 어떤 공간에서든 플레이어를 따라갈 수 있어야 하는 단일 괴물.

█ 지형을 인식하는 자세 기계

문제: 키가 2.7블록인 괴물은 1블록짜리 좁은 통로로 플레이어를 쫓아갈 수 없습니다. 바닐라 몹은 낮은 입구를 그냥 우회하거나 입구에서 막혀버려서, 어디든 따라오는 존재라는 공포 전제를 무너뜨립니다.

서버 틱마다 엔티티는 자신의 위와 앞의 블록을 탐지하고(벽을 마주할 때는 한 칸 위로 오르는 것 포함) 자세를 선택합니다. 서기, 살금 걷기, 기기는 각각 고유한 히트박스를 가집니다: 키 2.7, 1.7, 0.7 블록으로, getDimensionsrefreshDimensions를 통해 적용됩니다.

  • 바닐라 Pose의 수영/웅크리기를 거부: 플레이어 형태의 히트박스만 다루고 전환 시점에 대한 제어를 주지 않습니다
  • 틱당 최대 8회의 블록 상태 읽기를 수용; 각 전환 후 20틱 쿨다운이 가장자리에서 자세가 요동치는 것을 막습니다
  • 플레이어가 갈 수 있는 곳이면 어디든 물리적으로 들어맞는 몹을 얻었으며, 이는 어떤 경로 탐색 조정만으로도 제공되지 않습니다

▚ 커스텀 패킷 없는 자세 동기화

문제: 자세는 서버에서 결정되지만 애니메이션은 클라이언트에만 존재합니다. 평범한 Java 필드는 멀티플레이에서 조용히 어긋납니다: 각 클라이언트가 자기 나름의 자세를 추측해 잘못된 클립을 재생하게 됩니다.

현재 자세, 이전 자세, 그리고 숫자형 자세 변경 코드가 모두 Minecraft의 복제 엔티티 상태 채널인 SynchedEntityData accessor에 담깁니다. 클라이언트는 변경 코드를 읽고 일치하는 전환 클립을 시작한 다음, 코드는 0으로 초기화됩니다.

  • 커스텀 네트워크 채널을 거부: 불리언과 int 하나에 들어가는 데이터에 대해 더 많은 코드, 버전 관리, 순서 문제가 생깁니다
  • 한 엔티티에 열 개의 동기화 accessor를 수용, 손수 만든 패킷보다 수다스럽지만 공짜로 복제되고 레이스 컨디션이 없습니다
  • 전환 중간에 렌더 거리 안으로 걸어 들어오는 플레이어를 포함해 모든 관찰자에게 올바른 애니메이션을 얻었습니다

▞ 2단계 전환 안무

문제: 기기에서 서기로 곧장 튀는 것은 순간이동처럼 보입니다. 전환 클립은 인접한 자세 사이에만 만들어져 있었기에, 기기에서 서기로의 애니메이션은 아예 없었습니다.

자세를 건너뛰면 두 개의 클립이 큐에 들어갑니다: 기기에서 살금 걷기가 먼저 재생되고, 대기 플래그가 후속 동작을 준비시키며, 첫 클립의 타임아웃이 0에 도달하는 틱에 살금 걷기에서 서기가 시작됩니다. 클라이언트의 틱 카운트 타임아웃이 전체 시퀀스를 구동합니다.

  • 단계 건너뛰기 전용 클립 제작을 거부: 드문 경우를 위해 구축하고 유지해야 할 Blockbench 애니메이션 두 개가 더 늘어납니다
  • 새 자세가 최고 속도로 움직이기 전까지 약 16틱의 전환 지연을 수용
  • 단 네 개의 제작된 전환 클립만으로 모든 자세 경로에서 매끄러운 움직임을 얻었습니다

▓ 월드당 walker 하나, 두 번 강제

문제: 스폰 엔진이 수십 개의 복제본을 만들어내면 공포 몹은 모든 위협감을 잃습니다. 바닐라 스폰 상한은 카테고리별, 청크별로 작동하므로 '월드 전체에서 영원히 정확히 하나'를 표현할 수 없습니다.

월드 세이브 안의 SavedData 불리언이 진실의 원천입니다: canSpawnHere는 그것이 설정되어 있는 동안 어떤 스폰도 거부하고, Forge의 join/leave 이벤트가 그것을 뒤집습니다. 모든 오버월드 바이옴에서 스폰 가중치 100은 허용된 단 하나의 스폰이 빠르게 일어나게 합니다.

  • 로드된 엔티티 계수를 거부: 언로드된 청크가 walker를 숨겨서 계수가 중복을 허용하게 됩니다
  • 차원별 장부 대신 월드 전체 플래그를 수용, walker 총 하나는 제약이 아니라 설계입니다
  • 플래그가 NBT로 영속되기 때문에 서버 재시작에도 살아남는 싱글턴 동작을 얻었습니다

▒ 틱이 아니라 애니메이션에 맞춘 근접 공격

문제: 바닐라 MeleeAttackGoal은 대상이 사거리에 들어오는 순간 피해를 적용하지만, 돌진 애니메이션은 대상에 닿기까지 여러 틱이 걸립니다. 플레이어는 아직 팔도 움직이지 않은 괴물에게 맞습니다.

커스텀 공격 목표는 준비 카운터를 돌립니다: 공격 플래그가 일찍 뒤집혀 클라이언트가 휘두르기를 시작하고, doHurtTarget는 카운터가 팔이 내려오는 프레임에 도달할 때만 발동합니다.

  • 바닐라 목표에 클라이언트 측 추측을 더하는 것을 거부: 지연 때문에 정확히 중요한 순간에 추측이 틀립니다
  • 읽히고 피할 수 있는 휘두르기를 대가로 더 느린 실효 공격 속도(12틱 쿨다운)를 수용
  • 세 자세 모두에서 애니메이션 프레임에 맞아 들어가는 타격을 얻었으며, 각 자세는 고유한 공격 클립을 가집니다

▧ 스크립트된 스폰과 죽음, 래그돌 없음

문제: 바닐라 죽음 시퀀스(빨간 섬광, 90도로 넘어가기, 즉시 소멸)는 죽어가는 생물이 아니라 게임 시스템처럼 읽히고, 표준 팝인 스폰은 첫 조우를 낭비합니다.

죽음은 렌더러가 바닐라 시체 회전을 억제하는 동안 키프레임 붕괴 클립을 시작합니다; 몸은 60틱 동안 남아 있고 그 사이 클릭할 수 없습니다. 스폰은 108틱짜리 출현 클립을 재생하며 그동안 서버 두뇌는 가만히 있습니다.

  • LivingEntity의 기본 죽음 처리를 거부: tickDeath와 setupRotations를 오버라이드하는 것이 자세를 유지하는 유일한 방법이었습니다
  • 출현이 결코 잘리지 않도록, 취약한 구간인 스폰 시 5초간 AI를 멈추는 것을 수용
  • 캐릭터를 유지하는 등장과 퇴장을 얻었으며, 이것이 공포 모드의 핵심입니다