
벌침쏘는 꿀벌
꿀벌 모델 제공자:

- 독 화살: 일반 화살 대신 맞으면 독(Poison) 효과를 주는 화살로 변경.
- 벌침 발사: 화살 모델 대신 벌침 모양의 커스텀 투사체를 쏘도록 변경.
이거, living entity는 아니지만,
디펜스겜 만들수잇게는데
근데 좀비는 living entity가 자기를 공격한것만 반격하나
[ ]
공격 애니메이션 실행되는지, 로그파일 남기게
공중공격 히트박스문제[ ]
꿀벌 엔티티 완성, 체력바, 레벨업 시스템 등은 더 해야겟지만
꿀벌이라 그런가 화살 쏘는거 나쁘지않은데
독침 엔티티 만들수도 잇을지도
[ ]제네틱관련 mymod등록부분 뭔차이인지 이해
[ ]이게 먼소리고, 뭐를 어떻게 해결해야하는거지
AnimationFactory(software.bernie.geckolib3.core.IAnimatable)'은(는) 더 이상 사용되지 않으며 제거용으로 표시되어 있습니다
GeckoLib 3.x의 최신 버전(특히 1.18.2~1.19.2 대응 버전)에서는 new AnimationFactory(this) 생성자가 Deprecated(더 이상 권장되지 않음) 처리되었습니다. 대신 **GeckoLibUtil.createFactory(this)**를 사용하는 것이 올바른 방법입니다.
addAnimation(java.lang.String, java.lang.Boolean)'은(는) 더 이상 사용되지 않으며 제거용으로 표시되어 있습니다
GeckoLib 3.x 버전에서는 triggerAnim이라는 메서드가 Entity 클래스나 IAnimatable 인터페이스에 기본적으로 포함되어 있지 않은 경우가 많습니다 (이 메서드는 GeckoLib 4.x나 특정 최신 빌드에서 지원됩니다).

이 오류는 왜뜨는거지

이제 import부분에서 오류는 안뜨는데
걍 intelij다시 키니깐 문제 해결됨
mymod에서 등록하는부분에서 제네틱관련 문제
걍 import문제 해결되니깐 제네틱 관련 문제도 해결되케


왜 모양이 바뀐거지
event.registerEntityRenderer(BeeGirlEntityTypeInit.BEE_GIRL.get(), BeeGirlRenderer::new);
event.registerEntityRenderer(BeeGirlEntityTypeInit.BEE_GIRL.get(), (context) -> {
// 반환 타입을 제네릭이 없는 EntityRenderer로 강제 캐스팅
return (net.minecraft.client.renderer.entity.EntityRenderer) new BeeGirlRenderer(context);
});
::new라는 표현이 생소해서 그럴 수 있습니다! 아주 간단하게 비유로 설명해 드릴게요.
이전 방식과 지금 방식은 결과적으로 100% 똑같은 일을 합니다. 다만 코드를 쓰는 **"말투"**가 달라진 것입니다.
1. 이전 방식 (복잡한 방식)
event.registerEntityRenderer(BeeGirlEntityTypeInit.BEE_GIRL.get(), (context) -> {
return (net.minecraft.client.renderer.entity.EntityRenderer) new BeeGirlRenderer(context);
});
- 말투: "야, 포지(Forge)! 벌 소녀 렌더러 등록할 건데, 내가 지금부터 메서드를 하나 직접 짜서 줄게. 이 메서드는 context라는 재료를 받아서, BeeGirlRenderer를 새로 만들고(new), 그걸 렌더러 타입으로 변신(Casting)시켜서 돌려주는 기능을 해!"
- 특징: 코드가 길고, (context) -> { ... } 같은 복잡한 문법이 들어갑니다.
2. 새로운 방식 (메서드 참조 - ::new)
event.registerEntityRenderer(BeeGirlEntityTypeInit.BEE_GIRL.get(), BeeGirlRenderer::new);
- 말투: "야, 포지! 벌 소녀 렌더러 등록할 건데, 그냥 **BeeGirlRenderer의 생성자(Constructor)**를 갖다 써!"
- 특징: 코드가 압도적으로 짧고 깔끔합니다. ::new는 "이 클래스의 생성자를 호출해라"라는 뜻입니다.
3. 왜 ::new를 써도 되나요?
마인크래프트 포지의 registerEntityRenderer 메서드는 두 번째 자리에 **"Context를 받아서 렌더러를 만들어주는 녀석"**을 원합니다.
우리가 만든 BeeGirlRenderer의 생성자를 다시 볼까요?
public BeeGirlRenderer(EntityRendererProvider.Context renderManager) {
super(renderManager, new BeeGirlModel());
}
보시다시피 Context를 받아서 자기 자신을 만들고 있죠?
포지가 원하는 조건("Context 줄게, 렌더러 내놔")과 BeeGirlRenderer의 생성자 모양이 딱 맞기 때문에, 자바가 "아, 굳이 복잡하게 람다식 안 쓰고 그냥 생성자 이름만 알려줘도 내가 알아서 연결해줄게!" 하고 편의를 봐주는 기능입니다. 이를 **메서드 참조(Method Reference)**라고 부릅니다.
요약하자면:
- 동작은 똑같다.
- BeeGirlRenderer::new는 **"BeeGirlRenderer의 생성자를 사용해서 렌더러를 만들어라"**는 뜻의 줄임말이다.
- 코드가 훨씬 읽기 편해지기 때문에 숙련된 개발자들이 선호하는 방식이다.
결론: 복잡하게 캐스팅((EntityRenderer))하거나 화살표(->)를 그릴 필요 없이, 클래스 이름 뒤에 ::new만 붙이면 자바가 알아서 똑똑하게 처리해 줍니다!
예전에도 이런 오류잇엇던것같은데
Note: Some input files use or override a deprecated API.
Note: Recompile with -Xlint:deprecation for details.
Note: C:\dev5\src\main\java\changmin\myMod\feature\custom_villager\CustomVillagerEntity.java uses unchecked or unsafe operations.
Note: Recompile with -Xlint:unchecked for details.


