我的世界1.12.2Forge模组开发笔记——方块

第一章 方块

1 什么是方块

方块是Minecraft世界的重要组成部分。它们构成了所有的地形、结构和机器。如果你对制作模组感兴趣,那么你可能会想添加一些方块,这篇文章将会带你了解如何添加新的方块。

1.1基本方块

对于不需要特殊功能的简单方块(比如圆石、木板等),不需要自定义类。只需实例化Block类并调用一些setter方法,就可以创建出多种不同类型的方块。例如:
setHardness      //控制破坏方块所需的时间。这是一个任意值。以供参考,石头的硬度为1.5,泥土的硬度为0.5。如果方块应该是不可破坏的,则提供了一个方便的方法setBlockUnbreakable。

setResistance   // 控制方块的爆炸抗性。这与硬度是分开的,但如果爆炸抗性低于硬度的5倍,则setHardness也会将爆炸抗性设置为硬度的5倍。

setSoundType   // 控制当方块被击打、破坏或放置时发出的声音。需要一个SoundType参数

setLightLevel  // 控制方块的发光度。注意:此方法接受的值是从0到1,而不是从0到15。要计算此值,请取你希望方块发出的光亮度并除以16。例如,一个发出5级光的方块应该向此方法传递5 / 16f。

setLightOpacity // 控制光穿过此方块时的减弱量。与setLightLevel不同,此值的范围是0到15。例如,将此值设置为3将每次光线穿过此类方块时降低3级亮度。

setUnlocalizedName // 主要是自解释的,设置方块的非本地化名称。出于本地化目的,这个名称前面会添加“tile.”,后面会添加“.name”。例如,setUnlocalizedName("foo") 会导致该方块的实际本地化键为“tile.foo.name”。对于更高级的本地化控制,需要自定义项目。

setCreativeTab // 控制这个方块将属于哪个创造模式物品栏。如果方块应显示在创造模式物品栏中,则必须调用此方法。可以在 CreativeTabs 类中找到物品栏选项。

所有这些方法都是可链接的,这意味着您可以连续调用它们。

1.2高级方块

以上内容仅允许使用极其基础的方块。如果您想添加功能,如玩家交互,则需要自定义类。

2 注册方块

方块必须先注册才可以使用。

2.1方块的特性

(1) 世界中的方块

世界中的方块IBlockState表示,其行为由Block的实例定义。

(2) 物品栏中的方块

库存中的物品是ItemStack,由Item控制。

(3) ItemBlock类

作为Block和Item两个不同世界之间的桥梁,存在ItemBlock类。ItemBlock是Item的子类,它有一个名为block的字段,该字段保存了对它所表示的Block的引用。ItemBlock定义了一些“方块”作为物品时的行为,比如右键单击会放置方块。有可能存在一个Block而没有ItemBlock。(例如,minecraft:water存在方块,但没有物品。因此,无法将其作为一个物品放入库存中。)

(4) IProperty

每个数据块都有一组零个或多个这样的对象,毫不奇怪,它们描述了数据块的属性。这方面的例子包括颜色(IProperty)、朝向(IProperty)、整数和布尔值等。每个属性都可以有由IProperty参数化的类型的值。

例如,对于相应的示例属性,我们可以有值EnumDyeColor.WHITE、EnumFacing.EAST、1或false。

现在,与其使用“minecraft:stone_button meta 9”,我们可以使用“minecraft:stone_button[facing=east,powered=true]”。猜猜哪个更有意义?我们对这些三元组有一个非常特殊的名字——它们被称为IBlockState。

在你的方块类中,为你的方块所具有的每一个属性创建static final IProperty<> 对象。Vanilla 提供了几种方便的实现方式:

  PropertyInteger   //实现了 IProperty<Integer>。通过调用 PropertyInteger.create(“name”, minValue, maxValue) 创建。

  PropertyBool     //实现了 IProperty<Boolean>。通过调用 PropertyBool.create(“name”) 创建。

  PropertyEnum<E extends Enum<>>   //实现了 IProperty<E>,定义了一个属性,该属性可以接受一个枚举类的值。通过调用 PropertyEnum.create(“name”, EnumClass.class) 创建。
        //你也可以只使用枚举值的一个子集(例如,要获取一个表示基本方向的属性,通过调用PropertyDirection.create("<name>", EnumFacing.Plane.HORIZONTAL)获取方向)
        //您可以自由创建自己的 IProperty<> 实现

