【Minecraft】 【原创】 MOD 开发教程!.
窝决定用我正在开发的MOD作为实例来讲解
首先你需要建立一个包(Package)
Package的命名很随意,Forge建议的命名规范是"作者名.mod名”,Java建议的命名规范是Package的制作公司的网站的域名的倒写,例如"com.lofter"
实际上无需在这个问题上纠结这个教程里使用了“作者名.mod名”的命名规范,毕竟不是谁都有一级域名的= =
右键项目中的minecraft,选择New->Package来创建一个Package
那么此时你的Eclipse应该是这个样子的:
然后需要在public class mod_RPG {的上面加上
@Mod(modid="rpg", name="RPG", version="0.0.1")
@NetworkMod(clientSideRequired=true, serverSideRequired=false)
(@Mod必须标注在类的上方,它的含义是告诉Forge”这是一个Mod主类”.
它有3个字符串类型参数,modid是Mod的id号,它将用于内部识别,请确保它不包含特殊字符,并且不会经常变动.name是显示给玩家看的Mod名,version是版本号,对于联机Mod来说不要乱填.
@NetworkMod必须标注在类的上方,它的含义是告诉Forge”这个Mod对客户端和服务器端来说是否是必备的”.
clientSideRequired=true, serverSideRequired=false
代表客户端必需安装而服务端不必需安装- -
Forge在这方面的做法很不强势,也就是说你不按照设定好的来配置服务端与客户端,它只会在你连接服务器时给出一个警告,然后依旧可以正常连进去,只有在...比如客户端使用mod里的物品时而服务端没有这个mod(数据不同步)时,才会强制吧你踢出游戏。)
静等两秒中,Eclipse开始报错,因为你没有导入相关的package,点击”Import …”来自动导入相关package.
然后我们要添加用于初始化Mod的方法(Method)我们使用Annotation来实现.
在你的类中添加这些代码.
@EventHandler
public void preLoad(FMLPreInitializationEvent event)
{
}
@EventHandler
public void load(FMLInitializationEvent event)
{
}
@EventHandler
public void postLoad(FMLPostInitializationEvent event)
{
}
并导入相关的Package.
这时你的Eclipse应该是这样的:
这时你的第一个Mod就已经可以运行了,虽然他没有任何东西,但他是你亲手做出来的第一个Mod不是么?
按下键盘上的F11键,并等待他编译完成,就可以在Mod页面里看到你的Mod了:
新建一个名字叫RPGConfig的类
package snowMiKu.RPG;
import net.minecraftforge.common.Configuration;
import java.io.File;
import java.io.IOException;
public class RPGConfig
{
private static RPGConfig instance;
private Configuration config;
public static void InitliazeConfig(File ConfigFile)
{
if(RPGConfig.instance != null)
{
return;
}
RPGConfig.instance = new RPGConfig(ConfigFile);
}
private RPGConfig(File configFile)
{
if(!configFile.exists())
{
try
{
configFile.createNewFile();
}
catch(IOException e)
{
System.out.println(e);
return;
}
}
config = new Configuration(configFile);
config.load();
}
//储存配置文件的函数
public static void SaveConfig()
{
RPGConfig.instance.config.save();
}
//下面三个是获取方块ID、物品ID、和其他配置信息的函数
public static String GetGeneralProperties(String PropertyName, String DefaultValue) throws Exception
{
if(RPGConfig.instance == null)
{
throw new Exception("没有初始化配置文件!");
}
return RPGConfig.instance.config.get("general", PropertyName, DefaultValue).getString();
}
public static int GetItemID(String ItemName, int DefaultValue) throws Exception
{
if(RPGConfig.instance == null)
{
throw new Exception("没有初始化配置文件!");
}
return RPGConfig.instance.config.getItem("item", "ID." + ItemName, DefaultValue).getInt();
}
public static int GetBlockID(String BlockName, int DefaultID) throws Exception
{
if(RPGConfig.instance == null)
{
throw new Exception("没有初始化配置文件!");
}
return RPGConfig.instance.config.getBlock("ID." + BlockName, DefaultID).getInt();
}
}
作为一个联机Mod
代理类是重要的
在Forge里一个Mod最完美的状态就是不区分客户端mod和服务端mod
服务端Mod可以粗略理解为没有渲染,GUI和纹理的客户端Mod
所以我们只要想办法让Mod在服务端不加载纹理什么的就好了
这时我们就要通过代理类来实现~
首先,右键snowmiku.RPG包来创建一个名为RPGCommonProxy的类
然后添加
/**
* 执行第1阶段的加载行为。
*/
public void preInit() {}
/**
* 执行第2阶段的加载行为。
*/
public void init() {}
/**
* 执行第3阶段的加载行为。
*/
public void postInit() {}
这时你的class应该是这个样子的:
并让其继承(extends)RPGCommonProxy类
<知识点:继承(extends)的用法是“在class名后加extends再加父类名”,举个栗子!上面那一步的操作应该是在“public class RPGClientProxy”后加上“extends RPGCommonProxy”;继承的类称为子类,被继承的类称为父类,子类拥有父类的所有方法(函数),也可以通过重写(Override)来重新定义方法>
然后在类中添加:
public void preInit() {
super.preInit();
}
public void init() {
super.init();
}
public void postInit() {
super.postInit();
}
这时你的RPGClientProxy类应该是这样的:
然后,我们需要回到mod_RPG类中打上注解(2014.7.3更正):
@SidedProxy(
clientSide = "snowmiku.RPG.RPGClientProxy",
serverSide = "snowmiku.RPG.RPGCommonProxy"
)
并添加静态字段:
public static RPGCommonProxy proxy;
然后你的Eclipse会报错,我们把鼠标移到波浪线处来导入相关类(以后请自行导入):
注意以后窝说的在mod主类中添加xxx都是指在上图红色方框处添加!
OK,你已经成功的为mod添加了代理类~
后记:
鼠标又坏了...你们无法想象冰音是怎么完成这篇教程的QAQ
.
一个mod总要有属于自己的分类是吧~
那么,我们开始!
首先我们要建立一个class(别问我为啥OTZ
因为是创造模式的菜单栏,所以我们就把这个类起名RPGCreativeTab好了...
然后,通过看源码我们可以得知mc里来实现这个功能的都继承自CreativeTabs类。
那我们就也继承它好了- -
别问我继承是什么,我之前一定讲过!
半自动(= =|||)导入相应的包后,静等两秒,声明类的类名处会报错
构造函数中声明了两个变量:par1 和 par2Str
par1是整数型,它只能被赋予整数值,先将它当做只能赋予自然数好了- -
par2Str是字符串,它可以被赋值任意字符,单字符两边要加上引号,举个栗子!"我就是栗子!"
以上两个变量要在声明实例时被赋值- -
通过研究源代码
我们知道(我是猜出来的)par1这个变量是用来控制标签在创造模式中显示位置的,需要注意的是0-11已经被minecraft原有的标签占用了,也就是说你只能从12开始- -,并且很蛋疼的一点就是中间不能空开(1,2,3,5,= =4去哪了?!),说白了也就是只能用12- -|||
所以我建议删掉它,让minecraft自己费脑筋好了- -
这时只剩下par2Str这个变量了,很明显- -它是用来设置你菜单栏的名称的
这时我们再重写getTabIconItemIndex()函数
@Override
public int getTabIconItemIndex() {
return Block.stone.blockID;
}
return Block.stone.blockID;这一行是用来返回方块ID的,但实际用途是设置标签图标,这里我们先用了石头的ID,以后随时能改,先付张图
这时回到你的代理类RPGCommonProxy在第二阶段加载行为处
添加
LanguageRegistry.instance().addStringLocalization("itemGroup.RPG", "RPG");//这个现在有点问题,先添加吧
报错请自行导入...
如题:
那么 每天更新这么长一章 大家慢慢吸收 虽然信息量不大. 各位耐心看 如果第一次学的嘛可以结合别的MOD开发教程一起学. 慢慢吸收吧 欢迎帖内留言.
我们终于可以讲点有实际意义的东西了- -
首先,我们需要新建一个类,当然如果方块多的话我推荐再新建一个三级包
名字叫snowmiku.RPG.block
像这样:
点击Finish
然后我们在这个包新建一个类
因为minecraft的block命名规则是BlockXXX
所以我们命名为BlockTest(不要吐槽这么有创意的名字= =)
既然是建方块- -
我们需要让他继承block类,导入相关的包
同样,添加构造函数
这时你的class应该是这个样子的:
然后让我们回到mod_RPG
在主类中添加:
public static Block BlockTest;
然后在Mod主类的preLoad方法(即一个拥有@EventHandler的方法)中添加:
BlockTest = new BlockTest(1000,Material.rock);
BlockTest.setUnlocalizedName("BlockTest");
BlockTest.setTextureName("rpg:BlockTest");
BlockTest.setHardness(1.5f); //可省略
BlockTest.setResistance(10.0f); //可省略
BlockTest.setLightValue(0.0f); //可省略
BlockTest.setStepSound(Block.soundStoneFootstep); //可省略
BlockTest.setCreativeTab(RPGCreativeTab); //可省略
GameRegistry.registerBlock(BlockTest, "BlockTest");
LanguageRegistry.addName(BlockTest, "测试方块");
new MyBlock(1000,1,Material.rock)是创建一种基于MyBlock类,编号为1000,材质类型为石材质的砖块.
setUnlocalizedName是设置砖块的名称,名称用于游戏内部处理使用,最好使用纯英文
setTextureName是用来定义纹理,下面详解...
setHardness是设置砖块的硬度,这个硬度是相对于徒手而言的,泥土0.5,石头1.5,矿石一般3.0.
setResistance是设置抗爆炸能力,石头是10.0.
setLightValue是设置发光亮度,范围是0.0~1.0,南瓜灯、萤石、岩浆1.0,传送门0.75.采集中的红石是0.625.
setStepSound是设置踩在上面的脚步声.
setCreativeTab是设置在创造模式中它在哪个菜单分类里.这里我们设置了自己新建的分类
GameRegistry.registerBlock是注册一个砖块,砖块只有被注册后才能正式使用.
LanguageRegistry.addName是为砖块添加一个显示名称,即游戏里你看到的名字.
当然,设置砖块属性的方法远不止这些,但一般只用这几个.
这时你的mod_RPG类应该是这样的:
setTextureName方法需要以”文件夹名:纹理名”的格式在纹理名前面加上文件夹名,比如上面的“rpg:BlockTest”.
然后,MCP目录下的src/minecraft目录中新建一个assets目录,在assets再新建你的文件夹目录(比如rpg),再建textures目录,在建blocks文件夹,然后把BlockTest.png丢进去= =
注意!文件夹名”rpg“必须小写!
后记:
都快写完了的时候,手一抖把工程删了(╯`□′)╯(┻━┻(恭喜你还可以看到这篇教程)
mod中的东西总不能只有创造才能获得吧- -
我们需要让生存模式也能获得- -
那么创建冶炼和合成方法吧- -
冶炼:
简单爆了= =
我们有两种方式可以实现...
minecraft的方法
minecraft提供了一个叫FurnaceRecipes的类来管理冶炼公式,它是伪静态类,我们可以跳过Forge来直接添加冶炼
FurnaceRecipes.smelting().addSmelting(要冶炼的方块.blockID, new ItemStack(生成物), 5f);
其中要冶炼的方块是要冶炼的方块= =|||
生成物是生成物(你丫讲什么废话
5f是冶炼物品获得的经验(1.3以后挖矿工都有经验了- -)
这样我们就可以冶炼东西了,并且还能获得一笔可观的经验- -(为什么可观?因为原版烧一个金矿才1f啊- -)
然后然我们来看看Forge提供的方法:
FML的GameRegistry类提供了addSmelting方法来添加冶炼公式,它的参数和上面的一样,所以说应该这么写:
GameRegistry.addSmelting(要冶炼的方块.blockID, new ItemStack(生成物), 5f);
那么这两种方法有什么区别呢?
答案是字母不一样...(它们两种方法没有任何区别,GameRegistry的addSmelting只是对FurnaceRecipes的addSmelting简单包装了一下,没有进行任何额外的操作…)
好了,然我们来创建一个冶炼= =
我们要把前几章讲的测试方块冶炼成冰矿锭(别问我冰矿锭是怎么烧出来的= =
首先打开你mod的主类
然后在load方法中添加:
GameRegistry.addSmelting(BlockTest.blockID, new ItemStack(ItemTest), 5f);
导入相关的包后就完成了冶炼的创建,简单吧~
(诶?你问我为什么用Forge的方法?因为字母数量少啊= =
合成:
于是我们来添加一个合成表来得到这把剑- -
minecraft的方法不管了,直接上Forge的方发
GameRegistry.addRecipe(new ItemStack(ItemIceSword, 1), new Object[] {" A "," A ", " B ", 'A', ItemTest, 'B', Item.stick});
new ItemStack(ItemIceSword, 1) 设置合成物与数量
" A "," A ", " B ", 一个抽象的合成表空格代表此处不能摆放东西,如果没有空格就表示可以摆在该层的任何一格(合成表三层每层三格)
'A', ItemTest, 'B', Item.stick 定义上面抽象的合成表中A代表什么B代表什么(当然你也可以用XYZ什么的),这里A代表冰矿锭,B代表木棒
好让我们进游戏看看
总算讲到这里了= =
方块总是要有自己的效果的吧,不然还不如用mod制作器呢= =
首先...练习!
任务:自己创作一个内部名字叫BlockTest2的方块,贴图自己想办法弄
.
..
...
如果你完成了任务请继续看...不然回去做任务(╯`□′)╯(┻━┻
本次教程窝们要创建一个一点就会生成一个小房子的方块
我们来试试图解试讲解:
如上~
我们的工作都是在构造函数下面进行的~
通过分析Block类的源代码我们可以发现onBlockActivated方法可以定义方块被右键单击后的行为~
让我们重写他..并添加以下代码
下面的代码涉及到世界生成~
int block = Block.ice.blockID;
int size = 5;
for (int i1 = 0; i1 < size; i1++)
{
for (int j1 = 0; j1 < size; j1++)
{
//墙
par1World.setBlock(par2+i1,par3+j1,par4,block);
par1World.setBlock(par2+i1,par3+j1,par4+(size-1),block);
par1World.setBlock(par2,par3+j1,par4+i1,block);
par1World.setBlock(par2+(size-1),par3+j1,par4+i1,block);
//屋顶
par1World.setBlock(par2+i1,par3+(size-1),par4+j1,block);
//地板
par1World.setBlock(par2+i1,par3,par4+j1,block);
}
}
//门
par1World.setBlock(par2+1,par3+2,par4,0);
par1World.setBlock(par2+1,par3+1,par4,0);
//内部设置
par1World.setBlock(par2+1,par3+1,par4+(size-2),Block.chest.blockID);
par1World.setBlock(par2+(size-2),par3+1,par4+1,Block.workbench.blockID);
par1World.setBlock(par2+(size-2),par3+1,par4+(size-2),Block.furnaceIdle.blockID);
其中setBlock是用来放置方块的~
自行导入包~
最后你的重写应该长这个样子:
par1World是所在世界
par2,par3,par4是坐标~
然后让我们运行一下~
我们也可以重叠着盖哦~
后记:
这只是个普通的棺材房~
想办法让它变得漂亮起来吧~
或许你也可以一跺脚地上长出来一座冰宫殿?!
顺便一提,某快速建造mod源代码估计和这个也差不了多少= =
也就是说这时候你已经有能力写出并发布自己的mod了~
什么?你问我怎么编译并打包?你猜~我不告诉你~~~
喵~