아 있네
57이 마지막 버전이 아니네
implementation fg.deobf('software.bernie.geckolib:geckolib-forge-1.18:3.0.59')
근데 이렇게 바꾸니깐, 또 import해올수없다고 뜨는데
걍 젤 최신껄로 해볼가

geckolib-forge-1.18
아 이거 forge랑 faric둘다잇네
뭔지 봐야겟네

가불기인줄알앗는데 아니엇고
추가 확인 사항 (mods.toml)
혹시 위 작업을 했는데도 비슷한 에러가 난다면, 모드 설정 파일에서 버전 제한을 걸어두었을 수 있습니다.
- 경로: src/main/resources/META-INF/mods.toml
- 이 파일을 열어서 [[dependencies.mymod]] 섹션 아래에 versionRange="[3.0.59,)" 같은 내용이 있는지 확인해 보세요. (이미 gradle에서 버전을 올렸다면 이 부분은 건드리지 않아도 정상 작동할 것입니다.)
mods.toml 파일 수정
프로젝트 내의 설정 파일에서 요구하는 GeckoLib의 버전을 3.0.57로 낮춰주어야 합니다.
- 파일 위치: src/main/resources/META-INF/mods.toml 파일을 엽니다
- 내용 수정: 파일 하단부의 의존성(dependencies) 섹션을 찾으세요
[[dependencies.mymod]] # 'mymod'는 사용자님의 모드 ID
modId="geckolib3"
mandatory=true
# 이 부분이 문제의 원인입니다! [3.0.59,) 라고 되어 있을 것입니다.
versionRange="[3.0.57,)" # [3.0.59,) 에서 [3.0.57,) 로 수정하세요.
ordering="BEFORE"
side="BOTH"
이 코드는 보통 모드 템플릿을 복사해서 붙여넣거나, 다른 버전(1.19 등)의 튜토리얼을 참고할 때 함께 딸려오는 경우가 많습니다.
- versionRange="[3.0.59,)"의 의미는 **"GeckoLib 버전이 3.0.59 이상이어야만 내 모드를 실행시켜 주겠다"**는 강한 제약 조건입니다.
- 하지만 앞서 확인했듯이 1.18.2 Forge용 GeckoLib은 3.0.57이 마지막이기 때문에, 스스로 통과할 수 없는 벽을 세워버린 셈이 된 것입니다.

