制作一个物品一共分几步
制作一个物品一共分三步:
- 创建一个物品
- 实例化并注册这个物品
- 为这个物品添加模型和材质
Minecraft中,使用八个金锭和一个苹果可以创造一个金苹果,那我们是不是可以创造一个金蛋呢,这一次教程会一步一步地带着你完成制作新物品的全过程。
创建一个物品
如果读者翻看了net.minecraft.item
包,想必就会发现你在Minecraft中遇到的各种物品,都继承了Item
类,那很明显,我们制作的物品也要继承这个类。
新建一个包com.github.ustc_zzzz.fmltutor.item
,在其中创建一个类ItemGoldenEgg
:
src/main/java/com/github/ustc_zzzz/fmltutor/item/ItemGoldenEgg.java:
package com.github.ustc_zzzz.fmltutor.item;
import net.minecraft.item.Item;
public class ItemGoldenEgg extends Item
{
public ItemGoldenEgg()
{
super();
this.setUnlocalizedName("goldenEgg");
}
}
这里的setUnlocalizedName
方法为该物品添加了一个非本地化的名称,该名称为“item.
”+设置的名称,比如这里就是item.goldenEgg
,这个非本地化名称,与本地化和国际化有关,在后面的部分我们会讲到。非本地化名称尽量使用小写驼峰式写法,即第一个词以小写字母开始,第二个词开始首字母大写,中间不使用任何符号分隔。
实例化并注册这个物品
在CommonProxy
类中添加下面的代码:
src/main/java/com/github/ustc_zzzz/fmltutor/common/CommonProxy.java(部分):
public void preInit(FMLPreInitializationEvent event)
{
new ItemLoader(event);
}
新建一个类ItemLoader
:
src/main/java/com/github/ustc_zzzz/fmltutor/item/ItemLoader.java:
package com.github.ustc_zzzz.fmltutor.item;
import net.minecraft.item.Item;
import net.minecraftforge.fml.common.event.FMLPreInitializationEvent;
import net.minecraftforge.fml.common.registry.GameRegistry;
public class ItemLoader
{
public static Item goldenEgg = new ItemGoldenEgg();
public ItemLoader(FMLPreInitializationEvent event)
{
register(goldenEgg, "golden_egg");
}
private static void register(Item item, String name)
{
GameRegistry.registerItem(item.setRegistryName(name));
}
}
新建一个类进行初始化的原因是当你的物品越来越多,如果所有的注册过程都直接在CommonProxy
类中进行,随着注册的对象越来越多,这些注册的对象会越来越难以管理。换句话说,这体现了代码模块化的原则。
首先,我们要实例化这个物品:
src/main/java/com/github/ustc_zzzz/fmltutor/item/ItemLoader.java(部分):
public static Item goldenEgg = new ItemGoldenEgg();
然后,我们来到了这块的重点,注册这个物品:
src/main/java/com/github/ustc_zzzz/fmltutor/item/ItemLoader.java(部分):
GameRegistry.registerItem(item.setRegistryName(name));
GameRegistry
是Forge提供的一个用来注册物品、方块、合成表、烧炼规则等各种常见内容的类,比如下面的用于注册的方法我们在后面都会遇到并加以讲解:
registerBlock
方法用于注册方块registerFuelHandler
方法用于注册燃料registerItem
方法用于注册物品registerTileEntity
方法用于注册TileEntity(后面会讲到什么是TileEntity)registerWorldGenerator
方法用于注册世界生成器以生成不同的世界addRecipe
方法和addShapedRecipe
方法用于注册合成表addSmelting
方法用于注册物品烧炼规则
这个方法需要传入一个Item
类的实例用于注册物品,那么如何指定这个物品的id呢?在示例中,我们通过调用物品的setRegistryName
方法指定了物品的id,这也是从1.8.9开始指定物品id的通用做法。
有一点需要注意,从Minecraft的1.9及以上版本开始,GameRegistry
类中相应的方法名被修改为了register
方法,因此注册物品时需要用到的代码会有一点微小的变动。
我们这里通过参数提供物品的id。id请尽量使用小写字母加下划线,并且同一个Mod下的物品id不能相同,有的Mod会使用驼峰式,这样的好处是把物品的非本地化名称和物品id设置成相同的,但是我们不推荐这样的做法。
现在不管是在服务端,还是在客户端,Forge都会在preInit
阶段,运行到ItemLoader
类中的构造函数,也就是实例化一个物品,并注册它。
现在运行客户端,运行命令:
/give @a fmltutor:golden_egg
玩家的手上便多了一个新的物品。
为这个物品添加模型和材质
可以看到,你手上的物品,现在还什么都没有,只是一个两种颜色交替的方块,这是因为你没有添加模型和材质。模型的作用是保证你手里的物品是一个扁平的长方体,而材质的作用,就是给这个长方体上色。
首先,新建一个文件夹:src/main/resources/assets/fmltutor/models/item
,并在其中新建一个文件:golden_egg.json
:
src/main/resources/assets/fmltutor/models/item/golden_egg.json:
{
"parent": "builtin/generated",
"textures": {
"layer0": "fmltutor:items/golden_egg"
},
"display": {
"thirdperson": {
"rotation": [ -90, 0, 0 ],
"translation": [ 0, 1, -2 ],
"scale": [ 0.55, 0.55, 0.55 ]
},
"firstperson": {
"rotation": [ 0, -135, 25 ],
"translation": [ 0, 4, 2 ],
"scale": [ 1.7, 1.7, 1.7 ]
}
}
}
当然,这里的fmltutor
就是Mod id,golden_egg
就是你的物品id。
这个json的文件,就是这个物品的模型,这个文件的内容解释起来极其复杂,这也不是教程负责的介绍内容,直接抄下来就好了,但是有的地方是显而易见的:
src/main/resources/assets/fmltutor/models/item/golden_egg.json(部分):
"textures": {
"layer0": "fmltutor:items/golden_egg"
},
这一部分告诉我们的是这个物品材质的位置,也就是fmltutor:items/golden_egg
,很明显,我们需要建立一个材质文件。这里使用的是16x16的材质文件(当然Minecraft也支持尺寸更大如32x32的材质文件,不过建议还是使用16x16的),新建文件夹src/main/resources/assets/fmltutor/textures/items
,把制作完成的goldenegg.png放入(其实是我照着鸡蛋的原图嗐改的==||):
src/main/resources/assets/fmltutor/textures/items/golden_egg.png:
所有模型和材质都准备好了,现在需要做的,就是让Minecraft知道你准备的模型和材质了。
修改ItemLoader
类的内容:
src/main/java/com/github/ustc_zzzz/fmltutor/item/ItemLoader.java:
package com.github.ustc_zzzz.fmltutor.item;
import net.minecraft.client.resources.model.ModelResourceLocation;
import net.minecraft.item.Item;
import net.minecraftforge.client.model.ModelLoader;
import net.minecraftforge.fml.common.event.FMLPreInitializationEvent;
import net.minecraftforge.fml.common.registry.GameRegistry;
import net.minecraftforge.fml.relauncher.Side;
import net.minecraftforge.fml.relauncher.SideOnly;
public class ItemLoader
{
public static Item goldenEgg = new ItemGoldenEgg();
public ItemLoader(FMLPreInitializationEvent event)
{
register(goldenEgg, "golden_egg");
}
@SideOnly(Side.CLIENT)
public static void registerRenders()
{
registerRender(goldenEgg);
}
private static void register(Item item, String name)
{
GameRegistry.registerItem(item.setRegistryName(name));
}
@SideOnly(Side.CLIENT)
private static void registerRender(Item item)
{
ModelResourceLocation model = new ModelResourceLocation(item.getRegistryName(), "inventory");
ModelLoader.setCustomModelResourceLocation(item, 0, model);
}
}
我们来看这一段:
src/main/java/com/github/ustc_zzzz/fmltutor/item/ItemLoader.java(部分):
@SideOnly(Side.CLIENT)
private static void registerRender(Item item)
{
ModelResourceLocation model = new ModelResourceLocation(item.getRegistryName(), "inventory");
ModelLoader.setCustomModelResourceLocation(item, 0, model);
}
Forge提供了ModelLoader
类用于加载和处理模型,其setCustomModelResourceLocation
方法有三个参数:
- 第一个参数是要被注册的物品。
- 第二个参数是这个物品的Metadata。Metadata是一个用于区分同一个物品或方块的不同状态的数据,比如钟表的十六种状态、羊毛的十六种颜色,在3.2.1节会讲到Metadata,默认为零就好了。
- 第三个参数就是这个物品模型的资源位置了,资源位置是类
ModelResourceLocation
的一个实例,它用于描述一个模型,在后面我们还会比较常用到这个类的。
ModelResourceLocation
被用于标注模型的位置,通常为由冒号(:
)和井号(#
)分隔的三个字符串组成,对于我们这里构造的ModelResourceLocation
,它的一部分通过调用物品的getRegistryName
方法得到,第二部分由我们指定,为inventory
,是一个固定的字符串,代表作为一个物品的渲染模型。
在这里,第一部分为fmltutor:golden_egg
,第二部分为inventory
,组合后的ModelResourceLocation
就是fmltutor:golden_egg#inventory
。Minecraft便会去相应的目录下寻找相应的资源:
fmltutor
指示游戏应该在assets.fmltutor
包下找到这个资源inventory
指示游戏应该在assets.fmltutor.models.item
包下找到这个资源golden_egg
指示这个资源就是assets.fmltutor.models.item.golden_egg.json
,对应到源代码,就是src/main/resources/assets/fmltutor/models/item/golden_egg.json
这一文件
当然,上面已经提到了,golden_egg.json
模型文件里已经包含了材质的信息。
@SideOnly
注解的作用是注解这一方法、类等只作用于客户端或服务端。很明显,对于模型和材质的操作只会在客户端执行(实际上如果在服务端执行会出错),所以我们同时要在ClientProxy
的preInit
阶段中初始化:
src/main/java/com/github/ustc_zzzz/fmltutor/client/ClientProxy.java:
package com.github.ustc_zzzz.fmltutor.client;
import com.github.ustc_zzzz.fmltutor.common.CommonProxy;
import net.minecraftforge.fml.common.event.FMLInitializationEvent;
import net.minecraftforge.fml.common.event.FMLPostInitializationEvent;
import net.minecraftforge.fml.common.event.FMLPreInitializationEvent;
public class ClientProxy extends CommonProxy
{
@Override
public void preInit(FMLPreInitializationEvent event)
{
super.preInit(event);
new ItemRenderLoader();
}
@Override
public void init(FMLInitializationEvent event)
{
super.init(event);
}
@Override
public void postInit(FMLPostInitializationEvent event)
{
super.postInit(event);
}
}
在com.github.ustc_zzzz.fmltutor.client
下新建ItemRenderLoader
类:
src/main/java/com/github/ustc_zzzz/fmltutor/client/ItemRenderLoader.java:
package com.github.ustc_zzzz.fmltutor.client;
import com.github.ustc_zzzz.fmltutor.item.ItemLoader;
public class ItemRenderLoader
{
public ItemRenderLoader()
{
ItemLoader.registerRenders();
}
}
现在在客户端,Forge会在preInit
阶段,运行到ItemRenderLoader
类的构造函数,进而运行到ItemLoader
类中的registerRenders
方法中的代码,也就是注册这个物品的渲染,而在服务端则不会运行。
现在运行游戏,你的手上是不是有了一个金色的蛋啦~
最后说一句,把所有只在客户端执行的代码放到同一个client
文件夹下是一个好的习惯。