본문으로 건너뛰기
버전: 1.20.x

상태 효과 & 물약

상태 효과, 또는 포션 이펙트는 코드에선 MobEffect라 불리며, 매 틱마다 엔티티에 영향을 줍니다. 이 문서에선 상태 효과 사용법, 효과와 물약의 차이, 그리고 새로운 효과를 만드는 방법에 대해 다루겠습니다.

용어

  • MobEffect는 상태 효과로, 매 틱 마다 엔티티에 영향을 줍니다. 블록이나 아이템처럼, MobEffect는 레지스트리에 등록되어야 정상적으로 동작합니다.
    • 즉시 효과는 특수한 상태 효과로, 영향을 한 틱만 줍니다. 즉시 치유, 즉시 피해가 이에 해당합니다.
  • MobEffectInstanceMobEffect와 지속 시간, 강도 등을 저장합니다 (아래 참고). MobEffectInstanceMobEffect의 관계는 ItemStackItem의 관계와 유사합니다.
  • PotionMobEffectInstance의 집합입니다. 바닐라 마인크래프트는 네 개의 물약 아이템에만 포션을 사용합니다 (아래 참고). 하지만 아이템이 포션을 사용할 줄만 안다면 아무 아이템에다 적용하셔도 괜찮습니다.
  • 포션 아이템은 포션이 적용되어야 하는 아이템입니다. 이는 PotionItem 클래스와 아무런 연관이 없습니다 (이 클래스는 물약 아이템을 구현하는 데 사용됩니다). 마인크래프트에는 기본적으로 이러한 아이템이 네 가지가 있는 데: 물약, 투척용 물약, 잔류형 물약, 화살촉입니다. 또한 모드에서 다른 아이템도 추가할 수 있습니다.

MobEffect

새로운 MobEffect를 만들려면, 먼저 MobEffect의 하위 클래스를 만드세요:

public class MyMobEffect extends MobEffect {
public MyMobEffect(MobEffectCategory category, int color) {
super(category, color);
}

@Override
public boolean applyEffectTick(LivingEntity entity, int amplifier) {
// Apply your effect logic here.

// If this returns false when shouldApplyEffectTickThisTick returns true, the effect will immediately be removed
return true;
}

// 이번 틱에 효과가 적용되어야 할지를 결정합니다. 예를 들어 재생 물약이 강도에 따라
// 몇 틱에 한 번씩만 사용자를 회복시켜 주는 데 사용합니다.
@Override
public boolean shouldApplyEffectTickThisTick(int tickCount, int amplifier) {
return tickCount % 2 == 0; // 2 틱에 한 번씩만 효과를 적용함
}

// Utility method that is called when the effect is first added to the entity.
// This does not get called again until all instances of this effect have been removed from the entity.
@Override
public void onEffectAdded(LivingEntity entity, int amplifier) {
super.onEffectAdded(entity, amplifier);
}

// Utility method that is called when the effect is added to the entity.
// This gets called every time this effect is added to the entity.
@Override
public void onEffectStarted(LivingEntity entity, int amplifier) {
}
}

여타 레지스트리 객체와 동일하게, MobEffect 또한 아래와 같이 등록되어야 합니다:

//MOB_EFFECTS는 DeferredRegister<MobEffect>
public static final Supplier<MyMobEffect> MY_MOB_EFFECT = MOB_EFFECTS.register("my_mob_effect", () -> new MyMobEffect(
// 아래 값은 BENEFICIAL, NEUTRAL 또는 HARMFUL로 지정할 수 있음. 포션의 툴팁 글자 색상을 결정하는 데 사용함.
MobEffectCategory.BENEFICIAL,
// 포션의 파티클 색상.
0xffffff
));

포션 효과를 단순히 마커로만 사용하려면, Block이나 Item을 만들 때와 동일하게 바로 MobEffect 클래스의 인스턴스를 만드셔도 됩니다. MobEffect 클래스는 엔티티에 속성을 추가하는 기본적인 기능도 제공합니다. 예를 들어 신속 효과는 엔티티에 속성을 추가하여 이동 속도를 조절합니다. 속성은 다음과 같이 추가할 수 있습니다:

public static final String MY_MOB_EFFECT_UUID = "01234567-89ab-cdef-0123-456789abcdef";
public static final Supplier<MyMobEffect> MY_MOB_EFFECT = MOB_EFFECTS.register("my_mob_effect", () -> new MyMobEffect(...)
.addAttributeModifier(Attribute.ATTACK_DAMAGE, MY_MOB_EFFECT_UUID, 2.0, AttributeModifier.Operation.ADD_VALUE)
);
노트

UUID는 무조건 올바르고 고유한 UUIDv4여야 합니다. 모장은 무슨 이유에선가 텍스트로 구분하는 것이 아닌 UUID로 각 속성을 구분합니다. 새 UUID는 이곳과 같은 사이트에서 생성하실 수 있습니다.

InstantenousMobEffect

즉시 적용되는 효과를 만들고 싶으시다면, 아래와 같이 InstantenousMobEffect를 대신 사용하실 수 있습니다:

public class MyMobEffect extends InstantenousMobEffect {
public MyMobEffect(MobEffectCategory category, int color) {
super(category, color);
}

@Override
public void applyEffectTick(LivingEntity entity, int amplifier) {
// 효과를 여기서 구현하세요
}
}

이후 MobEffect와 동일하게 레지스트리에 등록하셔야 합니다.

이벤트

