【Minecraft】 【原创】 MOD 开发教程!.

1L喂度娘

楼主 snowMiKu酱  发布于 2014-07-19 08:50:00 +0800 CST  


楼主 snowMiKu酱  发布于 2014-07-19 08:51:00 +0800 CST  


楼主 snowMiKu酱  发布于 2014-07-19 08:56:00 +0800 CST  


楼主 snowMiKu酱  发布于 2014-07-19 09:00:00 +0800 CST  


楼主 snowMiKu酱  发布于 2014-07-19 09:02:00 +0800 CST  





窝决定用我正在开发的MOD作为实例来讲解


首先你需要建立一个包(Package)

Package的命名很随意,Forge建议的命名规范是"作者名.mod名”,Java建议的命名规范是Package的制作公司的网站的域名的倒写,例如"com.lofter"

实际上无需在这个问题上纠结这个教程里使用了“作者名.mod名”的命名规范,毕竟不是谁都有一级域名的= =

右键项目中的minecraft,选择New->Package来创建一个Package


楼主 snowMiKu酱  发布于 2014-07-19 09:05:00 +0800 CST  

那么此时你的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了:


楼主 snowMiKu酱  发布于 2014-07-19 09:11:00 +0800 CST  



新建一个名字叫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();
}
}

楼主 snowMiKu酱  发布于 2014-07-19 09:17:00 +0800 CST  
那个我要添加一个签名档在这做下链接


楼主 snowMiKu酱  发布于 2014-07-19 16:28:00 +0800 CST  


作为一个联机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
.

楼主 snowMiKu酱  发布于 2014-07-19 20:38:00 +0800 CST  
嘛. 今晚更新一次吧. 如果大家第一次就慢慢吸收一下 以后每天早晚各一章吧.

楼主 snowMiKu酱  发布于 2014-07-19 20:43:00 +0800 CST  
嘛. 今天早起再来一章.

楼主 snowMiKu酱  发布于 2014-07-20 08:28:00 +0800 CST  


一个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");//这个现在有点问题,先添加吧

报错请自行导入...
如题:



楼主 snowMiKu酱  发布于 2014-07-20 08:39:00 +0800 CST  


楼主 snowMiKu酱  发布于 2014-07-20 08:40:00 +0800 CST  
那么 每天更新这么长一章 大家慢慢吸收 虽然信息量不大. 各位耐心看 如果第一次学的嘛可以结合别的MOD开发教程一起学. 慢慢吸收吧 欢迎帖内留言.

楼主 snowMiKu酱  发布于 2014-07-20 08:58:00 +0800 CST  


我们终于可以讲点有实际意义的东西了- -
首先,我们需要新建一个类,当然如果方块多的话我推荐再新建一个三级包
名字叫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“必须小写!


后记:
都快写完了的时候,手一抖把工程删了(╯`□′)╯(┻━┻(恭喜你还可以看到这篇教程)

楼主 snowMiKu酱  发布于 2014-07-20 21:35:00 +0800 CST  
今天更新完了. 明天早起和晚上各一篇好了.. 各位消化着..
欢迎光临我的千人小游戏服务器

楼主 snowMiKu酱  发布于 2014-07-20 21:36:00 +0800 CST  
一本正经的发帖永远没人看.. 跟风为主的吧就是这样么...

楼主 snowMiKu酱  发布于 2014-07-20 21:49:00 +0800 CST  


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代表木棒

好让我们进游戏看看

楼主 snowMiKu酱  发布于 2014-07-21 08:45:00 +0800 CST  


总算讲到这里了= =
方块总是要有自己的效果的吧,不然还不如用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了~

什么?你问我怎么编译并打包?你猜~我不告诉你~~~
喵~

楼主 snowMiKu酱  发布于 2014-07-25 11:08:00 +0800 CST  

楼主:snowMiKu酱

字数:8676

发表时间:2014-07-19 16:50:00 +0800 CST

更新时间:2016-03-15 11:32:59 +0800 CST

评论数:364条评论

帖子来源:百度贴吧  访问原帖

 

热门帖子

随机列表

大家在看