java.lang.IllegalStateException: Duplicate DefaultAttributes entry: entity.mymod.bee_girl
이것은 BeeGirl의 속성(Attributes) 등록 코드가 두 군데 이상 중복되어 있다는 뜻입니다. 로그를 더 자세히 보면 어떤 클래스들이 충돌하고 있는지 나옵니다:
- changmin.myMod.feature.experiment.BeeGirl.BeeGirlEvents (3번 리스너)
- changmin.myMod.MyMod$ModEventBusEvents (23번 리스너)
즉, 제가 이전에 말씀드린 대로 MyMod.java에도 등록 코드를 넣었고, 기존에 있던 BeeGirlEvents.java에도 코드가 남아 있어서 충돌이 발생한 것입니다.
왜 이렇게 해야 하나요?
마인크래프트 포지 엔진은 한 엔티티(bee_girl)에 대해 "얘 체력은 얼마야?" 라는 질문을 단 한 번만 받길 원합니다. 그런데 현재 코드는:
- BeeGirlEvents 클래스가 "벌 소녀 체력은 20이야!"라고 말함.
- MyMod 클래스가 또 "벌 소녀 체력은 20이야!"라고 말함.
이 상황이 되면 포지는 "이미 누가 말했는데 왜 또 말해? 중복이야!" 라며 게임을 강제로 종료(Crash)시킵니다.
렇다면 에러의 원인인 onAttributeCreation은 어디에 있나요?
아까 보내주신 **에러 로그(Crash Report)**를 다시 보면 정확한 위치가 나옵니다. 로그의 26~30번 줄 근처를 보세요:
3: ASM: class changmin.myMod.feature.experiment.BeeGirl.BeeGirlEvents onAttributeCreation
즉, 이 메서드는 BeeGirlEntity 파일이 아니라 **BeeGirlEvents.java**라는 별개의 파일에 들어있습니다.
어떤 클래스의 어떤 메소드인지, 로그가 알려주네
Non [a-z0-9/._-] character in path of location: mymod:animations/ModelBeegirl.animation.json
마인크래프트 1.13 버전 이후부터는 모든 리소스 경로(파일명, 폴더명 포함)에 대문자를 사용할 수 없습니다.
ModelBeegirl에서 사용된 대문자 **'M'**과 **'B'**가 규칙에 어긋나서 게임이 실행을 거부한 것입니다.
공격애니메이션이 안됫네
특히, **attack 애니메이션의 길이가 2초(40틱)**로 설정되어 있는데, 현재 벌 소녀가 0.25초(5틱)마다 화살을 쏘고 있으므로 애니메이션이 계속 초기화될 수 있습니다.
- Attack: animation_length: 2 (2초)입니다. 팔을 뻗는 동작인 것 같습니다.
- 현재 BeeGirlSkirmishGoal에서 5틱마다 화살을 쏘므로, 공격 시마다 팔을 뻗는 동작이 매우 빠르게(연사 느낌으로) 리셋되며 보일 것입니다.
- 만약 팔 동작을 더 길게 보고 싶다면 SkirmishGoal의 stateTimer % 5 숫자를 20 정도로 늘리면 1초에 한 번씩 팔을 크게 뻗으며 쏘게 됩니다.
1초동안 팔뻣고 1초동안 공격세레인데
아예애니메이션이 안보이는데
re?
좀더 확실하게 애니메이션 보이도록