如果你的模组有一个API或者意味着可以从其他模组与之交互,那么强烈建议你将你的IProperty(以及任何用作值的类)放在你的API中。这样,人们就可以使用属性和值来设置你的世界中的方块,而不是像以前那样忍受任意的数字。

创建完IProperty<>对象后,请在您的Block类中覆盖createBlockState方法。在该方法中,只需编写返回new BlockState()的代码。首先,将BlockState的构造函数传递给您的Block(即this),然后跟随您想要声明的每个IProperty

您可以通过调用getValue(来获取属性的值,并将您想要测试的IProperty<>传递给它。

如果您想获取具有不同值集的IBlockState,只需如上所述调用withProperty("<"PROPERTY">","<"NEW_VALUE">")。这将返回另一个具有您请求值的预生成的IBlockState

您可以使用setBlockState()getBlockState()在世界中获取和放置IBlockState

第二章 创建方块

1 方块类

所有方块的类型都是net.minecraft.block.Block类的实例。所以也需要继承这个类,实现自己的方块。

1.1 创建方块类

新建一个fktg.test.block(fktg.test是自定义的路径)的子包,然后在新建的子包下新建一个BlockExample类(例子方块类)

(1) 新建包


(2) 新建类

在类中写入以下代码,注意类名要与文件名一致
package fktg.test.block;

import fktg.test.FKTGTest;
import net.minecraft.block.Block;
import net.minecraft.block.material.Material;

public class BlockExample extends Block {
    public BlockExample() {
        super(Material.GOURD);
        this.setUnlocalizedName(FKTGTest.MODID + ".example");
        this.setRegistryName("example");
        this.setHarvestLevel("shovel",0);  //挖掘工具(pickaxe、shovel、axe等)和挖掘等级(0是木质工具)
        this.setHardness(0.5F);  //方块的硬度,即挖掘所需的时间和防爆强度
    }
}

(3) 方块类代码

1.2 注册方块

新建一个fktg.test.block.BlockRegistryHandler类,并填写代码

(1) 新建BlockRegistryHandler类

将以下代码写入BlockRegisterHandler类中,注意class文件格式:
package fktg.test.block;


import net.minecraft.block.Block;
import net.minecraftforge.event.RegistryEvent.Register;
import net.minecraftforge.fml.common.Mod;
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
import net.minecraftforge.registries.IForgeRegistry;

@Mod.EventBusSubscriber
public class BlockRegistryHandler {
    public static final BlockExample Block_Example = new BlockExample();

    @SubscribeEvent
    public static void onRegistry(Register<Block> event) //方块注册器
    {
        IForgeRegistry<Block> registry = event.getRegistry();
        registry.register(Block_Example);
    }
}

(2) BlockRegistryHandler类代码

1.3 注册方块对应的物品

对绝大多数方块来说,它们都有对应的物品,这类物品在右键一个方块时,会在该方块旁边被放置,其对应的类就是net.minecraft.item.ItemBlock类

首先新建一个fktg.test.item包(有就不用新建),然后在该包中新建一个ItemRegistryHandler类,写入相应代码

(1) 新建fktg.test.item包

随后新建ItemRegistryHandler类

(2) 新建ItemRegistryHandler类

将以下代码写入ItemRegistryHandler类:
package fktg.test.item;

import fktg.test.block.BlockRegistryHandler;
import net.minecraft.client.renderer.block.model.ModelResourceLocation;
import net.minecraft.item.Item;
import net.minecraft.item.ItemBlock;
import net.minecraftforge.client.event.ModelRegistryEvent;
import net.minecraftforge.client.model.ModelLoader;
import net.minecraftforge.event.RegistryEvent;
import net.minecraftforge.fml.common.Mod;
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
import net.minecraftforge.fml.relauncher.Side;
import net.minecraftforge.fml.relauncher.SideOnly;
import net.minecraftforge.registries.IForgeRegistry;

@Mod.EventBusSubscriber
public class ItemRegistryHandler {
  public static final ItemBlock ITEM_EXAMPLE = withRegistryName(new ItemBlock(BlockRegistryHandler.Block_Example));

  @SubscribeEvent
  public static void onRegistry(RegistryEvent.Register<Item> event) //注册物品
  {
      IForgeRegistry<Item> registry = event.getRegistry();
      //ITEM_EXAMPLE.setRegistryName(ITEM_EXAMPLE.getBlock().getRegistryName());   //为了防止重复造轮子,将重复的部分封装成了方法
      registry.register(ITEM_EXAMPLE);
  }

  @SubscribeEvent
  @SideOnly(Side.CLIENT)
  public static void onModelRegistry(ModelRegistryEvent event)  //注册物品材质
  {
      registerModel(ITEM_EXAMPLE);
  }

  private static ItemBlock withRegistryName(ItemBlock item)
  {
      item.setRegistryName(item.getBlock().getRegistryName());
      return item;
  }

  @SideOnly(Side.CLIENT)
  private static void registerModel(Item item)
  {
      ModelResourceLocation modelResourceLocation = new ModelResourceLocation(item.getRegistryName(),
              "inventory");
      ModelLoader.setCustomModelResourceLocation(item, 0, modelResourceLocation);
  }
}

(3) ItemRegistryHandler类代码

如果以上步骤没问题,那么你就可以在游戏中使用看到紫红色光芒的物品
如果你不会启动游戏,可以去看看我之前的笔记一切的开始
打开游戏,输入:
/give @a test:example (注意:test是模组id,example是setRegistryName方法注册的名称)

(4) 游戏中该方块的物品

将获取到的物品右键放置在地面,就会得到紫红色的方块

(5) 游戏中的该方块

1.4 语言文件和方块材质

语言文件能够让方块在游戏中的名称标准化,方块的前缀为title

首先在resources目录中新建一个assets.test.lang包

(1) 新建lang包

然后在该包下新建一个en_us.lang的File文件,在该文件中写入:
tile.test.example.name=example

(2) en_us.lang内容

之后在该包下新建一个zh_cn.lang的File文件,在该文件中写入:
tile.test.example.name=样例

(3) zh_cn.lang内容

然后在resources目录中新建一个assets.test.blockstates包

(4) 创建blockstates包

在blockstates包中创建example.json文件

(5) 创建example.json文件

在其中写入以下内容:
{
  "forge_marker": 1,
  "defaults": {
    "model": "minecraft:cube_all",
    "textures": {"all": "test:blocks/example"}
  },
  "variants": {
    "normal": [{}],
    "inventory": [{"transform": "forge:default-block"}]
  }
}

(6) example.json文件内容

然后在resources目录中创建assets.test.textures.blocks包,准备将材质图片放进去

(7) 创建blocks包

在作图软件里制作一张材质图,要求大小必须是2的n次方

(8) 制作方块材质

最后将图存放进blocks目录中,注意图片名称为example.png

(9) assets目录

打开游戏查看材质和名字是否正确 目前为止,添加一个自己的方块已经完成了

第三章 修改方块的属性

1 方块的方法

public Block setUnlocalizedName(String name)    //设置本地名称 
        //this.setUnlocalizedName(FKTGTest.MODID + ".example");
public final T setRegistryName(String name)     //设置名称     
        //this.setRegistryName("example");
public void setHarvestLevel(String toolClass, int level)     //设置挖掘工具和挖掘等级
        //this.setHarvestLevel("shovel",0);
        //shovel - 铲  axe - 斧 pickaxe - 镐  sword - 剑  hoe - 锄
        // 0-木、金  1-石  2-铁  3-钻石
public Block setHardness(float hardness)        //设置方块的硬度、防爆等级
        //this.setHardness(0.5F);
        // 泥土-0.5  
public Block setBlockUnbreakable()             //设置方块为不可破坏
       //this.setBlockUnbreakable();
protected Block setSoundType(SoundType sound)   //设置方块音效类型
        // this.setSoundType(SoundType.STONE);
        // SoundType在Minecraft的代码中有 WOOD、GROUND、PLANT、STONE、METAL、GLASS、CLOTH、SAND、SNOW、LADDER、ANVIL、SLIME
public Block setLightLevel(float value)     //控制方块的发光度
        //this.setLightLevel(2/16F);
        //注意:此方法接受的值是从0到1,而不是从0到15。要计算此值,请取你希望方块发出的光亮度并除以16。
        //例如,一个发出5级光的方块应该向此方法传递5 / 16f。
public Block setLightOpacity(int opacity)    //控制光穿过此方块时的减弱量
        //this.setLightOpacity(3);
        //与setLightLevel不同,此值的范围是0到15。例如,将此值设置为3将每次光线穿过此类方块时降低3级亮度。
public Block setCreativeTab(CreativeTabs tab)   //控制这个方块将属于哪个创造模式物品栏
        //setCreativeTab(CreativeTabs.BUILDING_BLOCKS);
        //物品栏有: BUILDING_BLOCKS(建筑方块)、DECORATIONS(装饰方块)、REDSTONE(红石)、TRANSPORTATION(交通)、MISC(杂项)、SEARCH(搜索栏)、FOOD(食物)、TOOLS(工具)、COMBAT(武器)、BREWING(药水)、HOTBAR(玩家底部物品槽)
        //物品栏也可以进行自定义
public Block setResistance(float resistance)   //设定方块的爆炸抗性
        //this.setResistance(10.0f);
        //如果一个方块的爆炸阻力为 0,则它会在任何爆炸中被直接破坏。
        //常见方块的爆炸阻力:草块(0.6)、木板(2.0)、木头(2.0)、石头(3.0)、铁块(30)、黑曜石(6000)
protected final void setDefaultState(IBlockState state)  //设置方块的默认状态
        /*import net.minecraft.block.Block;
        import net.minecraft.block.material.Material;
        import net.minecraft.block.properties.IProperty;
        import net.minecraft.block.properties.PropertyBool;
        import net.minecraft.block.state.BlockStateContainer;
        import net.minecraft.block.state.IBlockState;
        import net.minecraftforge.fml.common.registry.GameRegistry;
        public class MyCustomBlock extends Block {
            // 定义一个布尔属性,表示方块是否处于“激活”状态
            public static final PropertyBool ACTIVE = PropertyBool.create("active");
            public MyCustomBlock() {
                super(Material.ROCK); // 设置材料类型为石头
                setUnlocalizedName("my_custom_block"); // 设置方块的名称
                setHardness(5.0f); // 设置方块的硬度
                setResistance(10.0f); // 设置方块的爆炸阻力

                 //设置默认状态为未激活状态
                this.setDefaultState(this.blockState.getBaseState().withProperty(ACTIVE, Boolean.FALSE));
            }

            @Override
            protected BlockStateContainer createBlockState() {
                return new BlockStateContainer(this, new IProperty[]{ACTIVE});
            }

            @Override
            public IBlockState getStateFromMeta(int meta) {
                return this.getDefaultState().withProperty(ACTIVE, (meta & 1) == 1);
            }

            @Override
            public int getMetaFromState(IBlockState state) {
                return state.getValue(ACTIVE) ? 1 : 0;
            }

            public static void registerBlock() {
                GameRegistry.registerBlock(new MyCustomBlock(), "my_custom_block");
            }
        }*/

public boolean removedByPlayer(IBlockState state, World world, BlockPos pos, EntityPlayer player, boolean willHarvest)   //处理当玩家移除方块时的行为
  /*@Override
    public void removedByPlayer(World worldIn, BlockPos pos, IBlockState state, EntityPlayer player, boolean willHarvest) {
        super.removedByPlayer(worldIn, pos, state, player, willHarvest);
        
        // 在方块被移除时执行特定逻辑
        if (!worldIn.isRemote) {
            // 逻辑,比如播放音效
            worldIn.playSound(null, pos, SoundEvents.BLOCK_STONE_BREAK, SoundCategory.BLOCKS, 1.0F, 1.0F);
            
            // 可能执行其他操作,例如掉落物品
            // ItemStack stack = new ItemStack(Items.DIAMOND, 1);
            // spawnAsEntity(worldIn, pos, stack);
        }
    }*/

public void updateTick(World worldIn, BlockPos pos, IBlockState state, Random rand)  //用于处理方块的定期更新逻辑

 //public class MyCustomBlock extends Block {

   // public MyCustomBlock() {
   //     super(Material.ROCK);
   //     setUnlocalizedName("my_custom_block");
   //    setTickRandomly(true); // 启用随机更新
   // }

    //@Override
   // public void updateTick(World worldIn, BlockPos pos, IBlockState state, Random rand) {
        // 确保代码只在服务端执行
   //     if (!worldIn.isRemote) {
   //         // 生成一个随机数
   //         if (rand.nextInt(10) == 0) { // 10% 的几率
   //             // 将当前方块变为金块
   //             worldIn.setBlockState(pos, Blocks.GOLD_BLOCK.getDefaultState());
   //         }
   //     }
   // }
//}


public Item getItemDropped(IBlockState state, Random rand, int fortune)  //用于定义当方块被破坏时掉落的物品

@Override
public Item getItemDropped(IBlockState state, Random rand, int fortune) {
        return Items.DIAMOND;   //设置掉落物为钻石
    }


public int quantityDropped(Random random) //定义方块被破坏时掉落的物品数量

@Override
public int quantityDropped(Random random) {
    return 1 + random.nextInt(3); // 随机掉落1到3个物品
}

public int quantityDroppedWithBonus(int fortune, Random random)  //根据玩家使用的工具的时运(Fortune)效果掉落的物品数量

@Override
public int quantityDroppedWithBonus(int fortune, Random random) {
        int baseDrop = this.quantityDropped(random);  // 获取基础掉落数量
        if (fortune > 0) {
            int extraDrops = random.nextInt(fortune + 2); // 根据时运等级随机计算额外掉落
            return baseDrop + extraDrops;  // 返回基础掉落加上额外掉落
        } else {
            return baseDrop;  // 如果没有时运,直接返回基础掉落
        }
    }

public int damageDropped(IBlockState state) //控制方块掉落物品的具体子类型,特别是当方块有多种变体或状态时

@Override
public int damageDropped(IBlockState state) {
        // 获取方块的颜色属性,并返回对应的染料损伤值
        return state.getValue(COLOR_PROPERTY).getMetadata();
    }

public void getDrops(NonNullList<ItemStack> drops, IBlockAccess world, BlockPos pos, IBlockState state, int fortune) //让一个方块在被破坏时掉落多种物品,或者掉落多个物品

@Override
public void getDrops(NonNullList<ItemStack> drops, IBlockAccess world, BlockPos pos, IBlockState state, int fortune) {
        super.getDrops(drops, world, pos, state, fortune); // 调用父类方法,通常用于初始化drops列表
        drops.add(new ItemStack(Item.getItemFromBlock(this), 3, 0)); // 添加3个钻石
        drops.add(new ItemStack(Item.getByNameOrId("minecraft:redstone"), 5)); // 添加5个红石
    }

1.2 动手实现

下面的例子利用了getDrops方法使方块拥有多个掉落物
package fktg.testmod.block;

import fktg.testmod.TestMod;
import net.minecraft.block.Block;
import net.minecraft.block.material.Material;
import net.minecraft.block.state.IBlockState;
import net.minecraft.creativetab.CreativeTabs;
import net.minecraft.item.Item;
import net.minecraft.item.ItemStack;
import net.minecraft.util.NonNullList;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.IBlockAccess;

public class BlockGoldWall extends Block {
    public BlockGoldWall() {
        super(Material.GOURD);
        this.setUnlocalizedName(TestMod.MODID + ".goldWall");
        this.setRegistryName("gold_wall");
        this.setHarvestLevel("shovel",0);  //挖掘工具(pickaxe、shovel、axe等)和挖掘等级(0是木质工具)
        this.setHardness(0.5F);  //方块的硬度,即挖掘所需的时间和防爆强度
        this.setCreativeTab(CreativeTabs.BUILDING_BLOCKS);
    }

    @Override
    public Item getItemDropped(IBlockState state, Random rand, int fortune) {
        return null;   //设置本身不会掉落
    }

    @Override
    public void getDrops(NonNullList<ItemStack> drops, IBlockAccess world, BlockPos pos, IBlockState state, int fortune) {
        super.getDrops(drops, world, pos, state, fortune); // 调用父类方法,通常用于初始化drops列表
        drops.add(new ItemStack(Item.getByNameOrId("minecraft:gold_ingot"), 1)); // 添加1个金锭
        drops.add(new ItemStack(Item.getByNameOrId("minecraft:cobblestone"), 8)); // 添加8个圆石
    }
}


posted @ 2024-08-29 00:05  方块特工  阅读(475)  评论(0)    收藏  举报