여러 효과들은 그 기능이 다른 곳에 구현된 경우가 많습니다. 예를 들어 공중 부양의 경우 엔티티 물리 연산에서 날아다니는 기능이 구현되어 있습니다. 모드에서 추가한 효과도 이와 유사한 기능을 만들 수 있도록 네오 포지는 여러 이벤트들을 제공합니다:

  • MobEffectEvent.ApplicableMobEffectInstance를 엔티티에 적용할 수 있는지 확인할 때 방송됨. 효과 부여를 차단 또는 강행할 때 사용할 수 있음.
  • MobEffectEvent.AddedMobEffectInstance가 추가되면 방송되는 이벤트. 대상 엔티티가 새로운 효과를 부여받기 이전에 또 다른 MobEffectInstance가 있었다면 이 또한 이벤트를 통해 전달됨.MobEffectEvent.ExpiredMobEffectInstance가 만료되면 방송됨. 예: 효과 지속 시간이 0이 됨.
  • MobEffectEvent.Remove는 상태 효과가 만료 이외의 이유로 제거되었을 때 방송됨. 예: 우유를 마심, 명령어로 제거함

MobEffectInstance

MobEffectInstance는, 단순히 말해서, 엔티티에 적용된 상태 효과입니다. MobEffectInstance는 다음과 같이 생성합니다:

MobEffectInstance instance = new MobEffectInstance(
// 사용할 상태 효과 종류.
MobEffects.REGENERATION,
// 상태 효과의 지속시간. 0이 기본값.
500,
// 상태 효과 강도. 0이 최솟값 및 기본값.
0,
// 상태 효과가 주변 환경에 의해 부여됐는지 여부.
// 신호기 또는 전달체의 경우 true를 사용함. false가 기본값.
false,
// 상태 효과가 인벤토리에 표시되는지 여부. true가 기본값.
true,
// 상태 효과가 화면 우 상단에 표시되는지 여부. true가 기본값.
true
);

이것 말고도 MobEffectInstance에는 뒤 5개의 인자들이 하나씩 누락된 생성자들이 다수 있습니다.

정보

MobEffectInstance의 값은 변경될 수 있습니다. 만약 복사를 하고 싶으시다면 new MobEffectInstance(oldInstance)를 호출하세요.

MobEffectInstance 응용하기

MobEffectInstance는 아래와 같이 엔티티에 적용할 수 있습니다:

MobEffectInstance instance = new MobEffectInstance(...);
entity.addEffect(instance);

비슷하게 MobEffectInstance를 제거할 수도 있습니다. 엔티티에 같은 MobEffect를 가지는 MobEffectInstance를 여러 번 적용할 수 없어 같은 효과를 다시 적용하면 기존 효과는 제거됩니다. 각 상태 효과당 MobEffectInstance는 하나만 있으므로 상태 효과는 아래와 같이 MobEffect만 지정하여도 제거할 수 있습니다:

entity.removeEffect(MobEffects.REGENERATION);
정보

MobEffect는 오직 플레이어, 몬스터와 같은 LivingEntity의 하위 클래스에만 적용할 수 있습니다. 아이템 엔티티나 눈덩이에는 적용할 수 없습니다.

Potion

Potion은 아래와 같이 섭취 시 적용할 MobEffectInstance들을 생성자에 전달하여 생성할 수 있습니다:

//POTIONS은 DeferredRegister<Potion>
public static final Supplier<Potion> MY_POTION = POTIONS.register("my_potion", () -> new Potion(new MobEffectInstance(MY_MOB_EFFECT, 3600)));

위 생성자의 인자는 가변 인자입니다, 상태 효과들을 원하시는 만큼 추가할 수 있습니다. 이때 상태 효과가 없는 포션을 만드는 것도 가능한데, 단순히 new Potion()을 호출하시면 됩니다. (마인크래프트는 어색한 포션을 이렇게 만듭니다.)

포션의 이름은 위 생성자의 첫 번째 인자로 전달합니다. 이는 번역에 사용되는 값으로, 바닐라는 이를 활용해 강한 포션과 오래가는 포션이 기본 포션에 같은 이름을 부여합니다. 이름은 누락되어도 되며, 레지스트리에서 대신 가져옵니다.

The PotionContents class offers various helper methods related to potion items. Potion item store their PotionContents via DataComponent#POTION_CONTENTS.

양조기

이제 포션을 추가했으니, 서바이벌 모드에서 추가한 포션을 얻을 수 있도록 양조기의 조합법을 추가해 봅시다.

Potions are traditionally made in the Brewing Stand. Unfortunately, Mojang does not provide datapack support for brewing recipes, so we have to be a little old-fashioned and add our recipes through code via the RegisterBrewingRecipesEvent event. This is done like so:

// Using some method to listen to the event
@SubscribeEvent
public static void registerBrewingRecipes(RegisterBrewingRecipesEvent event) {
// Gets the builder to add recipes to
PotionBrewing.Builder builder = event.getBuilder();

// Will add brewing recipes for all container potions (e.g. potion, splash potion, lingering potion)
builder.addMix(
// The initial potion to apply to
Potions.AWKWARD,
// The brewing ingredient. This is the item at the top of the brewing stand.
Items.FEATHER,
// The resulting potion
MY_POTION
);
}

This should be called some time during setup, for example during FMLCommonSetupEvent. Make sure to wrap this into an event.enqueueWork() call, as the brewing recipe registry is not thread-safe.