0.75까지는 준비동작
0.75~1.25까지 공격
틱으로 계산하네
1. 대본 (JSON 파일)
먼저 블록벤치에서 만든 애니메이션 파일(beegirl2.animation.json)은 일종의 대본입니다.
- 이 대본에는 "이름: idle - 날개를 살랑살랑 흔든다", "이름: attack2 - 하늘로 솟구치며 화살을 쏜다"와 같은 동작 지시사항이 이름별로 적혀 있습니다.
2. 무전기 신호 (서버와 클라이언트)
마인크래프트에서 공격 계산은 **서버(두뇌)**가 하고, 화면에 그림을 그리는 건 **클라이언트(눈)**가 합니다.
- 벌 소녀가 공격 사거리에 들어와서 화살을 쏠 시간이 되면, 서버가 클라이언트에게 무전기를 칩니다.
- "지금 5번 신호 보낸다! 다들 준비해!" 라고 신호를 보내는 것입니다.
3. 배우의 뇌 (애니메이션 컨트롤러)
벌 소녀라는 배우의 뇌 속에는 **"상태 체크기"**가 계속 돌아가고 있습니다.
- 신호 확인: "방금 5번 무전(신호)이 왔네? 그럼 지금부터 1.25초 동안은 '공격 상태'로 지내야지." 라고 마음을 먹습니다.
- 이름 찾기: 배우는 대본(JSON)을 펼쳐서 **'attack2'**라는 이름을 찾습니다.
- 연기 시작: 대본에 적힌 attack2의 동작(0초에 위로 점프, 0.75초에 팔 뻗기 등)대로 3D 모델의 관절을 움직이기 시작합니다.
요약하자면 이렇습니다!
- 파일에 동작을 저장하고 이름을 붙여둡니다 (attack2).
- 서버가 공격할 때 "신호"를 보냅니다.
- 클라이언트가 그 신호를 받으면, 파일에서 그 이름을 찾아다가 화면에 재생하는 것입니다.
우리가 코드에 attack2라고 적어준 이유는, 수많은 애니메이션 중에서 어떤 이름을 가진 연기를 할지 집어주기 위해서입니다. 만약 코드에 이름을 잘못 적으면 배우가 대본에서 이름을 못 찾아서 가만히 서 있게 됩니다.
애니메이션 한번만 나오고 충분히 날지를 못하네
[ ]근데 높이를 지정해버리면 위에 블록이 없을때 오류날수도 잇을것같은데 예외처리 해줘야하나
블록벤치에서 **Only Once(한 번만 재생)**로 설정하면, 애니메이션이 끝난 뒤에 "나 이제 할 일 다 했어"라고 상태가 고정되어 버립니다. 그래서 다음 공격 때 똑같은 애니메이션을 틀으라고 명령해도, 컴퓨터는 **"이미 아까 끝까지 다 틀었는데요?"**라고 생각하며 반응하지 않는 것입니다.
이를 해결하려면 애니메이션이 끝났을 때 **"이 컨트롤러는 이제 초기화해도 돼"**라고 강제로 알려주는 코드가 필요합니다.
BeeGirlEntity.java 파일의 attack2Predicate 메서드를 아래와 같이 수정하세요.
BeeGirlEntity.java 수정 코드
private <E extends IAnimatable> PlayState attack2Predicate(AnimationEvent<E> event) {
if (this.attack2Timer > 0) {
// 애니메이션을 재생합니다.
event.getController().setAnimation(new AnimationBuilder().addAnimation("attack2", ILoopType.EDefaultLoopTypes.PLAY_ONCE));
return PlayState.CONTINUE;
}
// 타이머가 0이 되면 (공격이 끝나면)
// 컨트롤러를 리셋(markNeedsReload)하여 다음 번에 다시 재생될 수 있도록 준비합니다.
event.getController().markNeedsReload();
return PlayState.STOP;
}
왜 이렇게 해야 하나요?
- 상태 리셋: markNeedsReload()는 "이 컨트롤러가 지금 잡고 있는 애니메이션 정보를 잊어버려라"라는 뜻입니다. 이걸 해줘야 다음 공격 때 다시 attack2 신호를 줬을 때 0초부터 시작하게 됩니다.
- Only Once 설정 유지: 블록벤치에서 Only Once로 설정한 것은 아주 잘하신 겁니다. 공격 애니메이션은 무한 반복되면 안 되니까요. 다만 자바 코드에서는 "재생이 끝난 뒤의 뒤처리"를 해줘야 할 뿐입니다.
추가 확인 사항 (SkirmishGoal)
혹시 위 코드를 수정해도 한 번만 나온다면, BeeGirlSkirmishGoal.java에서 **sequenceTick**이 제대로 초기화되는지도 확인해야 합니다.
// BeeGirlSkirmishGoal.java의 tick 메서드 마지막 부분
if (sequenceTick > 25) {
sequenceTick = 0; // 이 부분이 있어야 다음 공격 때 다시 0부터 시작하며 신호를 보냅니다.
stateTimer -= 25;
}
결론: BeeGirlEntity.java의 attack2Predicate 안에 event.getController().markNeedsReload(); 한 줄만 추가하면 두 번째 공격부터 애니메이션이 안 나오는 문제가 해결될 것입니다! 지금 바로 적용해 보세요.
일단 애니메이션 나오기는 한다는거
근데 아마 이전 공격에도 팔뻣는 애니메이션이 나왓는데
너무 작고, 한번만 실행되서 안나온것처럼 새악되엇으리조
이제는 공격할때마다 해당 애니메이션이 재생되기는 하는데 타이밍이 좀
애니메이션에서 모델이 위로 올라가는 경우(Visual Height), 실제 엔티티의 히트박스는 지면에 남아있기 때문에 게임이 튕기거나 오류가 발생하지는 않습니다. 하지만 머리가 천장에 박혀서 모델이 블록 속으로 사라지는 어색한 연출이 발생할 수 있습니다.
이를 방지하기 위해 **"내 머리 위에 빈 공간이 충분히 있는가?"**를 체크하는 예외 처리 코드를 추가해주는 것이 좋습니다.
BeeGirlSkirmishGoal.java 파일에 아래 로직을 추가하면 머리 위에 블록이 있을 때 공중 부양 공격을 취소하거나 안전하게 처리할 수 있습니다.
1. SkirmishGoal.java에 추가할 공간 체크 메서드
이 메서드를 BeeGirlSkirmishGoal 클래스 내부에 추가하세요.
// 머리 위로 특정 칸(height)만큼 비어있는지 확인하는 메서드
private boolean isSpaceAbove(int height) {
for (int i = 1; i <= height; i++) {
// 엔티티 위치에서 i만큼 위쪽 블록의 상태를 가져옴
// 그 블록이 공기가 아니거나, 통과할 수 없는 블록이라면 false 반환
if (!this.beeGirl.level.getBlockState(this.beeGirl.blockPosition().above(i)).isAir()) {
return false;
}
}
return true;
}
2. tick() 메서드 내 적용 (예외 처리)
기존 tick() 메서드에서 sequenceTick == 0인 부분(애니메이션 시작 직전)에 이 체크 로직을 넣어주면 됩니다.
@Override
public void tick() {
LivingEntity target = this.beeGirl.getTarget();
if (target == null) return;
this.beeGirl.getLookControl().setLookAt(target, 30.0F, 30.0F);
if (isSiegeMode) {
this.beeGirl.getNavigation().stop();
// --- 공격 시퀀스 시작 전 예외 처리 ---
if (sequenceTick == 0) {
// 머리 위로 2~3칸 정도 공간이 있는지 확인 (애니메이션 높이에 따라 조절)
if (isSpaceAbove(3)) {
// 공간이 있으면 정상적으로 공중 공격 신호(5번) 전송
this.beeGirl.level.broadcastEntityEvent(this.beeGirl, (byte) 5);
} else {
// 공간이 없으면 일반 지상 공격 신호(기존에 썼던 4번 등)를 보내거나
// 시퀀스를 건너뛰고 바로 화살만 쏘게 할 수 있습니다.
changmin.myMod.MyMod.LOGGER.info("BeeGirl: 머리 위 공간 부족! 지상 사격으로 대체");
// 애니메이션 없이 사격만 하기 위해 sequenceTick을 바로 발사 단계(15)로 점프시킬 수도 있습니다.
this.sequenceTick = 15;
}
}
// (이후 사격 로직은 동일...)
3. 왜 이 코드가 필요한가요? (설명)
- 시각적 버그 방지: 천장이 낮은 동굴이나 집 안에서 벌 소녀가 갑자기 천장을 뚫고 나가는 현상을 막아줍니다.
- 안전성: 마인크래프트에서 블록 안에 엔티티(또는 모델)가 겹치면 간혹 끼임 현상이 발생할 수 있는데, 이를 논리적으로 차단합니다.
- 지능적 AI: 공간이 없을 때는 "준비 동작"을 건너뛰고 바로 화살을 쏘게 하거나(sequenceTick = 15), 다른 행동을 하게 함으로써 AI가 더 똑똑해 보입니다.
팁: 만약 머리 위에 블록이 있을 때 아예 공격을 안 하게 하고 싶다면, isSpaceAbove가 false일 때 sequenceTick을 올리지 않고 그냥 넘기시면 됩니다. 현재 코드는 공간이 없으면 0.75초의 대기 시간 없이 바로 화살을 쏘기 시작하는 방식으로 짜여 있습니다.
1초 = 20틱이므로, 0.5초는 10틱 지점
이거 맞긴한데
현재의 시즈모드랑 공격애니메이션이 매칭이 안되나보다
공격애니메이션은 1.25초인데
시즈모드는 5초이니깐
(오.. 지렷다 ㅋㅋ)
마인크래프트 리소스 규칙상 파일명에는 절대 띄어쓰기가 있으면 안 되지만, JSON 파일 내부의 애니메이션 이름에는 띄어쓰기를 쓸 수는 있습니다.
json 내부 에니메이션 이름이 뭥미~?햇는데 ㅇㅎ
애니메이션으로 모델을 위로 올리더라도 실제 히트박스(Hitbox, 충돌 범위)는 땅에 그대로 남아 있습니다.
그 이유는 다음과 같습니다.
1. 비주얼(그림) vs 로직(실제 위치)의 차이
- 애니메이션(Blockbench/GeckoLib): 이건 단순히 눈에 보이는 모델의 "관절"을 움직이는 것입니다. 모델이 10미터 위로 올라가 보이게 연기하는 것일 뿐, 게임 엔진이 생각하는 엔티티의 실제 좌표는 변하지 않습니다.
- 히트박스: 게임 서버가 엔티티의 발 밑 좌표를 기준으로 계산합니다.
2. 이로 인해 발생하는 현상
- 벌 소녀가 애니메이션 때문에 공중에 떠 있는 것처럼 보여도, 플레이어나 좀비가 벌 소녀의 **원래 서 있던 빈 공간(땅)**을 때리면 벌 소녀가 데미지를 입습니다.
- 반대로, 공중에 떠 있는 벌 소녀의 몸통 모델을 화살로 맞추면 화살이 그냥 통과해버릴 수 있습니다. (히트박스는 거기 없으니까요.)
3. 히트박스도 같이 올리고 싶다면? (해결 방법)
만약 단순히 시각적인 것뿐만 아니라 실제로 공중으로 날아올라서 아래에서 때릴 수 없게 만들고 싶다면, 애니메이션과 동시에 엔티티의 실제 좌표를 위로 올려주는 코드를 추가해야 합니다.
정말 idle이랑 충돌이 낫엇던건가
갑자기 애니메이션 실행자체가 안됨
로그가 찍히는데 애니메이션이 보이지 않거나 아주 잠깐만 보이다가 사라지는 이유는 "컨트롤러 충돌" 때문일 확률이 가장 높습니다.
idle_controller와 attack_controller가 동시에 root 본(Bone)을 조절하려고 하면, 서로 자기 애니메이션을 틀겠다고 싸우다가 결국 idle이 이기거나 애니메이션이 0초 지점에서 멈춰버리게 됩니다. 벌 소녀가 하늘로 올라가는 동작은 root 본을 움직이는 것인데, idle 애니메이션이 "아니야, 너는 땅(0,0,0)에 있어야 해"라고 강제로 붙잡고 있는 상태인 것이죠.
이를 해결하려면 공격 애니메이션이 재생되는 동안은 idle 애니메이션을 완전히 꺼주어야 합니다.
되긴하는데 공격을 얼마동안하고잇는거지
공격 애니메이션이 끝나고났는데도,
시간 분석 (왜 시간이 안 맞을까?)
마인크래프트는 1초 = 20틱입니다.
- 애니메이션 길이: 5.75초 = 115틱
- 현재 화살 발사: sequenceTick 15틱(0.75초)부터 115틱(5.75초)까지 발사.
- 문제점: 애니메이션 상으로는 공격 동작이 이미 끝났는데, 코드(Goal)에서는 115틱까지 꽉 채워서 화살을 쏘라고 되어 있기 때문에 "애니메이션은 끝났는데 화살은 계속 나가는" 현상이 생기는 것입니다.
2. 해결책
화살 발사를 애니메이션이 완전히 끝나기 전인 5초(100틱) 지점에서 멈추도록 수정하면, 애니메이션이 마무리되면서 자연스럽게 공격이 끝나는 느낌이 듭니다.
한 3초? 2초정도 화살을 더쏘는느낌인데
현재 적용된 벌 소녀의 공격 메커니즘을 **시간의 흐름(타임라인)**에 따라 말로 쉽게 설명해 드릴게요.
벌 소녀가 좀비를 발견하고 "공격 모드(시즈 모드)"에 들어가면, 0초부터 5.75초까지 다음과 같은 순서로 일이 벌어집니다.
1단계: 공격 시작 (0초 지점)
- 두뇌(Goal): 벌 소녀의 AI가 "이제 쏠 시간이다!"라고 판단합니다. 이때 sequenceTick이 0이 됩니다.
- 신호 발송: 서버가 클라이언트(플레이어 화면)에게 **"5번 신호"**를 보냅니다.
- 배우(Entity): 신호를 받은 벌 소녀 모델은 즉시 attack 5sec 애니메이션을 재생합니다.
- 상태: 벌 소녀가 하늘로 솟구치기 시작합니다. (이때는 아직 화살이 나가지 않습니다.)
2단계: 공격 준비 (0초 ~ 0.75초 사이)
- 두뇌(Goal): sequenceTick이 1부터 14까지 올라가는 동안 서버는 아무것도 하지 않고 숫자만 셉니다.
- 배우(Entity): 애니메이션 대본에 따라 몸을 뒤로 젖히거나 높이 올라가는 연기를 합니다.
3단계: 공중 포격 (0.75초 ~ 5초 사이)
- 두뇌(Goal): sequenceTick이 **15(0.75초)**가 되는 순간부터 화살을 만들기 시작합니다.
- 화살 발사: 115틱까지 쏘는 게 아니라, **100틱(5초)**이 될 때까지만 3틱마다 한 번씩 화살을 생성해서 좀비에게 날립니다.
- 상태: 벌 소녀가 공중에 떠서 계속 팔을 흔들며 화살을 쏟아붓는 모습이 연출됩니다.
4단계: 사격 중단 및 착지 (5초 ~ 5.75초 사이)
- 두뇌(Goal): sequenceTick이 100을 넘어가면 화살 발사를 멈춥니다.
- 배우(Entity): 애니메이션은 아직 115틱(5.75초)까지 남아있으므로, 남은 0.75초 동안 공중에서 내려오거나 자세를 바로잡는 마무리를 합니다.
- 상태: 화살은 멈췄지만 벌 소녀가 자연스럽게 지면으로 내려오는 듯한 모습이 보입니다.
5단계: 재정비 (5.75초 이후)
- 두뇌(Goal): sequenceTick이 115가 되면 모든 공격 과정을 초기화합니다.
- 이동: 이제 공격이 끝났으니, 잠시 동안 좀비로부터 거리를 벌리거나 다른 곳으로 이동(도망 모드)합니다.
만약 0.5초 시점부터 화살을 쏘게 하고 싶다면?
BeeGirlSkirmishGoal.java에서 if (sequenceTick >= 15) 부분을 **if (sequenceTick >= 10)**으로 고치기만 하면 됩니다. (10틱 = 0.5초)
현재 벌 소녀는 애니메이션과 사격 타이밍이 거의 완벽하게 동기화되어 있는 상태입니다!
마인크래프트에서 4초는 80틱입니다. (1초 = 20틱이므로 4 × 20 = 80)
// 기존: 15(0.75초) ~ 100(5초)까지 발사
if (sequenceTick >= 15 && sequenceTick <= 80) {
// 80으로 수정하면 정확히 4초가 되는 순간 화살 발사가 멈춥니다.
이부분 좀 조정해볼까
근데 관련 변수가 하나가 아니네
왜 변수가 두개나 필요한거지
현재 코드에서 sequenceTick과 stateTimer는 서로 역할이 완전히 다르며, 값이 달라도 괜찮습니다. 오히려 서로 다른 목적을 위해 존재합니다.
누가 무엇을 결정하는지 아주 쉽게 정리해 드릴게요.
1. sequenceTick (동작 연출가)
역할: "애니메이션 한 사이클(5.75초) 안에서 언제 무엇을 할지" 결정
- 비유: 노래 한 곡(5.75초짜리)의 진행 바(Progress Bar)입니다.
- 누가 사격을 결정하나? 바로 이 변수입니다.
- if (sequenceTick >= 15 && sequenceTick <= 100) -> 이 코드가 **"애니메이션 시작 0.75초 후부터 5초까지만 쏴라"**라고 사격 시간을 결정하는 핵심입니다.
- 특징: 0에서 시작해서 115(애니메이션 끝)가 되면 다시 0으로 초기화됩니다. 즉, **벌 소녀가 한 번 하늘로 올라갔다 내려오는 "한 번의 행동"**을 담당합니다.
2. stateTimer (상태 관리자)
역할: "이 모드(공격 모드 또는 도망 모드)를 얼마나 오랫동안 유지할지" 결정
- 비유: 노래방에서 "공격 노래방"을 몇 분 동안 이용할지 정해진 전체 시간입니다.
- 값이 달라도 되나? 네.
- stateTimer는 공격 모드 전체 시간(예: 115틱)을 관리하다가, 이 시간이 다 되면 isSiegeMode = false로 바꾸면서 "이제 도망가자!"라고 모드 자체를 전환시키는 역할을 합니다.
- 특징: 공격 중에는 sequenceTick이 한 번 끝날 때마다 뭉텅이로 깎이거나(stateTimer -= 25), 도망 중에는 1틱씩 정직하게 깎입니다(stateTimer--).
3. 정리: 화살 쏘는 시간은 누가 결정하나요?
화살을 쏘는 "정확한 타이밍"은 오직 sequenceTick이 결정합니다.
- 시작 지점 수정: sequenceTick >= 15 이 숫자를 바꾸면 됩니다. (0.5초면 10, 1초면 20)
- 끝나는 지점 수정: sequenceTick <= 100 이 숫자를 바꾸면 됩니다. (3초면 60, 4초면 80)
4. 만약 사격 시간을 더 늘리고 싶다면?
만약 화살을 5초가 아니라 더 오래(예: 10초) 쏘게 하고 싶다면 두 가지를 다 건드려야 합니다.
- stateTimer: 공격 모드에 머무는 전체 시간을 늘려야 합니다. (예: 200)
- sequenceTick: 115에서 초기화되지 않고 계속 숫자가 올라가게 하거나, 여러 번 반복되게 해야 합니다.
현재 구조에서의 흐름:
- isSiegeMode가 켜짐.
- sequenceTick이 0부터 115까지 흐르면서 딱 한 번의 멋진 공중 포격을 수행. (이 중 15~100틱 사이에 화살 발사)
- sequenceTick이 115를 넘으면 "공격 한 번 다 했다!" 하고 isSiegeMode를 끄고 도망 모드로 전환.
결론:
지금 코드에서 화살 쏘는 시간을 조금씩 조정해보고 싶으시면 sequenceTick >= 15 && sequenceTick <= 100 이 부분의 숫자만 계속 바꿔보시면 됩니다! stateTimer는 모드 전환용이라 지금은 크게 신경 쓰지 않으셔도 됩니다.
현재 작성하신 코드에서 벌 소녀의 인식 거리, 공격 사거리, 도망 거리는 모두 BeeGirlEntity.java 파일의 registerGoals() 부분과 createAttributes() 부분에 숫자로 적혀 있습니다.
각각의 의미와 수정 위치를 설명해 드릴게요.
1. 좀비 인식 거리 (얼마나 멀리 있는 적을 찾아내는가?)
- 현재 거리: 32블록
- 설정 위치: BeeGirlEntity.java의 createAttributes() 메서드
.add(Attributes.FOLLOW_RANGE, 32.0D) // 32블록 안에 있는 좀비를 감지하고 타겟으로 잡습니다. - 이 범위 안에 좀비가 들어오면 벌 소녀가 그 좀비를 쳐다보고 공격할 준비를 합니다.
2. 화살 공격 범위 (어느 정도 거리에서 활을 쏘는가?)
- 현재 거리: 15블록
- 설정 위치: BeeGirlEntity.java의 registerGoals() 메서드 중 BeeGirlSkirmishGoal 생성 부분의 세 번째 숫자
this.goalSelector.addGoal(1, new BeeGirlSkirmishGoal(this, 1.0D, 15.0F, 5.0F)); // ↑ ↑ ↑ // 속도 공격범위 도망범위 - 좀비와의 거리가 15블록 이내일 때만 시즈 모드(공중 부양 포격)를 시작합니다.
이걸 이용해서, 장거리 저격수 만들수도
3. 도망가는 거리 (얼마나 가까이 오면 도망가는가?)
- 현재 거리: 5블록
- 설정 위치: BeeGirlEntity.java의 registerGoals() 메서드 중 BeeGirlSkirmishGoal 생성 부분의 마지막 숫자
this.goalSelector.addGoal(1, new BeeGirlSkirmishGoal(this, 1.0D, 15.0F, 5.0F)); - 도망 로직:
- 공격(시즈 모드)이 한 번 끝나고 재정비(isSiegeMode = false) 상태가 되었을 때 체크합니다.
- 이때 좀비가 5블록 이내로 너무 가까이 있으면, 벌 소녀는 좀비 반대 방향으로 1.2배 빠른 속도로 도망갑니다.
- 만약 좀비가 15블록보다 멀리 있으면 다시 15블록 안으로 접근하려고 합니다.
이래서 공격마치고 가만히 잇는것처럼 보엿구나
좀비는 거리 멀리잇을테니깐, 공격 다 맞고나면
디펜스 게임 아이디어는 마인크래프트 모딩에서 정말 인기 있고 재미있는 주제입니다! 좀비의 인공지능(AI)이 어떻게 작동하는지 알면 이 게임 디자인을 훨씬 정교하게 만드실 수 있습니다.
코드를 제외하고, 좀비의 **'복수 방식'**과 **'인식 방식'**을 쉽게 설명해 드릴게요.
1. 좀비의 "복수 리스트" (HurtByTarget)
좀비는 기본적으로 **"나를 아프게 한 녀석은 끝까지 쫓아간다"**는 본능이 있습니다.
- 살아있는 생명체(Living Entity)가 공격할 때: 벌 소녀나 마법사가 화살을 쏘면, 좀비는 "방금 저 녀석이 나를 때렸어!"라고 인식하고 타겟을 즉시 바꿉니다. 원래 마을로 가고 있었더라도, 자기를 공격한 벌 소녀를 향해 뛰어갑니다.
- 생명체가 아닌 것이 공격할 때: 만약 디펜스 게임의 **'함정'**이나 **'자동 포탑(블록)'**이 좀비를 공격한다면? 좀비는 아파하긴 하지만 반격할 대상을 찾지 못합니다. 왜냐하면 좀비의 뇌는 '블록'이나 '기계'를 적으로 인식하도록 설계되어 있지 않기 때문입니다.
2. 좀비의 "우선순위"
좀비에게는 머릿속에 **"꼭 잡아야 할 명단"**이 있습니다.
- 플레이어
- 주민 (그리고 아군 엔티티들)
- 철 골렘
좀비는 공격받지 않는 이상, 이 명단에 있는 대상을 향해 무작정 걸어갑니다.
3. 디펜스 게임을 만들 때 고려할 점 (아이디어)
사용자님이 떠올리신 디펜스 게임을 더 재밌게 만들려면 좀비의 AI를 이렇게 이용해볼 수 있습니다.
- 어그로(Aggro) 시스템: 벌 소녀가 좀비를 때리면 좀비가 벌 소녀에게 달려듭니다. 이때 벌 소녀가 공중으로 도망가면 좀비는 다시 원래 목표(성문이나 마을)로 돌아갑니다. 이걸 이용해 좀비를 유인하는 전략을 짤 수 있습니다.
- 공격받지 않아도 공격하게 만들기: 현재 좀비는 자기를 때려야만 벌 소녀를 보지만, 설정을 바꾸면 "벌 소녀가 보이기만 해도" 좀비가 눈이 뒤집혀서 달려들게 만들 수 있습니다. 그러면 벌 소녀가 일종의 '방어 타워' 역할을 하게 되는 거죠.
- 타겟팅의 한계: 좀비는 한 번에 단 한 명만 타겟으로 잡습니다. 벌 소녀가 10명이 있어도 좀비는 자기를 가장 마지막에 때린 '한 명'만 쫓아갑니다.
요약하자면:
좀비는 기본적으로 **"자기를 때린 살아있는 존재"**에게만 반격합니다.
디펜스 게임을 만드실 때, 벌 소녀(궁수)를 곳곳에 배치하면 좀비들이 마을로 가다가 벌 소녀의 화살을 맞고 "어? 나 때렸어?" 하고 벌 소녀 쪽으로 우르르 몰려가는 '어그로 분산' 효과를 자연스럽게 만드실 수 있습니다.
이 원리를 이용하면 좀비들이 가는 길목에 유인용 엔티티를 세워두는 등 아주 전략적인 디펜스 게임이 가능해집니다!
'모딩 > 마인크래프트 모드 개발 일지' 카테고리의 다른 글
| 마크 모딩) brutal boss처럼 (0) | 2026.01.30 |
|---|---|
| 마크 모딩) 디펜스 타워 만들기 (1) | 2026.01.12 |
| 마크) 1.18.2 게코립 설정 시 1.18.2가 아닌 1.18을 써야 하는 이유 (1) | 2025.12.28 |
| 맠모딩 ) 다른사람의 엔티티 모델 가져오기 (1) | 2025.12.27 |
| 마크 모딩) "천사강림" (1) | 2025.12.26 |