Створення вашої першої рідини
Цей приклад охоплює створення рідини кислоти, яка завдає шкоду сутності та ослаблює й осліплює її, які в рідині. Для цього нам знадобляться два екземпляри рідини для джерела та станів рідини, блок рідини, предмет відра та теґ рідини.
Створення клас рідини
Ми почнемо зі створення абстрактного класу, у цьому випадку під назвою AcidFluid, який розширює базовий клас FlowingFluid. Потім ми замінимо будь-які методи, які мають бути однаковими як для джерела, так і для текучої рідини.
Зверніть особливу увагу на такі методи:
animateTickвикористовується для показу частинок і звуку. Поведінка, показана нижче, заснована на воді, яка відтворює звук, коли тече, і містить частинки підводні бульбашки.entityInsideвикористовується для обробки того, що має статися, коли сутність торкається рідини. Ми будемо базувати його на воді та гасити будь-який вогонь на сутностях, але також змусимо його завдати шкоди сутності та ослаблювати й осліплювати її всередині — адже це кислота.canBeReplacedWithобробляє деяку поточну логіку — зауважте, щоModFluidTags.ACIDще не визначено, ми розглянемо це в кінці.
Зібравши все це разом, ми отримаємо наступний клас:
java
public abstract class AcidFluid extends FlowingFluid {
@Override
public void animateTick(Level world, BlockPos pos, FluidState state, RandomSource random) {
if (!state.isSource() && !(Boolean) state.getValue(FALLING)) {
if (random.nextInt(64) == 0) {
world.playLocalSound(
pos.getX() + 0.5,
pos.getY() + 0.5,
pos.getZ() + 0.5,
SoundEvents.BUBBLE_COLUMN_WHIRLPOOL_AMBIENT, // Bubbling poison/swamp sound
SoundSource.AMBIENT,
random.nextFloat() * 0.25F + 0.75F,
random.nextFloat() + 0.5F,
false);
}
} else if (random.nextInt(10) == 0) {
world.addParticle(
ParticleTypes.UNDERWATER, pos.getX() + random.nextDouble(), pos.getY() + random.nextDouble(),
pos.getZ() + random.nextDouble(), 0.0, 0.0, 0.0);
}
}
@Nullable
@Override
public ParticleOptions getDripParticle() {
return ParticleTypes.DRIPPING_WATER;
}
@Override
protected boolean canConvertToSource(ServerLevel world) {
return world.getGameRules().get(GameRules.WATER_SOURCE_CONVERSION);
}
@Override
protected void beforeDestroyingBlock(LevelAccessor world, BlockPos pos, BlockState state) {
BlockEntity blockEntity = state.hasBlockEntity() ? world.getBlockEntity(pos) : null;
Block.dropResources(state, world, pos, blockEntity);
}
@Override
protected void entityInside(Level world, BlockPos pos, Entity entity, InsideBlockEffectApplier handler) {
handler.apply(InsideBlockEffectType.EXTINGUISH);
if (!(world instanceof ServerLevel serverLevel) || !(entity instanceof LivingEntity livingEntity)) return;
if (world.getGameTime() % 20 == 0) {
// Hurt and weaken entities that step inside.
livingEntity.hurtServer(serverLevel, world.damageSources().magic(), 2.0F); // 1 heart/sec
livingEntity.addEffect(new MobEffectInstance(MobEffects.WEAKNESS, 300, -3));
livingEntity.addEffect(new MobEffectInstance(MobEffects.BLINDNESS, 300, -3));
}
}
@Override
protected int getSlopeFindDistance(LevelReader world) {
return 4;
}
@Override
public int getDropOff(LevelReader world) {
return 1;
}
@Override
public int getTickDelay(LevelReader world) {
return 5;
}
@Override
public boolean canBeReplacedWith(FluidState state, BlockGetter world, BlockPos pos, Fluid fluid,
Direction direction) {
return direction == Direction.DOWN && !fluid.is(ModFluidTags.ACID);
}
@Override
protected float getExplosionResistance() {
return 100.0F;
}
@Override
public Optional<SoundEvent> getPickupSound() {
return Optional.of(SoundEvents.BUCKET_FILL);
}
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
Усередині AcidFluid ми створимо два підкласи для рідин Source і Flowing.
java
public static class Flowing extends AcidFluid {
@Override
protected void createFluidStateDefinition(StateDefinition.Builder<Fluid, FluidState> builder) {
super.createFluidStateDefinition(builder);
builder.add(LEVEL);
}
@Override
public int getAmount(FluidState state) {
return state.getValue(LEVEL);
}
@Override
public boolean isSource(FluidState state) {
return false;
}
}
public static class Source extends AcidFluid {
@Override
public int getAmount(FluidState state) {
return 8;
}
@Override
public boolean isSource(FluidState state) {
return true;
}
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
Реєстрація рідин
Далі ми створимо клас для реєстрації всіх екземплярів рідини. Ми назвемо це ModFluids.
java
public class ModFluids {
public static final FlowingFluid ACID_FLOWING = register("flowing_acid", new AcidFluid.Flowing());
public static final FlowingFluid ACID_STILL = register("acid", new AcidFluid.Source());
private static FlowingFluid register(String name, FlowingFluid fluid) {
return Registry.register(BuiltInRegistries.FLUID, Identifier.fromNamespaceAndPath(ExampleMod.MOD_ID, name), fluid);
}
public static void initialize() {
}
}1
2
3
4
5
6
7
8
9
10
11
2
3
4
5
6
7
8
9
10
11
Як і з блоками, вам потрібно переконатися, що клас завантажено, щоб усі статичні поля, що містять екземпляри вашої рідини, були ініціалізовані. Ви можете зробити це, створивши фіктивний метод initialize, який можна викликати в ініціалізаторі мода, щоб запустити статичну ініціалізацію.
Тепер поверніться до класу AcidFluid і додайте ці методи, щоб зв’язати зареєстровані екземпляри рідини з цією рідиною:
java
@Override
public Fluid getFlowing() {
return ModFluids.ACID_FLOWING;
}
@Override
public Fluid getSource() {
return ModFluids.ACID_STILL;
}
@Override
public boolean isSame(Fluid fluid) {
return fluid == ModFluids.ACID_STILL || fluid == ModFluids.ACID_FLOWING;
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
2
3
4
5
6
7
8
9
10
11
12
13
14
15
Наразі ми зареєстрували вихідний стан рідини та її текучий стан. Далі нам потрібно буде зареєструвати відро та LiquidBlock для нього.
Реєстрація блоків рідини
Тепер додаймо блок рідини для нашої рідини. Це потрібно деяким командам, таким як setblock, щоб ваша рідина могла існувати у світі. Якщо ви ще цього не зробили, вам слід поглянути на те, як створити свій перший блок.
Відкрийте свій клас ModBlocks і зареєструйте наступний LiquidBlock:
java
public static final Block ACID = register(
"acid",
(props) -> new LiquidBlock(ModFluids.ACID_STILL, props),
BlockBehaviour.Properties.ofFullCopy(Blocks.WATER),
false
);1
2
3
4
5
6
2
3
4
5
6
Потім змініть цей метод у AcidFluid, щоб пов’язати ваш блок із рідиною:
java
@Override
protected BlockState createLegacyBlock(FluidState state) {
return ModBlocks.ACID.defaultBlockState().setValue(LiquidBlock.LEVEL, getLegacyLevel(state));
}1
2
3
4
2
3
4
Реєстрація відер
Рідини в Minecraft зазвичай переносяться у відрах, тому подивімося, як ми можемо додати предмет для відра кислоти. Якщо ви ще цього не зробили, вам слід поглянути на те, як створити свій перший предмет.
Відкрийте свій клас ModItems і зареєструйте наступний BucketItem:
java
public static final Item ACID_BUCKET = register(
"acid_bucket",
props -> new BucketItem(ModFluids.ACID_STILL, props),
new Item.Properties()
.craftRemainder(Items.BUCKET)
.stacksTo(1)
);1
2
3
4
5
6
7
2
3
4
5
6
7
Потім змініть цей метод у AcidFluid, щоб пов’язати ваше відро з рідиною:
java
@Override
public Item getBucket() {
return ModItems.ACID_BUCKET;
}1
2
3
4
5
2
3
4
5
Не забувайте, що предмети вимагають перекладу, текстури, моделі та клієнтського предмета з назвою acid_bucket для правильного рендера. Приклад текстури наведено нижче.
Також рекомендовано додати відро вашого мода до теґу предмета ConventionalItemTags.BUCKET, щоб інші моди могли правильно його обробляти, або вручну, або через генерацію даних.
Додання до теґу вашої рідини
INFO
Користувачі генерації даних можуть забажати зареєструвати теґи через FabricTagProvider.FluidTagProvider, а не писати їх вручну.
Оскільки рідина вважається двома окремими блоками в її поточному та нерухомому станах, теґ часто використовується для перевірки обох станів разом. Ми створимо теґ рідини в data/example-mod/tags/fluid/acid.json:
json
{
"values": [
"example-mod:acid",
"example-mod:flowing_acid"
]
}1
2
3
4
5
6
2
3
4
5
6
TIP
Minecraft також надає інші теґи для контролю поведінки рідин:
- Якщо вам потрібно, щоб рідина вашого мода поводилася як вода (водяний туман, поглинається губками, плавання та уповільнення сутностей…), подумайте про те, щоб додати її до теґу рідини
minecraft:water. - Якщо вам потрібно, щоб вона поводилася як лава (лавовий туман, придатний для плавання блукачів та ґастів, уповільнює сутностей…), додайте її до теґу рідини
minecraft:lava. - Якщо вам потрібні лише деякі з цих речей, ви можете використовувати міксини, щоб точно контролювати поведінку.
Для цієї демонстрації ми також додамо теґ рідини кислоти до теґу рідини води, data/minecraft/tags/fluid/water.json.
json
{
"values": [
"#example-mod:acid"
]
}1
2
3
4
5
2
3
4
5
Текстурування вашої рідини
Щоб створити текстуру вашої рідини, вам слід використовувати FluidRenderHandlerRegistry Fabric API.
TIP
Для простоти ця демонстрація використовує BlockTintSources.constant, щоб застосувати постійний зелений відтінок до текстури стандартної води. Для подробиць про BlockTintSource, див. відтінки блока.
Додайте наступні рядки до свого ClientModInitializer, щоб створити FluidModel.Unbaked, який приймає два Material для текстур — один для нерухомого джерела та один для текучої рідини, а також відтінок кольору блока джерела, яким його потрібно відтінити.
java
FluidRenderingRegistry.register(
ModFluids.ACID_STILL,
ModFluids.ACID_FLOWING,
new FluidModel.Unbaked(
new Material(Identifier.withDefaultNamespace("block/water_still")),
new Material(Identifier.withDefaultNamespace("block/water_flow")),
new Material(Identifier.withDefaultNamespace("block/water_overlay")),
BlockTintSources.constant(0xFF075800)
)
);1
2
3
4
5
6
7
8
9
10
2
3
4
5
6
7
8
9
10
Наразі у нас є все, що нам потрібно, щоб побачити кислоту у грі! Ви можете використовувати setblock або предмет відра кислоти, щоб розмістити кислоту у світі.




















