MarsFish
本帖最后由 StarFish_ 于 2020-7-11 22:09 编辑


前言

之所以想要写这个教程,主要还是因为自己在开发Farbric Mod的时候发现官方文档更多起到的是一个抛砖引玉的效果。对于像我我这样的半吊子开发者来说,官方文档的内容还是不够详细,所以就萌生了把这个阶段摸索的东西分享出来的想法,如果其中有什么错误还请指出,不胜感激。也是希望Fabric的开发者能更多,MCBBS的开发氛围能够更好。
本篇教程主要基于1.16.1 Fabric,且MOD名为example。
Fabric的教程真的好少(小声比比
顺便:1.16必是Fabric的时代!


2021.12 数据,可能有更多内容


前言
之所以想要写这个教程,主要还是因为自己在开发Farbric Mod的时候发现官方文档更多起到的是一个抛砖引玉的效果。对于像我我这样的半吊子开发者来说,官方文档的内容还是不够详细,所以就萌生了把这个阶段摸索的东西分享出来的想法,如果其中有什么错误还请指出,不胜感激。也是希望Fabric的开发者能更多,MCBBS的开发氛围能够更好。本篇教程主要基于1.16.1 Fabric,且MOD名为example。Fabric的教程真的好少(小声比比顺便:1.16必是Fabric的时代!



1.如何在Fabric中创建一种作物


首先得明确一点,作物其实就是一个带随机刻的方块,所以我们创建一种作物也就是创建一种方块。而创建方块的第一步就是添加并注册这个方块。

代码:

  1. public class ExampleMod implements ModInitializer {
  2.   @Override
  3.   public void onInitialize() {

  4.     BlockRenderLayerMap.INSTANCE.putBlock(test_crop, RenderLayer.getCutout());

  5.     Registry.register(Registry.BLOCK, new Identifier("example", "test_crop"), test_crop);
  6.     Registry.register(Registry.ITEM, new Identifier("example", "test_crop"), new BlockItem(test_crop, new Item.Settings().group(ItemGroup.MISC).food(FoodComponents.test_crop)));

  7.   }

  8.   public static Block test_crop = new TestCrop(FabricBlockSettings.of(Material.PLANT).noCollision().ticksRandomly().breakInstantly().sounds(BlockSoundGroup.CROP));
  9. }

可以看到在添加这个方块的时候我们使用的类是TestCrop(下文会讲)。既然是作物,那总不能有碰撞体积吧,所以有noCollision()这个属性,也不能不生长吧,所以有ticksRandomly()这个属性,作物也不至于得像挖石头一样挖个好几秒吧,所以添加了breakInstantly()这个属性。而下面这行代码也是很重要的一部分,主要是将没有材质的地方透明化,如果不加这行代码,就会发生如下的惨状。

代码:

  1. BlockRenderLayerMap.INSTANCE.putBlock(test_crop, RenderLayer.getCutout());

惨状当我们添加和注册好我们的作物的时候,最重要的一个环节来了,TestCrop这个类的编写,具体可以参考(以胡萝卜为例)net.minecraft.block.CarrotsBlock这个类。我们不难写出如下代码。

代码:

  1. public class TestCrop extends CropBlock {
  2.     private static final VoxelShape[] AGE_TO_SHAPE = new VoxelShape[]{
  3.    Block.createCuboidShape(0.0D, 0.0D, 0.0D, 16.0D, 2.0D, 16.0D),
  4.    Block.createCuboidShape(0.0D, 0.0D, 0.0D, 16.0D, 3.0D, 16.0D),
  5.    Block.createCuboidShape(0.0D, 0.0D, 0.0D, 16.0D, 4.0D, 16.0D),
  6.    Block.createCuboidShape(0.0D, 0.0D, 0.0D, 16.0D, 5.0D, 16.0D),
  7.    Block.createCuboidShape(0.0D, 0.0D, 0.0D, 16.0D, 6.0D, 16.0D),
  8.    Block.createCuboidShape(0.0D, 0.0D, 0.0D, 16.0D, 7.0D, 16.0D),
  9.    Block.createCuboidShape(0.0D, 0.0D, 0.0D, 16.0D, 8.0D, 16.0D),
  10.    Block.createCuboidShape(0.0D, 0.0D, 0.0D, 16.0D, 9.0D, 16.0D)};

  11.     public TestCrop(Settings settings) {
  12.   super(settings);
  13.     }

其中我们唯一需要修改的就是这里的返回值,我们返回刚刚已经添加注册好的test_crop。

代码:

  1. @Environment(EnvType.CLIENT)
  2. protected ItemConvertible getSeedsItem() {
  3. return ExampleMod.test_crop;

接下来就是本地化这个作物,(总得给它个名字不是吗)
在resources/assets/example/lang 创建 en_us.json(简中就是zh_cn.json)并如下添加

代码:

  1. {
  2. "block.example.test_crop": "test_crop"
  3. }

再接下来我们要做的就是给这个作物添加材质。resources/assets/example/blockstates 创建 test_crop.json,并如下修改

代码:

  1. {
  2. "variants": {
  3.     "age=0": { "model": "example:block/test_crop0" },
  4.     "age=1": { "model": "example:block/test_crop1" },
  5.     "age=2": { "model": "example:block/test_crop2" },
  6.     "age=3": { "model": "example:block/test_crop3" },
  7.     "age=4": { "model": "example:block/test_crop4" },
  8.     "age=5": { "model": "example:block/test_crop5" },
  9.     "age=6": { "model": "example:block/test_crop6" },
  10.     "age=7": { "model": "example:block/test_crop7" }
  11. }
  12. }

resources/assets/example/models/block 创建 test_crop0.json,test_crop1.json ,......, test_crop7.json 并如下修改(以test_crop0.json为例)

代码:

  1. {
  2. "parent": "block/crop",
  3. "textures": {
  4.     "crop": "example:block/test_crop0"
  5. }
  6. }

resources/assets/example/models/item 创建 test_crop.json 并如下修改

代码:

  1. {
  2. "parent" : "item/generated" ,
  3. "textures" : {
  4.     "layer0" : "example:item/test_crop"
  5. }
  6. }

在resources/textures/block 里添加 test_crop0.png , test_crop1.png , ...... , test_crop7.png 这里是作物不同阶段的材质
在resources/textures/item 里添加 test_crop.png 这是作物在物品栏里、拿在手上时看到的样子
当我们做好了这一切之后,为了使得当我们破坏作物的时候会掉落物品,我们还得给它添加战利品表。
在resources/data/example/loot_tables/blocks 创建 test_crop.json 并如下修改,其中 extra 就是额外掉落的数量, probability 就是额外掉落这个事件的概率,可以根据自己的需求修改。

代码:

  1. {
  2. "type": "minecraft:block",
  3. "pools": [
  4.     {
  5.    "rolls": 1.0,
  6.    "entries": [
  7.   {
  8.     "type": "minecraft:item",
  9.     "name": "example:test_crop"
  10.   }
  11.    ]
  12.     },
  13.     {
  14.    "rolls": 1.0,
  15.    "entries": [
  16.   {
  17.     "type": "minecraft:item",
  18.     "functions": [
  19.    {
  20.   "function": "minecraft:apply_bonus",
  21.   "enchantment": "minecraft:fortune",
  22.   "formula": "minecraft:binomial_with_bonus_count",
  23.   "parameters": {
  24.     "extra": 2,
  25.     "probability": 0.5
  26.   }
  27.    }
  28.     ],
  29.     "name": "example:test_crop"
  30.   }
  31.    ],
  32.    "conditions": [
  33.   {
  34.     "condition": "minecraft:block_state_property",
  35.     "block": "example:test_crop",
  36.     "properties": {
  37.    "age": "7"
  38.     }
  39.   }
  40.    ]
  41.     }
  42. ],
  43. "functions": [
  44.     {
  45.    "function": "minecraft:explosion_decay"
  46.     }
  47. ]
  48. }

那或许有人就问了,你这个作物它可以吃吗?答案自然是可以的。
只需要新建一个类,这里我们命名为FoodComponents,并如下添加
其中hunger是回复的饥饿度,saturationModifier是回复的饱食度。具体方法可以在FoodComponent这个类中查看。

代码:

  1. public class FoodComponents {
  2.     public static final FoodComponent test_crop;
  3.     private static FoodComponent create(int hunger) {
  4.   return (new Builder()).hunger(hunger).saturationModifier(0.6F).build();
  5.     }
  6.     static {
  7.   test_crop = (new Builder()).hunger(4).saturationModifier(0.6F).build();
  8.     }
  9. }
并将主类中注册的test_crop改成

代码:

  1. Registry.register(Registry.ITEM, new Identifier("example", "test_crop"), new BlockItem(test_crop, new Item.Settings().group(ItemGroup.MISC).food(FoodComponents.test_crop)));

至此,如何关于如何在Fabric中创建作物的教程就算完成了。

2.如何在Fabric中创建一套盔甲并赋予套装效果
因为创建盔甲在官方文档中有,所以我们这就一笔带过。
首先可以先查看一下 net.minecraft.item.ArmorMaterial 这个类,这个类与盔甲的建立息息相关,其中有几个方法 


    1.名称(name),稍后会用做“护甲标签”。
    2.耐久因子(durabilityMultiplier),基础数值乘以耐久因子即为最终耐久。
    3.护甲值(armorValues),或者原版代码中的“保护点数(Protection Amounts)” ,这是个整型数组。
    4.附魔能力(EnchantAbility),代表了护甲在附魔时得到高级附魔或者多个附魔的概率。
    5.声音事件(equipSound),用在原版护甲的声音事件是SoundEvents.ITEM.EQUIP.ARMOR.X, X是护甲的类型。
    6.护甲韧性(toughness). 这是第二个保护值,遭受高伤害时护甲会更加坚韧,掉耐久少(译注:只有钻石护甲有这个参数)。
    7.修复材料(repairIngredient),这是一个 Supplier<Ingredient>实例而不是物品(Item)。
    8.基础耐久值(BASE_DURABILITY), 这里采用原版的{13, 15, 16, 11}。


然后我们可以新建一个枚举类 TestArmor 实现 ArmorMaterial。具体代码如下所示。

代码:

  1. public enum TestArmor implements ArmorMaterial {
  2.     test("test", 15, new int[]{2, 5, 6, 2}, 9, SoundEvents.ITEM_ARMOR_EQUIP_IRON, 0.0F, 0.0F, () -> {
  3.   return Ingredient.ofItems(new ItemConvertible[]{Items.IRON_INGOT});
  4.     }),;
  5.     private static final int[] BASE_DURABILITY = new int[]{13, 15, 16, 11};
  6.     private final String name;
  7.     private final int durabilityMultiplier;
  8.     private final int[] protectionAmounts;
  9.     private final int enchantability;
  10.     private final SoundEvent equipSound;
  11.     private final float toughness;
  12.     private final float knockbackResistance;
  13.     private final Lazy<Ingredient> repairIngredientSupplier;
  14.    
  15.     private TestArmor(String name, int durabilityMultiplier, int[] protectionAmounts, int enchantability, SoundEvent equipSound, float toughness, float knockbackResistance, Supplier<Ingredient> supplier) {
  16.   this.name = name;
  17.   this.durabilityMultiplier = durabilityMultiplier;
  18.   this.protectionAmounts = protectionAmounts;
  19.   this.enchantability = enchantability;
  20.   this.equipSound = equipSound;
  21.   this.toughness = toughness;
  22.   this.knockbackResistance = knockbackResistance;
  23.   this.repairIngredientSupplier = new Lazy(supplier);
  24.     }

  25.     public int getDurability(EquipmentSlot slot) {
  26.   return BASE_DURABILITY[slot.getEntitySlotId()] * this.durabilityMultiplier;
  27.     }
  28.    
  29.     public int getProtectionAmount(EquipmentSlot slot) {
  30.   return this.protectionAmounts[slot.getEntitySlotId()];
  31.     }
  32.     public int getEnchantability() {
  33.   return this.enchantability;
  34.     }
  35.     public SoundEvent getEquipSound() {
  36.   return this.equipSound;
  37.     }
  38.     public Ingredient getRepairIngredient() {
  39.   return (Ingredient)this.repairIngredientSupplier.get();
  40.     }

  41.     @Environment(EnvType.CLIENT)
  42.     public String getName() { return this.getName(); }
  43.     public float getToughness() { return this.toughness; }
  44.     public float getKnockbackResistance() { return this.knockbackResistance; }
  45. }

然后就是盔甲的添加,注册,材质,模型 的添加以及本地化。具体如下。
添加和注册

代码:

  1. public class ExampleMod implements ModInitializer {

  2.   public void onInitialize() {

  3.     Registry.register(Registry.ITEM, new Identifier("example", "test_helmet"), test_helmet);
  4.     Registry.register(Registry.ITEM, new Identifier("example", "test_chestplate"), test_chestplate);
  5.     Registry.register(Registry.ITEM, new Identifier("example", "test_leggings"), test_leggings);
  6.     Registry.register(Registry.ITEM, new Identifier("example", "test_boots"), test_boots);

  7.   }

  8.   public static final Item test_helmet = new ArmorItem(TestArmor.test, EquipmentSlot.HEAD, (new Item.Settings().group(ItemGroup.COMBAT)));
  9.   public static final Item test_chestplate = new ArmorItem(TestArmor.test, EquipmentSlot.CHEST, (new Item.Settings().group(ItemGroup.COMBAT)));
  10.   public static final Item test_leggings = new ArmorItem(TestArmor.test, EquipmentSlot.LEGS, (new Item.Settings().group(ItemGroup.COMBAT)));
  11.   public static final Item test_boots = new ArmorItem(TestArmor.test, EquipmentSlot.FEET, (new Item.Settings().group(ItemGroup.COMBAT)));

  12. }

材质与模型
resources/assets/example/models/item 添加 test_helmet.json, test_chestplate.json, test_leggings.json, test_boots.json
并做如下修改(以helmet为例)

代码:

  1. {
  2. "parent" : "item/generated" ,
  3. "textures" : {
  4.     "layer0" : "example:item/test_helmet"
  5. }
  6. }

resources/assets/textures/item 添加 test_helmet.png, test_chestplate.png, test_leggins.png, test_boots.png 这里是盔甲在物品栏、拿在手中时的材质
注意
盔甲模型添加在resources/assets/minecraft/textures/models/armor 且xx_layer_1/2.png中的 xx 就是 ArmorMaterial 中的 name 变量
其中test_layer_1.png对应的是头和胸甲,test_layer_2.png对应的是护腿和鞋子


本地化
resources/assets/land 在 en_us.json 中添加

代码:

  1. {
  2. "item.example.test_helmet": "test_helmet",
  3. "item.example.test_chestplate": "test_chestplate",
  4. "item.example.test_leggings": "test_leggings",
  5. "item.example.test_boots": "test_boots"
  6. }
至此,盔甲的创建就算完成。


接下来就是套装效果的实现。
首先看到example.mixins.json这个文件,我们对他进行如下修改,其中&quot;&quot;mixins&quot;里面的&quot;PlayerEntityMixin&quot;就是我们等会要实现套装效果的类

代码:

  1. {
  2. "required": true,
  3. "minVersion": "0.8",
  4. "package": "net.fabricmc.example.mixin",
  5. "compatibilityLevel": "JAVA_8",
  6. "mixins": [
  7.     "PlayerEntityMixin"
  8. ],
  9. "client": [
  10. ],
  11. "injectors": {
  12.     "defaultRequire": 1
  13. }
  14. }



然后在 net.fabricmc.example.mixin 下新建一个类并命名为 PlayerEntityMixin 并让这个类继承 LivingEntity 这个类

代码:

  1. @Mixin(PlayerEntity.class)
  2. public abstract class PlayerEntityMixin extends LivingEntity {
  3.     @Shadow public abstract void addExperience(int experience);

  4.     protected PlayerEntityMixin(EntityType<? extends LivingEntity> entityType, World world) {
  5.   super(entityType, world);
  6.     }
  7. }

再注入 LivingEntity 中的方法 tick ,这主要是为了下面的判定而服务

代码:

  1.     @Inject(
  2.    method = "tick",
  3.    at = @At("HEAD")
  4.     )

最后就是对玩家的装备进行读取以及判定,定义了4个临时的变量作为判定,如果要添加效果的话就在 if 语句中添加

代码:

  1.     private void tick(CallbackInfo ci) {
  2.   Item helmet = this.getEquippedStack(EquipmentSlot.HEAD).getItem();
  3.   Item chestplate = this.getEquippedStack(EquipmentSlot.CHEST).getItem();
  4.   Item leggings = this.getEquippedStack(EquipmentSlot.LEGS).getItem();
  5.   Item boots = this.getEquippedStack(EquipmentSlot.FEET).getItem();
  6.  
  7.   if (helmet.equals(ExampleMod.test_helmet) && chestplate.equals(ExampleMod.test_chestplate) && leggings.equals(ExampleMod.test_leggings) && boots.equals(ExampleMod.test_boots)) {
  8.    this.addStatusEffect(new StatusEffectInstance(StatusEffects.NIGHT_VISION, 20 * 3, 0));
  9.   }
  10.     }

例如上分的程序段就实现了判定玩家是否4个部位都穿着了test套装,如果判定通过则给与玩家 夜视I 的效果 20tick * 3 也就是 3秒


至此,套装效果的实现也就结束了



3.创建一个简单的机器(施工中)



-那位大人-
糟,我是不是被鞭尸了

MarsFish
南派董卓 发表于 2020-7-18 23:05
糟,我是不是被鞭尸了

我当时就想着选个不是原版的材质然后就看到大佬的MOD,就用了这个材质