MCBBS Wiki欢迎您共同参与编辑!在参与编辑之前请先阅读Wiki方针。
如果在编辑的过程中遇到了什么问题,可以去讨论板提问。
为了您能够无阻碍地参与编辑 未验证/绑定过邮箱的用户,请尽快绑定/验证。
MCBBS Wiki GitHub群组已上线!
您可以在回声洞中发表吐槽!
服务器状态监控。点击进入
本站由MCBBS用户自行搭建,与MCBBS及东银河系漫游指南(北京)科技有限公司没有从属关系。点此了解 MCBBS Wiki 不是什么>>
用户:MashKJo/1.12.2模组开发笔记/IRecipe接口详解:修订间差异
(未显示同一用户的1个中间版本) | |||
第52行: | 第52行: | ||
== 注册IRecipe == |
== 注册IRecipe == |
||
是的,IRecipe需要注册——而且走的是Forge的注册表系统。因此: |
是的,IRecipe需要注册——而且走的是Forge的注册表系统。因此: |
||
public class MyCustomRecipe implements IRecipe |
|||
{ |
|||
private ResourceLocation regName; |
|||
... |
|||
@Override |
|||
public IRecipe setRegistryName(ResourceLocation name) |
|||
{ |
|||
this.regName = name; |
|||
return this; |
|||
} |
|||
@Override |
|||
public ResourceLocation getRegistryName() |
|||
{ |
|||
return this.regName; |
|||
} |
|||
... |
|||
} |
|||
@Mod.EventBusSubscriber(modid = [Your MODID]) |
@Mod.EventBusSubscriber(modid = [Your MODID]) |
||
public class RecipeLoader |
public class RecipeLoader |
||
{ |
{ |
||
@SubscribeEvent |
@SubscribeEvent |
||
public void register(RegistryEvent.Register<IRecipe> event) |
public static void register(RegistryEvent.Register<IRecipe> event) |
||
{ |
{ |
||
event.getRegistry().register([Your IRecipe Instance]); |
event.getRegistry().register([Your IRecipe Instance].setRegistryName(new ResourceLocation(...))); |
||
} |
} |
||
} |
} |
2024年10月24日 (四) 15:44的最新版本
几乎所有1.12.2的模组开发教程都会介绍新的自定义工作台配方(广义)的添加方法——只需要在resources/assets/modid/recipes
路径下撰写一些.json文件就好了。实际上这说明这块内容确实很简单——因为大部分教程别说讲到方块实体了,能讲到附魔、药水效果这种比物品、方块稍微难一点的东西就算是谢天谢地了。毕竟照着规定的格式写.json文件谁不会呢?
不过你有没有想过——原版当中某些配方似乎并不是简单地通过撰写.json文件来实现的。比如你可以将两把不是满耐久的钻石镐叠加,合成为一把新的钻石镐,其耐久结合了合成前那两把钻石镐的耐久[1]。这种配方的实现显然并非依靠.json文件吧?
这就要请出我们今天的主角了——IRecipe接口(net.minecraft.item.crafting.IRecipe
)。
IRecipe接口中的方法
boolean matches(InventoryCrafting inv, World worldIn)
这个方法用于判断合成原料是否“匹配上了”——换言之,就是判断玩家放入合成界面的ItemStack及它们的摆放方式是否可以合成一个新ItemStack。例如,在原版的有序合成定义的逻辑中,木棍上面放煤炭或木炭,就是“匹配上了”——这样摆放就可以合成火把。但是,如果在把木棍换成木板——那不好意思,匹配不上,那就合成不了任何东西[2]。
这个方法传入了两个参数:第一个是InventoryCrafting类型的,你可以把这个类理解为“合成容器”,原版MC中合成容器有两种:一是工作台中3×3大小的合成容器,二是玩家背包里的2×2大小的合成容器;你可以从该参数中拿到合成容器的长、宽,以及该容器中放入的ItemStack。第二个参数是World类型的,代表玩家当前所处的world实例,这没什么好说的。
原版的ShapedRecipes和ShapelessRecipes类对于该方法的实现写的比较抽象,不过既然我们都要重新实现IRecipe了,不妨把这两个类抛开,把目光着眼于RecipeRepairItem这个类——看看这个类是怎么实现的该方法。
具体的代码实现,读者可以自行去查看,由于Minecraft EULA的限制,笔者就不在这里直接贴代码了,只说说大体的实现思路:
大致思路是:先创建了一个List<ItemStack>集合。再通过inv.getSizeInventory()
拿到了放入合成容器中的ItemStack数量,再通过一个for循环把合成容器中的ItemStack添加到新定义的集合中去。这个时候读者可能就会想:判断一下该集合的长度是否等于2,不就可以返回boolean值了?是这样没错,不过我们还得加点判断——修复工具的这个配方中,工具种类总得相同吧!所以得加个ItemStack对应的Item两两之间是否相等的判断。因为Item对象是基于享元模式的,因此直接用==
判断即可。
ItemStack getCraftingResult(InventoryCrafting inv)
这个方法用于获取合成配方的输出(outputs)——同样是一个ItemStack对象。
同时我们注意到这个方法传入的参数只有一个InventoryCrafting inv
。我们得再通过和上一部分相同的方法把合成容器中的ItemStack信息转移到一个集合中——这里算是MC一个小小的设计失误之处吧。或者你也可以写一个private方法,以把从InventoryCrafting对象中提取ItemStack信息并存储到集合中的代码封装起来,以便多次调用。
boolean canFit(int width, int height)
这是一个用于检验合成容器“装不装得下”该合成表的方法,width和height这两个参数代表合成表的长和宽,读者只需根据自己的需求,对这两个参数做个判断就好了。
读者可能会有疑问:这一部分的判断放到前面的matches方法中不行吗?当然不行。因为那个方法适用于检验合成容器中的ItemStack本身是否满足该IRecipe的合成条件;如果满足了,再进行canFit这个方法的检验。即——就算这些ItemStack可以合成出新的ItemStack,你合成容器能不能装得下这个合成表那还是个问题。
不过原版也就只有两种合成容器,因此这个方法在原版MC中的意义也不是很大。
ItemStack getRecipeOutput()
读者可能会觉得这个方法和前面的getCraftingResult方法在作用上重复了,的确,可以近似认为这两者的作用是相同的,所以返回和getCraftingResult返回值相同的值就可以了。
default NonNullList<ItemStack> getRemainingItems(InventoryCrafting inv)
这个方法用于决定:合成完后,哪些物品会留在合成容器之中。很多模组中的一些有流体桶参与的合成表,合成完后会自动返还空桶,利用的就是这个方法。
这个方法的默认实现是:返还所有有容器(Container)的ItemStack的容器。比如对于岩浆桶来说,桶即是它的容器。其实这一默认实现足以适用于绝大部分情况了。
default NonNullList<Ingredient> getIngredients()
Ingredient类其实和ItemStack类很相似,甚至它的构造器就接受一个ItemStack对象,只是该类封装了一些和合成有关的东西。
该方法的默认实现为返回一个元素为Ingredient.EMPTY
的NonNullList——说是“NonNullList”,其实只是其中的值不为null,但代表的含义还是“没有物品”。
default boolean isDynamic()
默认实现为return false;
。
该方法意为“合成表是否为动态的”,那当然要返回return true;
了,如果不是动态的,又何必重新实现IRecipe接口呢?写个.json文件不就得了。
default String getGroup()
该方法用于获取配方所在的“组别”,什么叫“配方所在的组别”?以.json文件形式添加的配方中,有这么一个“group”键,值的格式为“modid:examplegroup”。该方法的返回值的作用就类似于这个键值对。如果你没什么特别的需求,这个方法就可以不Override了。
注册IRecipe
是的,IRecipe需要注册——而且走的是Forge的注册表系统。因此:
public class MyCustomRecipe implements IRecipe { private ResourceLocation regName; ... @Override public IRecipe setRegistryName(ResourceLocation name) { this.regName = name; return this; } @Override public ResourceLocation getRegistryName() { return this.regName; } ... }
@Mod.EventBusSubscriber(modid = [Your MODID]) public class RecipeLoader { @SubscribeEvent public static void register(RegistryEvent.Register<IRecipe> event) { event.getRegistry().register([Your IRecipe Instance].setRegistryName(new ResourceLocation(...))); } }